Working Salvage (ME) zones, and working probability of both medevac and sling load spawn. Need to test dropping off salvage sling loads.

This commit is contained in:
iTracerFacer 2025-11-11 02:23:43 -06:00
parent 4a351a73dd
commit bb59b12601
33 changed files with 25 additions and 6045 deletions

View File

@ -1,220 +0,0 @@
--[[
EXAMPLE: How to Add MenuManager to Your Mission
This file shows the proper load order and trigger setup
for using the Unified F10 Menu System in your DCS mission.
]]--
--[[
===============================================================================
STEP 1: MISSION EDITOR - TRIGGERS TAB
===============================================================================
Create a new trigger:
Trigger Name: "Load Mission Scripts"
Type: ONCE
Event: MISSION START
CONDITIONS:
- TIME MORE (1) [This ensures mission is fully initialized]
ACTIONS (in this exact order):
1. DO SCRIPT FILE: Moose.lua
2. DO SCRIPT FILE: Moose_MenuManager.lua
3. DO SCRIPT FILE: CTLD.lua
4. DO SCRIPT FILE: Moose_FAC2MarkRecceZone.lua
5. DO SCRIPT FILE: Moose_Intel.lua
6. DO SCRIPT FILE: Moose_CaptureZones.lua
7. DO SCRIPT FILE: Moose_NavalGroup.lua
8. DO SCRIPT FILE: Moose_TADC_Load2nd.lua
9. DO SCRIPT FILE: Moose_TADC_SquadronConfigs_Load1st.lua
10. DO SCRIPT FILE: OnBirthMessage.lua
11. ... (any other scripts)
===============================================================================
STEP 2: FILE PLACEMENT
===============================================================================
Place all .lua files in your mission folder:
C:\Users\[YourName]\Saved Games\DCS\Missions\[MissionName].miz\
Or use the mission editor:
1. Right-click mission in editor
2. "Edit Mission"
3. Click "Load/Unload lua scripts"
4. Add files in order shown above
===============================================================================
STEP 3: VERIFY LOAD ORDER
===============================================================================
After mission loads, press F10 and verify:
- F1: Mission Options (with submenus)
- F2: CTLD
- F3: AFAC Control
If order is wrong, check your trigger actions order.
===============================================================================
STEP 4: CONFIGURATION (OPTIONAL)
===============================================================================
Edit Moose_MenuManager.lua to customize:
]]--
MenuManager.Config = {
EnableMissionOptionsMenu = true, -- Set to false to disable parent menu
MissionOptionsMenuName = "Mission Options", -- Change to "Utilities" or whatever
Debug = false -- Set to true for debug logging
}
--[[
===============================================================================
STEP 5: TESTING
===============================================================================
1. Save mission
2. Load mission in DCS
3. Spawn as pilot
4. Press F10
5. Check menu structure matches expected layout
Expected result:
F10 Other Radio Items
F1: Mission Options
INTEL HQ
Zone Control
CVN Command
TADC Utilities
F2: CTLD
F3: AFAC Control
===============================================================================
TROUBLESHOOTING
===============================================================================
Problem: Menus in wrong order
Solution: Check trigger actions are in correct order
Problem: "Mission Options" missing
Solution: Verify Moose_MenuManager.lua loaded before other scripts
Problem: CTLD not at F2
Solution: Ensure CTLD.lua loads right after Moose_MenuManager.lua
Problem: Script errors
Solution: Check dcs.log file at:
C:\Users\[YourName]\Saved Games\DCS\Logs\dcs.log
Enable debug mode in MenuManager for detailed logging:
MenuManager.Config.Debug = true
===============================================================================
ADVANCED: MULTIPLE COALITION MENUS
===============================================================================
If your mission has both RED and BLUE players:
The MenuManager automatically creates "Mission Options" for both:
- BLUE players see: F10 F1: Mission Options (BLUE)
- RED players see: F10 F1: Mission Options (RED)
Each coalition's scripts only appear in their respective menu.
===============================================================================
ADVANCED: DISABLING INDIVIDUAL MENUS
===============================================================================
To hide a specific script's F10 menu without removing the script:
In Moose_Intel.lua:
local EnableF10Menu = false -- Disables Intel menu
In Moose_CaptureZones.lua:
-- Comment out the SetupZoneStatusCommands() call
This is useful for:
- Training missions (hide complexity)
- Specific mission types (no CVN = no CVN menu)
- Server performance (reduce menu overhead)
===============================================================================
EXAMPLE: ADDING YOUR OWN SCRIPT
===============================================================================
If you create a new script "MyCustomScript.lua":
1. Add to trigger after Moose_MenuManager.lua
2. In your script, use this pattern:
]]--
-- MyCustomScript.lua
local MyMenu
if MenuManager then
-- Will be under "Mission Options"
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Custom Feature")
else
-- Fallback if MenuManager not loaded
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Custom Feature")
end
-- Add your commands
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Something Cool", MyMenu, function()
MESSAGE:New("Cool thing happened!", 10):ToBlue()
end)
--[[
===============================================================================
EXAMPLE: MISSION-WIDE MENU (Both Coalitions)
===============================================================================
For features available to all players:
]]--
local UtilityMenu
if MenuManager then
UtilityMenu = MenuManager.CreateMissionMenu("Server Utilities")
else
UtilityMenu = MENU_MISSION:New("Server Utilities")
end
MENU_MISSION_COMMAND:New("Show Server Time", UtilityMenu, function()
local time = timer.getAbsTime()
local hours = math.floor(time / 3600)
local minutes = math.floor((time % 3600) / 60)
MESSAGE:New(string.format("Server Time: %02d:%02d", hours, minutes), 5):ToAll()
end)
--[[
===============================================================================
COMPLETE TRIGGER EXAMPLE (COPY/PASTE)
===============================================================================
Trigger Name: Load Mission Scripts
Type: ONCE
Event: MISSION START
Condition: TIME MORE (1)
Actions:
DO SCRIPT FILE: Moose.lua
DO SCRIPT FILE: Moose_MenuManager.lua
DO SCRIPT FILE: CTLD.lua
DO SCRIPT FILE: Moose_FAC2MarkRecceZone.lua
DO SCRIPT FILE: Moose_Intel.lua
DO SCRIPT FILE: Moose_CaptureZones.lua
DO SCRIPT FILE: Moose_NavalGroup.lua
DO SCRIPT FILE: Moose_TADC_Load2nd.lua
===============================================================================
NOTES
===============================================================================
- MenuManager is backward compatible: scripts work with or without it
- CTLD and FAC use group menus (not coalition), so they stay at root level
- Load order determines F-key positions
- Mission Options will be F1 because it loads after CTLD (F2) and FAC (F3)
- All other scripts nest under Mission Options automatically
===============================================================================
]]--

View File

@ -1,91 +0,0 @@
# F10 Menu System - Quick Reference
## Menu Structure
```
F10 → Mission Options (Blue players)
├─ INTEL HQ
├─ Zone Control
└─ CVN Command
F10 → Mission Options (Red players)
├─ INTEL HQ
└─ (Red items)
F10 → TADC Utilities (all players)
F10 → CTLD (per-player, like CTLD always was)
F10 → AFAC Control (per-player, like FAC always was)
F10 → Welcome Messages (per-player)
```
**Note**: Group menus (CTLD, FAC, Welcome) cannot be nested under coalition menus due to DCS limitations.
## Load Order (CRITICAL!)
```
1. Moose.lua
2. Moose_MenuManager.lua ← Must be FIRST
3. CTLD.lua ← Group menu (any order)
4. Moose_FAC2MarkRecceZone.lua ← Group menu (any order)
5. OnBirthMessage.lua ← Group menu (any order)
6. Moose_Intel.lua ← Under Mission Options
7. Moose_CaptureZones.lua ← Under Mission Options
8. Moose_NavalGroup.lua ← Under Mission Options
9. Moose_TADC_Load2nd.lua ← Mission menu (root level)
```
## Script Integration Pattern
### For Coalition Menus:
```lua
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Menu")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Menu")
end
```
### For Mission Menus:
```lua
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateMissionMenu("My Menu")
else
MyMenu = MENU_MISSION:New("My Menu")
end
```
## Configuration (in Moose_MenuManager.lua)
```lua
EnableMissionOptionsMenu = true -- false to disable
MissionOptionsMenuName = "Mission Options" -- change name
Debug = false -- true for logging
```
## Disable Individual Script Menus
```lua
-- In each script (e.g., Moose_Intel.lua)
local EnableF10Menu = false
```
## Common Issues
| Problem | Solution |
|---------|----------|
| Duplicate "Mission Options" | Fixed in v1.1 - only coalition menus now |
| Empty menu | Check that scripts are loaded |
| Group menus not under Mission Options | That's correct - DCS limitation |
| TADC at root level | Correct - it's a mission menu (all players) |
## Files Modified
- ✅ Moose_Intel.lua
- ✅ Moose_CaptureZones.lua
- ✅ Moose_NavalGroup.lua
- ✅ Moose_TADC_Load2nd.lua
- ✅ OnBirthMessage.lua (v1.1)
## New Files
- ✅ Moose_MenuManager.lua (Core system v1.1)
- ✅ F10_MENU_SYSTEM_GUIDE.md (Full documentation)
- ✅ MENUMANAGER_UPDATE_NOTES.md (v1.1 changes)

View File

@ -1,262 +0,0 @@
# Unified F10 Menu System Guide
## Overview
The Unified F10 Menu Manager provides a consistent and organized F10 radio menu structure across all mission scripts. It ensures that the most frequently used menus (CTLD and FAC) maintain consistent positions while organizing all other mission options under a single parent menu.
## Menu Structure
```
F10 - Other Radio Items
├─ F1 - Mission Options <-- All other scripts go here
│ ├─ INTEL HQ
│ ├─ Zone Control
│ ├─ CVN Command
│ ├─ TADC Utilities
│ └─ (any other scripts)
├─ F2 - CTLD <-- Reserved, always in position 2
│ ├─ Check Cargo
│ ├─ Troop Transport
│ ├─ Vehicle / FOB Transport
│ └─ ...
└─ F3 - AFAC Control <-- Reserved, always in position 3
├─ Targeting Mode
├─ Laser Codes
├─ Marker Settings
└─ ...
```
## Benefits
1. **Consistent Positioning**: CTLD and FAC are always F10-F2 and F10-F3
2. **Reduced Clutter**: All other menus are grouped under "Mission Options"
3. **Easy Navigation**: Players know where to find commonly used functions
4. **Scalable**: Easy to add new scripts without menu reorganization
## Installation
### 1. Load Order in Mission Editor
The scripts must be loaded in this specific order in your DCS mission:
```
1. Moose.lua (MOOSE Framework)
2. Moose_MenuManager.lua (Menu Manager - LOAD FIRST!)
3. CTLD.lua (Will be F10-F2)
4. Moose_FAC2MarkRecceZone.lua (Will be F10-F3)
5. Moose_Intel.lua (Will be under Mission Options)
6. Moose_CaptureZones.lua (Will be under Mission Options)
7. Moose_NavalGroup.lua (Will be under Mission Options)
8. Moose_TADC_Load2nd.lua (Will be under Mission Options)
9. ... any other scripts ...
```
**CRITICAL**: `Moose_MenuManager.lua` must be loaded BEFORE any script that creates F10 menus (except CTLD and FAC which use their own system).
### 2. Script Triggers in DCS
In the DCS Mission Editor, create triggers for "MISSION START":
```
MISSION START
└─ DO SCRIPT FILE: Moose.lua
└─ DO SCRIPT FILE: Moose_MenuManager.lua
└─ DO SCRIPT FILE: CTLD.lua
└─ DO SCRIPT FILE: Moose_FAC2MarkRecceZone.lua
└─ DO SCRIPT FILE: Moose_Intel.lua
└─ DO SCRIPT FILE: Moose_CaptureZones.lua
└─ DO SCRIPT FILE: Moose_NavalGroup.lua
└─ DO SCRIPT FILE: Moose_TADC_Load2nd.lua
```
## Configuration
### MenuManager Configuration
Edit `Moose_MenuManager.lua` to customize behavior:
```lua
MenuManager.Config = {
EnableMissionOptionsMenu = true, -- Set to false to disable parent menu
MissionOptionsMenuName = "Mission Options", -- Change parent menu name
Debug = false -- Enable debug logging
}
```
### Individual Script Configuration
Each script has been updated to support the MenuManager. If you want to disable a specific script's F10 menu, edit that script:
**Example - Disable Intel Menu:**
```lua
-- In Moose_Intel.lua, line 10
local EnableF10Menu = false -- Changed from true to false
```
## For Script Developers
### Adding New Scripts to the System
If you're creating a new script that needs an F10 menu, use the MenuManager:
#### Coalition Menu Example
```lua
-- Old way (creates root menu)
local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
-- New way (creates under Mission Options)
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
-- Fallback if MenuManager not loaded
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
end
-- Add commands to your menu
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Something", MyMenu, MyFunction)
```
#### Mission Menu Example
```lua
-- Old way
local MyMenu = MENU_MISSION:New("My Script")
-- New way
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateMissionMenu("My Script")
else
MyMenu = MENU_MISSION:New("My Script")
end
```
#### Creating Submenus
```lua
-- Create a parent menu under Mission Options
local ParentMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Parent")
-- Create a submenu under your parent
local SubMenu = MENU_COALITION:New(coalition.side.BLUE, "Submenu", ParentMenu)
```
## CTLD and FAC Positioning
### Why CTLD and FAC Don't Use MenuManager
CTLD and FAC create **per-group menus** using `missionCommands.addSubMenuForGroup()`, which means each player/group gets their own instance. These are fundamentally different from coalition/mission menus.
The key is **load order**:
- By loading CTLD first (after MenuManager), it becomes F10-F2
- By loading FAC second, it becomes F10-F3
- Mission Options loads third, becoming F10-F1
This ensures consistent positioning without code modifications to CTLD/FAC.
### If You Need to Modify CTLD or FAC
If you control the CTLD or FAC script source and want to move them under Mission Options, you would need to:
1. Keep them as group menus (they need to be)
2. Accept that group menus can't be nested under coalition menus in DCS
3. Load them in the desired order for consistent F-key positioning
**Recommendation**: Keep CTLD and FAC as-is (F2 and F3) since they're used most frequently.
## Troubleshooting
### Menus Appear in Wrong Order
- **Cause**: Scripts loaded in wrong order
- **Fix**: Check your mission triggers and ensure MenuManager loads first
### "Mission Options" Not Appearing
- **Cause**: `EnableMissionOptionsMenu = false` in config
- **Fix**: Edit `Moose_MenuManager.lua` and set to `true`
### Script Menu Appears at Root Instead of Under Mission Options
- **Cause**: Script doesn't use MenuManager, or MenuManager not loaded
- **Fix**: Update the script to use MenuManager API
### CTLD or FAC Position Changes
- **Cause**: Another script is loading before them
- **Fix**: Adjust load order so CTLD and FAC load immediately after MenuManager
### Debug Mode
Enable debug logging to troubleshoot menu creation:
```lua
-- In Moose_MenuManager.lua
MenuManager.Config = {
Debug = true -- Changed from false
}
```
Check `dcs.log` for messages like:
```
MenuManager: Initialized parent menus
MenuManager: Created coalition menu 'INTEL HQ' for BLUE
```
## Advanced Usage
### Disabling the System at Runtime
You can disable/enable the parent menu system during mission execution:
```lua
-- Disable (all new menus will be created at root)
MenuManager.DisableParentMenus()
-- Re-enable
MenuManager.EnableParentMenus()
```
### Creating Direct Root Menus
If you want a specific menu at the root level instead of under Mission Options:
```lua
-- Pass 'nil' as parent to force root creation
local RootMenu = MENU_COALITION:New(coalition.side.BLUE, "Special Root Menu", nil)
```
### Custom Parent Menus
Create your own parent menu and pass it to MenuManager:
```lua
local MyParent = MENU_COALITION:New(coalition.side.BLUE, "Advanced Options")
local SubMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Sub Option", MyParent)
```
## Updates and Maintenance
### Version History
- **v1.0** - Initial release with support for Intel, Zones, CVN, and TADC scripts
### Modified Scripts
The following scripts have been updated to use MenuManager:
- `Moose_Intel.lua` - INTEL HQ menu
- `Moose_CaptureZones.lua` - Zone Control menu
- `Moose_NavalGroup.lua` - CVN Command menu
- `Moose_TADC_Load2nd.lua` - TADC Utilities menu
### Backward Compatibility
All scripts retain backward compatibility. If `MenuManager` is not loaded, they will create root-level menus as before.
## Questions and Support
For issues or questions about the Unified F10 Menu System, check:
1. Load order in mission editor
2. Debug logs in `dcs.log`
3. Configuration settings in each script
4. This guide's troubleshooting section
---
**Author**: Created for Operation Polar Shield mission series
**Last Updated**: November 9, 2025
**Version**: 1.0

View File

@ -1,194 +0,0 @@
# FIXES APPLIED - November 9, 2025
## Issues Fixed
### 1. ✅ Duplicate "Mission Options" Menu (One Empty)
**Root Cause**: MenuManager was creating both MENU_COALITION and MENU_MISSION parent menus with the same name.
**Fix**: Removed MENU_MISSION parent menu creation. Only coalition-specific parent menus are created now.
**Files Changed**: `Moose_MenuManager.lua`
---
### 2. ✅ OnBirthMessage Menu Not Integrated
**Root Cause**: OnBirthMessage uses group menus (like CTLD/FAC) which cannot be nested under coalition menus due to DCS API limitations.
**Fix**:
- Added configuration option to enable/disable menu
- Added documentation explaining group menu behavior
- Updated load order guidance
**Files Changed**: `OnBirthMessage.lua`
---
## What You'll See Now
### Before Fix
```
F10
├─ Mission Options (Blue) ← Has content
├─ Mission Options (???) ← EMPTY - The bug!
├─ INTEL HQ (separate)
├─ Zone Control (separate)
├─ TADC Utilities
├─ CTLD
├─ AFAC Control
└─ Welcome Messages (not integrated)
```
### After Fix
```
F10
├─ Mission Options (Blue) ← Clean, organized
│ ├─ INTEL HQ
│ ├─ Zone Control
│ └─ CVN Command
├─ TADC Utilities ← Mission menu (all players see it)
├─ CTLD ← Group menu (can't nest)
├─ AFAC Control ← Group menu (can't nest)
└─ Welcome Messages ← Group menu (can't nest)
```
**Key**: Only ONE "Mission Options" per coalition, and it has content!
---
## Understanding DCS Menu Types
### Why Some Menus Can't Be Nested
DCS has three different menu systems:
1. **MENU_COALITION** - Team menus (Blue or Red)
- Can be nested under other coalition menus ✅
- Example: Intel, Zone Control, CVN
2. **MENU_MISSION** - Global menus (everyone sees same)
- Cannot be nested under coalition menus ❌
- Example: TADC Utilities
3. **Group Menus** - Per-player menus (each player has own)
- Cannot be nested under ANY parent menu ❌
- Example: CTLD, FAC, Welcome Messages
**Why?** These are different DCS API systems that don't interoperate. It's a DCS limitation, not a bug in our code.
---
## Configuration Options
### Disable OnBirthMessage F10 Menu
In `OnBirthMessage.lua` (line 5):
```lua
local EnableF10Menu = false -- Set to false to hide menu
```
### Disable Entire MenuManager System
In `Moose_MenuManager.lua`:
```lua
MenuManager.Config.EnableMissionOptionsMenu = false
```
---
## Testing Checklist
- [x] Only ONE "Mission Options" appears (per coalition)
- [x] "Mission Options" contains items (not empty)
- [x] TADC at root level (correct - mission menu)
- [x] CTLD at root level (correct - group menu)
- [x] FAC at root level (correct - group menu)
- [x] Welcome Messages at root level (correct - group menu)
- [x] OnBirthMessage has config option
- [x] Documentation updated
---
## Files Modified
### Core System
- `Moose_MenuManager.lua` - v1.1
- Removed duplicate menu creation
- Updated CreateMissionMenu function
- Added better documentation
### Scripts
- `OnBirthMessage.lua`
- Added EnableF10Menu config
- Added documentation about group menus
- Improved error handling
### Documentation
- `F10_MENU_QUICK_REF.md` - Updated menu structure
- `MENUMANAGER_UPDATE_NOTES.md` - NEW - Detailed explanation
- `FIXES_APPLIED.md` - NEW - This file
---
## Load Order (Updated)
```
1. Moose.lua
2. Moose_MenuManager.lua ← Creates coalition parent menus
3. CTLD.lua ← Group menu
4. Moose_FAC2MarkRecceZone.lua ← Group menu
5. OnBirthMessage.lua ← Group menu
6. Moose_Intel.lua ← Under Mission Options
7. Moose_CaptureZones.lua ← Under Mission Options
8. Moose_NavalGroup.lua ← Under Mission Options
9. Moose_TADC_Load2nd.lua ← Mission menu (root)
```
**Note**: Order of group menus (3-5) doesn't matter. They'll all appear at root regardless.
---
## What Changed vs v1.0
### v1.1 Changes
1. Removed MENU_MISSION parent menu (was creating duplicate)
2. Updated CreateMissionMenu to create root-level menus
3. Added OnBirthMessage integration
4. Clarified documentation about menu types
5. Updated all guides with correct information
### Backward Compatibility
✅ All v1.0 scripts still work
✅ No breaking changes
✅ Optional configurations added
---
## Summary
**Problem**: Two "Mission Options" menus (one empty) + OnBirthMessage not integrated
**Solution**:
1. Fixed MenuManager to only create coalition parent menus
2. Updated OnBirthMessage with config option
3. Clarified documentation about DCS menu type limitations
**Result**: Clean, organized F10 menu structure with proper understanding of what can and cannot be nested.
---
## Questions?
**Q: Why can't CTLD be under Mission Options?**
A: CTLD uses group menus (per-player). DCS doesn't allow nesting group menus under coalition menus.
**Q: Why is TADC at root level?**
A: TADC is a mission menu (visible to all players). Can't nest mission menus under coalition menus.
**Q: Can I hide the Welcome Messages menu?**
A: Yes! Set `EnableF10Menu = false` in OnBirthMessage.lua
**Q: Will this break my existing missions?**
A: No! All changes are backward compatible.
---
*Applied: November 9, 2025*
*Version: MenuManager v1.1*

View File

@ -1,398 +0,0 @@
# Unified F10 Menu System
**Version 1.0** | **Created**: November 9, 2025 | **For**: DCS Mission Development
---
## 🎯 What This Does
Creates a unified F10 menu system that ensures **CTLD** and **FAC** are always in the same position (F2 and F3), while organizing all other mission scripts under a clean "Mission Options" parent menu.
**Result**: F10 → F2 for CTLD, F10 → F3 for FAC. Every mission. Every time.
---
## 📋 Quick Start
### 1. Copy Files to Mission
Copy these files to your mission folder:
- `Moose_MenuManager.lua` (required)
- `Moose_Intel.lua` (updated)
- `Moose_CaptureZones.lua` (updated)
- `Moose_NavalGroup.lua` (updated)
- `Moose_TADC_Load2nd.lua` (updated)
### 2. Set Load Order in Mission Editor
In DCS Mission Editor, Triggers tab, create "MISSION START" trigger:
```
1. Moose.lua
2. Moose_MenuManager.lua ← MUST BE FIRST!
3. CTLD.lua ← Will be F2
4. Moose_FAC2MarkRecceZone.lua ← Will be F3
5. Moose_Intel.lua ← Under Mission Options
6. Moose_CaptureZones.lua ← Under Mission Options
7. Moose_NavalGroup.lua ← Under Mission Options
8. Moose_TADC_Load2nd.lua ← Under Mission Options
```
### 3. Test
- Start mission
- Spawn as pilot
- Press F10
- Verify: F1 = Mission Options, F2 = CTLD, F3 = FAC
---
## 📚 Documentation Files
| File | Purpose | When to Use |
|------|---------|-------------|
| **F10_MENU_SYSTEM_GUIDE.md** | Complete documentation | Full understanding |
| **F10_MENU_QUICK_REF.md** | Quick reference card | Fast lookup |
| **MENUMANAGER_SUMMARY.md** | System overview | Understanding concept |
| **MENUMANAGER_VISUAL_GUIDE.md** | Visual diagrams | Visual learners |
| **MENUMANAGER_TEMPLATE.lua** | Code templates | Integrating scripts |
| **EXAMPLE_MISSION_SETUP.lua** | Mission setup example | Setting up missions |
| **README.md** | This file | Getting started |
### Reading Order
1. Start here (README.md) ← You are here
2. Read **MENUMANAGER_SUMMARY.md** (understand the concept)
3. Read **MENUMANAGER_VISUAL_GUIDE.md** (see diagrams)
4. Read **F10_MENU_SYSTEM_GUIDE.md** (complete details)
5. Use **F10_MENU_QUICK_REF.md** (ongoing reference)
---
## 🎮 What Players See
### Before
```
F10 → F1: Zone Control
→ F2: TADC Utilities
→ F3: INTEL HQ
→ F4: CVN Command
→ F5: CTLD ← Where is it this time?
→ F6: AFAC Control ← Always different
```
### After
```
F10 → F1: Mission Options ← Clean organization
├─ INTEL HQ
├─ Zone Control
├─ CVN Command
└─ TADC Utilities
→ F2: CTLD ← ALWAYS HERE!
→ F3: AFAC Control ← ALWAYS HERE!
```
**Player Experience**: Press F10 → F2 for CTLD. Every time. Muscle memory works!
---
## 🔧 Configuration
### Disable Entire System
In `Moose_MenuManager.lua`:
```lua
MenuManager.Config.EnableMissionOptionsMenu = false
```
### Disable Individual Script Menu
In any script (e.g., `Moose_Intel.lua`):
```lua
local EnableF10Menu = false
```
### Change Parent Menu Name
In `Moose_MenuManager.lua`:
```lua
MenuManager.Config.MissionOptionsMenuName = "Utilities"
```
### Enable Debug Logging
In `Moose_MenuManager.lua`:
```lua
MenuManager.Config.Debug = true
```
---
## 🛠️ For Script Developers
### Integrating New Scripts
**Old way** (creates root menu):
```lua
local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
```
**New way** (uses MenuManager):
```lua
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
end
```
**That's it!** Your menu now appears under "Mission Options" and falls back gracefully if MenuManager isn't loaded.
See **MENUMANAGER_TEMPLATE.lua** for more examples.
---
## ✅ What's Included
### Core Files
- [x] `Moose_MenuManager.lua` - Menu management system
### Updated Scripts
- [x] `Moose_Intel.lua` - INTEL HQ menu
- [x] `Moose_CaptureZones.lua` - Zone Control menu
- [x] `Moose_NavalGroup.lua` - CVN Command menu
- [x] `Moose_TADC_Load2nd.lua` - TADC Utilities menu
### Documentation
- [x] Complete user guide
- [x] Quick reference card
- [x] System summary
- [x] Visual diagrams
- [x] Code templates
- [x] Setup examples
---
## 🎓 Key Concepts
### Why Load Order Matters
DCS creates F10 menus in the order scripts are loaded:
- Script loaded 1st → F10-F1
- Script loaded 2nd → F10-F2
- Script loaded 3rd → F10-F3
By loading CTLD and FAC in specific positions, we ensure they're always F2 and F3.
### Why CTLD/FAC Don't Use MenuManager
CTLD and FAC create **per-group menus** (each player/group gets their own). These can't be nested under coalition menus. Solution: Use load order to position them at F2 and F3.
### Backward Compatibility
All scripts work with or without MenuManager:
- **With MenuManager**: Organized under "Mission Options"
- **Without MenuManager**: Creates root menu (old behavior)
No errors, no breaking changes.
---
## 🐛 Troubleshooting
| Problem | Solution |
|---------|----------|
| Menus in wrong order | Check script load order in mission triggers |
| No "Mission Options" | Ensure `Moose_MenuManager.lua` loads first |
| CTLD not at F2 | Load CTLD right after MenuManager |
| FAC not at F3 | Load FAC right after CTLD |
| Script errors | Enable debug mode, check dcs.log |
**Debug logs location**: `C:\Users\[You]\Saved Games\DCS\Logs\dcs.log`
---
## 📊 Benefits
### For Players
- ✅ CTLD always F2 (muscle memory)
- ✅ FAC always F3 (consistent)
- ✅ Clean, organized menus
- ✅ Faster navigation (1 second vs 15 seconds)
### For Mission Makers
- ✅ Professional appearance
- ✅ Easy to add/remove scripts
- ✅ Configurable per mission
- ✅ Better player feedback
### For Developers
- ✅ 3-line integration
- ✅ Backward compatible
- ✅ Well documented
- ✅ Flexible configuration
---
## 🚀 Features
- **Consistent Positioning**: CTLD and FAC always in same position
- **Clean Organization**: One parent menu instead of many root menus
- **Easy Integration**: Minimal code changes to existing scripts
- **Backward Compatible**: Works with or without MenuManager
- **Configurable**: Enable/disable globally or per-script
- **Scalable**: Add unlimited scripts without reorganization
- **Well Documented**: Complete guides and examples
---
## 📖 Examples
### Example 1: Training Mission (Hide Everything Except CTLD)
```lua
// In Moose_MenuManager.lua
MenuManager.Config.EnableMissionOptionsMenu = false
// In each other script
local EnableF10Menu = false
Result: Only CTLD appears (F10 → F2)
```
### Example 2: Custom Parent Menu Name
```lua
// In Moose_MenuManager.lua
MenuManager.Config.MissionOptionsMenuName = "Squadron Utilities"
Result: F10 → F1 → Squadron Utilities
```
### Example 3: Add Your Own Script
```lua
// In MyScript.lua
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Feature")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Feature")
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Thing", MyMenu, DoThing)
Result: F10 → F1 → Mission Options → My Feature
```
---
## 🔍 Technical Details
### Menu Types
- **MENU_COALITION**: Visible to one coalition (Blue or Red)
- **MENU_MISSION**: Visible to all players
- **missionCommands (Group)**: Per-group menus (CTLD/FAC use this)
MenuManager handles the first two. Group menus remain at root level.
### Architecture
```
MenuManager (loads first)
Creates "Mission Options" parent menu
Other scripts register under it
Result: F1 = Mission Options, F2 = CTLD, F3 = FAC
```
### Files Modified
Only menu creation code changed. All other functionality unchanged.
---
## 🎯 Success Criteria
- [x] CTLD always at F2
- [x] FAC always at F3
- [x] Other menus under F1
- [x] Backward compatible
- [x] Easy to integrate
- [x] Well documented
- [x] Tested and working
**Status**: All criteria met! ✅
---
## 📝 Version History
**v1.0** (November 9, 2025)
- Initial release
- Support for coalition and mission menus
- Integration with Intel, Zones, CVN, TADC scripts
- Complete documentation suite
---
## 💡 Tips
1. **Always load MenuManager first** (after Moose.lua)
2. **Load CTLD second** to ensure F2 position
3. **Load FAC third** to ensure F3 position
4. **Test load order** before deploying mission
5. **Use debug mode** when troubleshooting
6. **Check dcs.log** for error messages
---
## 🙋 Support
**Need Help?**
1. Read **F10_MENU_SYSTEM_GUIDE.md** (comprehensive guide)
2. Check **F10_MENU_QUICK_REF.md** (quick answers)
3. Review **MENUMANAGER_VISUAL_GUIDE.md** (visual explanations)
4. Use **MENUMANAGER_TEMPLATE.lua** (code examples)
5. Enable debug mode and check logs
**Common Issues?**
- See Troubleshooting section above
- Check load order in mission editor
- Verify all files are in mission folder
- Enable debug logging for details
---
## 🎖️ Credits
**Created for**: F-99th Fighter Squadron
**Mission**: Operation Polar Shield
**Date**: November 9, 2025
**Design Philosophy**:
- Keep it simple
- Make it consistent
- Document everything
- Maintain compatibility
---
## 📄 License
Free to use, modify, and distribute for DCS missions.
Credit appreciated but not required.
---
## 🚦 Getting Started Checklist
- [ ] Read this README
- [ ] Review MENUMANAGER_SUMMARY.md
- [ ] Copy Moose_MenuManager.lua to mission
- [ ] Set up load order in mission editor
- [ ] Test in DCS
- [ ] Configure as needed
- [ ] Deploy to server
**Estimated setup time**: 10-15 minutes
**Result**: Professional, organized F10 menus!
---
**Questions?** Start with the documentation files listed above.
**Want to integrate a new script?** See MENUMANAGER_TEMPLATE.lua.
**Need visual examples?** Check MENUMANAGER_VISUAL_GUIDE.md.
**Ready to get started?** Follow the Quick Start section at the top!
---
*Making DCS missions more organized, one F10 menu at a time.* 🎯✈️

View File

@ -1,270 +0,0 @@
# Unified F10 Menu System - Summary
## What Was Created
A complete F10 menu organization system that ensures CTLD and FAC remain in consistent positions (F2 and F3) while grouping all other mission scripts under a "Mission Options" parent menu at F1.
## Files Created
1. **Moose_MenuManager.lua** - Core menu management system
2. **F10_MENU_SYSTEM_GUIDE.md** - Complete documentation
3. **F10_MENU_QUICK_REF.md** - Quick reference card
4. **EXAMPLE_MISSION_SETUP.lua** - Mission integration example
5. **MENUMANAGER_TEMPLATE.lua** - Script integration templates
## Files Modified
1. **Moose_Intel.lua** - Updated to use MenuManager
2. **Moose_CaptureZones.lua** - Updated to use MenuManager
3. **Moose_NavalGroup.lua** - Updated to use MenuManager
4. **Moose_TADC_Load2nd.lua** - Updated to use MenuManager
## How It Works
### The Problem
- F10 menu items appear in load order
- CTLD and FAC could be at any position
- Menu becomes cluttered with many scripts
- Players waste time navigating to frequently-used items
### The Solution
1. Load MenuManager first to create "Mission Options" parent menu
2. Load CTLD second → becomes F10-F2 (consistent)
3. Load FAC third → becomes F10-F3 (consistent)
4. All other scripts register under "Mission Options" → F10-F1
### Result
```
F10 - Other Radio Items
├─ F1 - Mission Options ← All other menus here
│ ├─ INTEL HQ
│ ├─ Zone Control
│ ├─ CVN Command
│ └─ TADC Utilities
├─ F2 - CTLD ← Always here (muscle memory!)
└─ F3 - AFAC Control ← Always here
```
## Key Features
**Consistent Positioning**: CTLD and FAC always F2 and F3
**Reduced Clutter**: One parent menu instead of many root menus
**Backward Compatible**: Scripts work with or without MenuManager
**Easy Integration**: 3-line change to existing scripts
**Configurable**: Can disable entire system or individual menus
**Scalable**: Add unlimited scripts without reorganization
## Quick Start
### 1. Mission Editor Setup
Load scripts in this order:
```
1. Moose.lua
2. Moose_MenuManager.lua ← NEW - Must be first!
3. CTLD.lua
4. Moose_FAC2MarkRecceZone.lua
5. Moose_Intel.lua
6. Moose_CaptureZones.lua
7. Moose_NavalGroup.lua
8. Moose_TADC_Load2nd.lua
9. ... other scripts ...
```
### 2. Test in Mission
1. Start mission
2. Spawn as pilot
3. Press F10
4. Verify menu structure
### 3. Configuration (Optional)
Edit `Moose_MenuManager.lua`:
```lua
MenuManager.Config = {
EnableMissionOptionsMenu = true, -- false to disable
MissionOptionsMenuName = "Mission Options",
Debug = false -- true for logging
}
```
## Integration Pattern
To integrate any script with MenuManager:
**Before:**
```lua
local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
```
**After:**
```lua
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
end
```
That's it! The script now works with MenuManager but falls back gracefully if it's not loaded.
## Why CTLD/FAC Don't Use MenuManager
CTLD and FAC create **per-group menus** using DCS's native `missionCommands.addSubMenuForGroup()`. These are fundamentally different from MOOSE's coalition/mission menus and can't be nested under a parent menu.
**Solution**: Load order ensures consistent positioning:
- MenuManager loads first → creates "Mission Options"
- CTLD loads second → creates group menus (become F2)
- FAC loads third → creates group menus (become F3)
- "Mission Options" appears at F1 because coalition menus load before group menus
This gives us the consistent F1/F2/F3 structure we want.
## Benefits for Mission Makers
1. **Less Menu Navigation**: Players go straight to F2 for CTLD, F3 for FAC
2. **Cleaner Interface**: One parent menu instead of 5+ root menus
3. **Easier Maintenance**: Add/remove scripts without menu reorganization
4. **Professional Look**: Organized, predictable menu structure
5. **Player Feedback**: "Finally, I can find CTLD quickly!"
## Benefits for Script Developers
1. **3-Line Integration**: Minimal code changes
2. **Backward Compatible**: Works with or without MenuManager
3. **No Breaking Changes**: Existing scripts continue to work
4. **Flexible**: Can opt-in or opt-out per script
5. **Well Documented**: Templates and examples provided
## Technical Details
### Menu Types in MOOSE/DCS
1. **MENU_MISSION**: Visible to all players
2. **MENU_COALITION**: Visible to one coalition
3. **MENU_GROUP**: Visible to specific group (CTLD/FAC use this)
MenuManager handles MENU_MISSION and MENU_COALITION. Group menus remain independent.
### Load Order Matters
DCS creates menus in load order:
1. First loaded script → F1
2. Second loaded script → F2
3. Third loaded script → F3
etc.
By controlling load order, we control F-key positions.
### Fallback Behavior
If MenuManager is not loaded or disabled:
- Scripts create root-level menus (old behavior)
- Everything still works, just not organized
- No errors or warnings
## Configuration Options
### Global Configuration (in Moose_MenuManager.lua)
```lua
EnableMissionOptionsMenu = true -- Disable entire system
MissionOptionsMenuName = "..." -- Change parent menu name
Debug = false -- Enable logging
```
### Per-Script Configuration (in each script)
```lua
local EnableF10Menu = false -- Disable this script's menu
```
### Runtime Configuration
```lua
MenuManager.DisableParentMenus() -- Turn off at runtime
MenuManager.EnableParentMenus() -- Turn on at runtime
```
## Troubleshooting
| Issue | Cause | Fix |
|-------|-------|-----|
| Menus wrong order | Load order incorrect | Check mission triggers |
| No "Mission Options" | MenuManager not loaded | Add to triggers first |
| CTLD not F2 | CTLD loaded too late | Load right after MenuManager |
| Script errors | MenuManager syntax | Check debug logs |
Enable debug mode to see what's happening:
```lua
MenuManager.Config.Debug = true
```
Check logs at: `C:\Users\[You]\Saved Games\DCS\Logs\dcs.log`
## Future Enhancements
Possible additions for future versions:
- Menu hiding/showing based on game state
- Menu reorganization at runtime
- Per-player menu customization
- Menu usage analytics
- Internationalization support
## Version History
**v1.0** (November 9, 2025)
- Initial release
- Support for MENU_COALITION and MENU_MISSION
- Integration with Intel, Zones, CVN, TADC scripts
- Complete documentation and templates
## Support
For questions or issues:
1. Read **F10_MENU_SYSTEM_GUIDE.md** (comprehensive)
2. Read **F10_MENU_QUICK_REF.md** (quick answers)
3. Check **EXAMPLE_MISSION_SETUP.lua** (mission setup)
4. Use **MENUMANAGER_TEMPLATE.lua** (script integration)
5. Enable debug mode and check dcs.log
## Credits
Created for the F-99th Fighter Squadron's Operation Polar Shield mission series.
**Design Goals**:
- Keep CTLD and FAC in consistent positions
- Reduce menu clutter
- Easy to integrate
- Backward compatible
- Well documented
**Result**: All goals achieved! 🎉
---
## Quick Reference
### Load Order
1. Moose.lua
2. **Moose_MenuManager.lua** ← First!
3. CTLD.lua ← F2
4. FAC.lua ← F3
5. Other scripts ← Under F1
### Integration Pattern
```lua
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Name")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "Name")
end
```
### Result
- F1: Mission Options (all other scripts)
- F2: CTLD (always)
- F3: FAC (always)
Simple, effective, backward compatible! 👍

View File

@ -1,368 +0,0 @@
--[[
TEMPLATE: Integrating Scripts with MenuManager
Use this template when updating existing scripts or creating new ones
to work with the Unified F10 Menu System.
]]--
--=============================================================================
-- PATTERN 1: Coalition Menu (most common)
--=============================================================================
-- Configuration (add at top of script)
local EnableF10Menu = true -- Set to false to disable F10 menu
-- Your existing script code here...
-- At the point where you create your menu (usually near the end):
if EnableF10Menu then
local MyScriptMenu
-- Use MenuManager if available, otherwise fallback to standard
if MenuManager then
MyScriptMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script Name")
else
MyScriptMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script Name")
end
-- Add your menu commands
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Command 1", MyScriptMenu, MyFunction1)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Command 2", MyScriptMenu, MyFunction2)
-- Add submenus if needed
local SubMenu = MENU_COALITION:New(coalition.side.BLUE, "Advanced Options", MyScriptMenu)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Advanced Command", SubMenu, MyAdvancedFunction)
end
--=============================================================================
-- PATTERN 2: Mission Menu (available to all players)
--=============================================================================
if EnableF10Menu then
local MyScriptMenu
if MenuManager then
MyScriptMenu = MenuManager.CreateMissionMenu("My Script Name")
else
MyScriptMenu = MENU_MISSION:New("My Script Name")
end
MENU_MISSION_COMMAND:New("Command 1", MyScriptMenu, MyFunction1)
MENU_MISSION_COMMAND:New("Command 2", MyScriptMenu, MyFunction2)
end
--=============================================================================
-- PATTERN 3: Dual Coalition Menu (separate for each side)
--=============================================================================
if EnableF10Menu then
-- Blue Coalition Menu
local BlueMenu
if MenuManager then
BlueMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script Name")
else
BlueMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script Name")
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Blue Command", BlueMenu, MyBlueFunction)
-- Red Coalition Menu
local RedMenu
if MenuManager then
RedMenu = MenuManager.CreateCoalitionMenu(coalition.side.RED, "My Script Name")
else
RedMenu = MENU_COALITION:New(coalition.side.RED, "My Script Name")
end
MENU_COALITION_COMMAND:New(coalition.side.RED, "Red Command", RedMenu, MyRedFunction)
end
--=============================================================================
-- PATTERN 4: Complex Menu with Multiple Levels
--=============================================================================
if EnableF10Menu then
-- Create main menu
local MainMenu
if MenuManager then
MainMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
MainMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
end
-- Create submenus (these don't use MenuManager, just standard MOOSE)
local SettingsMenu = MENU_COALITION:New(coalition.side.BLUE, "Settings", MainMenu)
local ActionsMenu = MENU_COALITION:New(coalition.side.BLUE, "Actions", MainMenu)
-- Add commands to submenus
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Enable Feature", SettingsMenu, EnableFeature)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Disable Feature", SettingsMenu, DisableFeature)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Action 1", ActionsMenu, Action1)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Action 2", ActionsMenu, Action2)
end
--=============================================================================
-- PATTERN 5: Dynamic Menu (populated at runtime)
--=============================================================================
local MainMenu = nil
function CreateDynamicMenu()
if EnableF10Menu then
-- Create main menu if it doesn't exist
if not MainMenu then
if MenuManager then
MainMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Dynamic Menu")
else
MainMenu = MENU_COALITION:New(coalition.side.BLUE, "Dynamic Menu")
end
end
-- Clear and rebuild menu items
-- Note: MOOSE doesn't have a built-in clear, so you may need to track menu items
-- and remove them, or just add new items dynamically
-- Example: Add menu items based on game state
for i = 1, #SomeGameStateArray do
local item = SomeGameStateArray[i]
MENU_COALITION_COMMAND:New(
coalition.side.BLUE,
"Option " .. i .. ": " .. item.name,
MainMenu,
function() HandleOption(i) end
)
end
end
end
-- Call this whenever game state changes
SCHEDULER:New(nil, CreateDynamicMenu, {}, 30, 30) -- Rebuild every 30 seconds
--=============================================================================
-- PATTERN 6: Conditional Menu Creation
--=============================================================================
-- Only create menu if certain conditions are met
if EnableF10Menu then
-- Check if feature is enabled in mission
if CVN_GROUP and CVN_GROUP:IsAlive() then
local CVNMenu
if MenuManager then
CVNMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "CVN Command")
else
CVNMenu = MENU_COALITION:New(coalition.side.BLUE, "CVN Command")
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Request CVN Status", CVNMenu, GetCVNStatus)
end
-- Check if airbase is available
if AIRBASE:FindByName("Kutaisi") then
local AirbaseMenu
if MenuManager then
AirbaseMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Kutaisi ATC")
else
AirbaseMenu = MENU_COALITION:New(coalition.side.BLUE, "Kutaisi ATC")
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Request Landing", AirbaseMenu, RequestLanding)
end
end
--=============================================================================
-- PATTERN 7: Menu with Event Handlers
--=============================================================================
if EnableF10Menu then
local SettingsMenu
if MenuManager then
SettingsMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Settings")
else
SettingsMenu = MENU_COALITION:New(coalition.side.BLUE, "Settings")
end
-- Toggle setting with feedback
local FeatureEnabled = false
local function ToggleFeature()
FeatureEnabled = not FeatureEnabled
local status = FeatureEnabled and "ENABLED" or "DISABLED"
MESSAGE:New("Feature is now " .. status, 5):ToBlue()
-- Update menu text (by recreating menu or showing status elsewhere)
-- Note: MOOSE doesn't allow changing menu text dynamically
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Toggle Feature", SettingsMenu, ToggleFeature)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Show Status", SettingsMenu, function()
local status = FeatureEnabled and "ENABLED" or "DISABLED"
MESSAGE:New("Feature Status: " .. status, 5):ToBlue()
end)
end
--=============================================================================
-- PATTERN 8: Integrating Existing Script with Minimal Changes
--=============================================================================
-- BEFORE (typical existing script):
-- local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
-- MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Command", MyMenu, MyFunction)
-- AFTER (minimal change for MenuManager support):
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script") -- Keep original as fallback
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Command", MyMenu, MyFunction)
-- That's it! Only 3 lines changed, and it's backward compatible.
--=============================================================================
-- BEST PRACTICES
--=============================================================================
--[[
1. Always check if MenuManager exists before using it
- Ensures backward compatibility
- Script works even if MenuManager not loaded
2. Add EnableF10Menu config option
- Allows mission makers to disable menus without editing code
- Useful for training missions or specific scenarios
3. Use descriptive menu names
- "INTEL HQ" is better than "Intel"
- "CVN Command" is better than "CVN"
4. Group related functions in submenus
- Keeps main menu clean
- Easier to navigate for players
5. Provide feedback for actions
- Use MESSAGE:New() to confirm actions
- Let players know what happened
6. Consider coalition-specific menus
- Some features should only be available to one side
- Use MENU_COALITION instead of MENU_MISSION
7. Test without MenuManager
- Ensure fallback works
- Don't rely on MenuManager being present
8. Document your menu structure
- Comment what each menu does
- Makes maintenance easier
9. Clean up menus when no longer needed
- Remove dynamic menus when feature is destroyed
- Example: Remove CVN menu when CVN is sunk
10. Use consistent naming
- All scripts should follow same naming convention
- Makes F10 menu predictable for players
]]--
--=============================================================================
-- COMMON MISTAKES TO AVOID
--=============================================================================
--[[
MISTAKE 1: Not checking if MenuManager exists
local MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
end
MISTAKE 2: Creating menu too early
Creating menu before MOOSE is fully initialized
Create menus after all required objects exist
Use SCHEDULER:New() with delay if needed
MISTAKE 3: Wrong menu type
Using MENU_MISSION when coalition-specific menu needed
Use MENU_COALITION for per-coalition features
MISTAKE 4: Not wrapping in EnableF10Menu check
Always creating menu even when not wanted
if EnableF10Menu then ... end
MISTAKE 5: Creating too many root menus
Each script creates its own root menu
Use MenuManager to group under "Mission Options"
]]--
--=============================================================================
-- TESTING CHECKLIST
--=============================================================================
--[[
After integrating MenuManager:
Script loads without errors
Menu appears in correct location (under Mission Options)
All menu commands work
Script works WITHOUT MenuManager (fallback)
EnableF10Menu = false hides menu
No duplicate menus created
Menu names are clear and descriptive
Coalition-specific menus only show to correct side
dcs.log shows no errors
Test in multiplayer (if applicable)
]]--
--=============================================================================
-- EXAMPLE: Complete Script Integration
--=============================================================================
-- Here's a complete example showing how to integrate a typical script:
--[[
-- Original Script: MyFeatureScript.lua
local function DoSomething()
MESSAGE:New("Did something!", 5):ToBlue()
end
local function DoSomethingElse()
MESSAGE:New("Did something else!", 5):ToBlue()
end
local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Feature")
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Something", MyMenu, DoSomething)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Something Else", MyMenu, DoSomethingElse)
]]--
-- Updated Script: MyFeatureScript.lua (MenuManager Compatible)
-- Configuration
local EnableF10Menu = true -- Set to false to disable F10 menu
-- Feature functions (unchanged)
local function DoSomething()
MESSAGE:New("Did something!", 5):ToBlue()
end
local function DoSomethingElse()
MESSAGE:New("Did something else!", 5):ToBlue()
end
-- Menu creation (updated)
if EnableF10Menu then
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Feature")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Feature")
end
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Something", MyMenu, DoSomething)
MENU_COALITION_COMMAND:New(coalition.side.BLUE, "Do Something Else", MyMenu, DoSomethingElse)
end
-- That's it! Your script now works with MenuManager and remains backward compatible.
--=============================================================================

View File

@ -1,222 +0,0 @@
# F10 Menu System - Update Notes
## Issues Fixed
### Issue 1: Duplicate "Mission Options" Menu
**Problem**: Two "Mission Options" menus appeared, one was empty.
**Cause**: MenuManager was creating three parent menus:
- MENU_COALITION for Blue (correct)
- MENU_COALITION for Red (correct)
- MENU_MISSION for all players (unnecessary - this created the empty duplicate)
**Fix**: Removed the MENU_MISSION parent menu creation. Scripts that need mission-wide menus (like TADC) now create them directly at root level, which is the correct behavior.
### Issue 2: OnBirthMessage Menu Not Integrated
**Problem**: OnBirthMessage script created its own root-level menu without coordination with MenuManager.
**Cause**: OnBirthMessage uses `missionCommands.addSubMenuForGroup()` which creates per-group menus (like CTLD and FAC). These cannot be nested under coalition menus.
**Fix**:
- Added configuration option to OnBirthMessage
- Added documentation explaining why it must remain at root level
- Updated load order guidance
---
## Understanding DCS Menu Types
### Three Types of F10 Menus
1. **MENU_COALITION** - Visible to one coalition only
- Example: Blue Intel, Red Intel
- Can be nested under other coalition menus
- MenuManager creates "Mission Options" parent for these
2. **MENU_MISSION** - Visible to ALL players
- Example: TADC Utilities
- Cannot be nested under coalition-specific menus
- Should be created at root level
3. **Group Menus** (missionCommands) - Visible per-group
- Example: CTLD, FAC, Welcome Messages
- Each player/group gets their own instance
- **CANNOT be nested** under coalition or mission menus
- Must remain at root level
---
## Corrected Menu Structure
```
F10 - Other Radio Items
├─ F1 - Mission Options (Blue) ← MENU_COALITION (Blue players only)
│ ├─ INTEL HQ
│ ├─ Zone Control
│ └─ CVN Command
├─ F1 - Mission Options (Red) ← MENU_COALITION (Red players only)
│ ├─ INTEL HQ
│ └─ (Red-specific items)
├─ F2 - TADC Utilities ← MENU_MISSION (all players)
├─ F3 - CTLD ← Group menu (per-player)
├─ F4 - AFAC Control ← Group menu (per-player)
└─ F5 - Welcome Messages ← Group menu (per-player)
```
**Note**: The F-key numbers will vary based on load order. What matters is:
- Coalition-specific menus go under "Mission Options"
- Group menus (CTLD, FAC, Welcome) stay at root
- Mission menus (visible to all) stay at root
---
## Updated Load Order
```
Mission Editor Triggers:
1. Moose.lua
2. Moose_MenuManager.lua ← Creates "Mission Options" for Blue/Red
3. CTLD.lua ← Group menu (any position)
4. Moose_FAC2MarkRecceZone.lua ← Group menu (any position)
5. OnBirthMessage.lua ← Group menu (any position)
6. Moose_Intel.lua ← Under Mission Options
7. Moose_CaptureZones.lua ← Under Mission Options
8. Moose_NavalGroup.lua ← Under Mission Options
9. Moose_TADC_Load2nd.lua ← MENU_MISSION (root level)
```
**Key Points**:
- Load MenuManager first (always)
- Group menu scripts (CTLD, FAC, Welcome) can be in any order
- Coalition-specific scripts register under "Mission Options"
- Mission-wide scripts create root-level menus
---
## Scripts by Menu Type
### Coalition Menus (Under "Mission Options")
- ✅ Moose_Intel.lua
- ✅ Moose_CaptureZones.lua
- ✅ Moose_NavalGroup.lua
### Mission Menus (Root Level)
- ✅ Moose_TADC_Load2nd.lua
### Group Menus (Root Level - Cannot Be Nested)
- ⚠️ CTLD.lua
- ⚠️ Moose_FAC2MarkRecceZone.lua
- ⚠️ OnBirthMessage.lua
---
## Configuration Changes
### OnBirthMessage.lua
Added configuration option:
```lua
-- At top of file (line 5)
local EnableF10Menu = true -- Set to false to disable F10 menu
```
Set to `false` to hide the Welcome Messages F10 menu entirely while keeping the welcome messages active.
---
## Why Group Menus Can't Be Nested
**Technical Limitation**: DCS's `missionCommands.addSubMenuForGroup()` creates menus that are specific to each player's group. These exist in a different namespace than MOOSE's coalition/mission menus and cannot be nested under them.
**Analogy**: Think of it like this:
- Coalition menus are like "team channels" (Blue team sees Blue menus)
- Mission menus are like "global broadcast" (everyone sees same menu)
- Group menus are like "private DM" (each player has their own)
You can't put a private DM inside a team channel - they're different systems.
---
## What This Means for Users
### Blue Players See:
```
F10
├─ Mission Options ← Their Blue-specific utilities
├─ TADC Utilities ← Everyone sees this
├─ CTLD ← Their own CTLD instance
├─ AFAC Control ← Their own FAC instance
└─ Welcome Messages ← Their own settings
```
### Red Players See:
```
F10
├─ Mission Options ← Their Red-specific utilities
├─ TADC Utilities ← Everyone sees this
├─ CTLD ← Their own CTLD instance
├─ AFAC Control ← Their own FAC instance
└─ Welcome Messages ← Their own settings
```
**Key Benefit**: Each coalition's "Mission Options" is clean and organized, containing only their relevant utilities.
---
## Testing Checklist
After applying these fixes:
- [ ] Only ONE "Mission Options" appears (per coalition)
- [ ] "Mission Options" is NOT empty
- [ ] TADC Utilities appears at root (visible to all)
- [ ] CTLD appears at root (per-player)
- [ ] FAC appears at root (per-player)
- [ ] Welcome Messages appears at root (per-player)
- [ ] Blue players see Blue-specific items under Mission Options
- [ ] Red players see Red-specific items under Mission Options
- [ ] No duplicate or empty menus
---
## Files Modified
### c:\DCS_MissionDev\DCS_Kola\Operation_Polar_Shield\Moose_MenuManager.lua
- Removed MENU_MISSION parent menu creation
- Updated CreateMissionMenu() to create root-level menus
- Added documentation about menu types
### c:\DCS_MissionDev\DCS_Kola\Operation_Polar_Shield\OnBirthMessage.lua
- Added EnableF10Menu configuration option
- Added documentation about group menu limitations
- Improved error handling
---
## Summary
**What Changed**:
1. ✅ Removed duplicate "Mission Options" menu
2. ✅ Added config option to OnBirthMessage
3. ✅ Updated documentation about menu types
4. ✅ Clarified load order requirements
**What Stayed The Same**:
- All functionality preserved
- Menu organization still clean
- Coalition-specific items still grouped
- Backward compatible
**Key Takeaway**:
- Coalition menus → Can be organized under "Mission Options"
- Mission menus → Must stay at root (everyone sees them)
- Group menus → Must stay at root (can't be nested)
This is a DCS limitation, not a bug!
---
*Updated: November 9, 2025*

View File

@ -1,366 +0,0 @@
# F10 Menu System - Visual Guide
## Before MenuManager (Typical Mission)
```
F10 - Other Radio Items
├─ F1 - Zone Control ← Changes based on load order
├─ F2 - TADC Utilities ← Inconsistent position
├─ F3 - INTEL HQ ← Player has to search
├─ F4 - CVN Command ← Different every mission
├─ F5 - CTLD ← Where is it this time?
└─ F6 - AFAC Control ← Never the same
```
**Problems:**
- CTLD position changes between missions
- Too many root-level menus (cluttered)
- Players waste time searching for CTLD/FAC
- No organization or grouping
---
## After MenuManager (Organized)
```
F10 - Other Radio Items
├─ F1 - Mission Options ← All utility scripts here
│ ├─ INTEL HQ
│ ├─ Zone Control
│ ├─ CVN Command
│ └─ TADC Utilities
├─ F2 - CTLD ← ALWAYS HERE! Muscle memory works!
│ ├─ Check Cargo
│ ├─ Troop Transport
│ │ ├─ Unload / Extract Troops
│ │ └─ Load From Zone
│ ├─ Vehicle / FOB Transport
│ │ ├─ Unload Vehicles
│ │ └─ Load / Extract Vehicles
│ ├─ Vehicle / FOB Crates / Drone
│ └─ CTLD Commands
└─ F3 - AFAC Control ← ALWAYS HERE! Predictable!
├─ Targeting Mode
│ ├─ Auto Mode ON
│ ├─ Auto Mode OFF
│ └─ Manual Targeting
├─ Laser Codes
├─ Marker Settings
│ ├─ Smoke Color
│ └─ Flare Color
└─ AFAC Status
```
**Benefits:**
- CTLD always F2 (press F10 → F2 every time)
- FAC always F3 (press F10 → F3 every time)
- Other menus organized under F1
- Clean, predictable interface
---
## Load Order Visualization
```
Mission Editor Triggers - Script Load Order:
┌────────────────────────────────────────┐
│ 1. Moose.lua │ ← MOOSE Framework
├────────────────────────────────────────┤
│ 2. Moose_MenuManager.lua │ ← Creates "Mission Options" at F1
├────────────────────────────────────────┤
│ 3. CTLD.lua │ ← Creates CTLD menu → becomes F2
├────────────────────────────────────────┤
│ 4. Moose_FAC2MarkRecceZone.lua │ ← Creates FAC menu → becomes F3
├────────────────────────────────────────┤
│ 5. Moose_Intel.lua │ ← Registers under Mission Options
│ 6. Moose_CaptureZones.lua │ ← Registers under Mission Options
│ 7. Moose_NavalGroup.lua │ ← Registers under Mission Options
│ 8. Moose_TADC_Load2nd.lua │ ← Registers under Mission Options
│ 9. ... other scripts ... │ ← All register under Mission Options
└────────────────────────────────────────┘
Result: F1 = Mission Options, F2 = CTLD, F3 = FAC
```
---
## Menu Type Comparison
### Coalition Menu (MENU_COALITION)
```
BLUE Players See: RED Players See:
F10 F10
├─ Mission Options ├─ Mission Options
│ ├─ INTEL HQ (Blue) │ ├─ INTEL HQ (Red)
│ └─ Zone Control (Blue) │ └─ (Red content)
├─ CTLD ├─ CTLD
└─ AFAC Control └─ AFAC Control
```
### Mission Menu (MENU_MISSION)
```
ALL Players See Same:
F10
├─ Mission Options
│ └─ TADC Utilities ← Everyone sees this
├─ CTLD
└─ AFAC Control
```
### Group Menu (missionCommands - CTLD/FAC use this)
```
Each Group Sees Own:
Group #1: Group #2:
F10 F10
├─ CTLD (Group #1) ├─ CTLD (Group #2)
└─ AFAC (Group #1) └─ AFAC (Group #2)
Can't be nested under parent menus - that's why CTLD/FAC stay at root
```
---
## Integration Flowchart
```
┌─────────────────────────────────────────┐
│ Your Script Wants to Create F10 Menu │
└──────────────┬──────────────────────────┘
┌──────────────┐
│ MenuManager │
│ Available? │
└──┬───────┬───┘
│ │
Yes │ │ No
│ │
▼ ▼
┌──────────┐ ┌─────────────┐
│ Register │ │ Create Root │
│ Under │ │ Menu │
│ Mission │ │ (Fallback) │
│ Options │ │ │
└──────────┘ └─────────────┘
│ │
└──────┬───────┘
┌────────────────┐
│ Menu Appears │
│ in F10 │
└────────────────┘
```
---
## Player Experience Comparison
### Without MenuManager
```
Player: "Where's CTLD?"
→ Checks F1: Zone Control
→ Checks F2: TADC
→ Checks F3: Intel
→ Checks F4: CVN
→ Checks F5: CTLD ← FOUND IT!
(Time wasted: 10-15 seconds)
Next Mission:
Player: "Where's CTLD now?"
→ Checks F1: Intel
→ Checks F2: CVN
→ Checks F3: Zone Control
→ Checks F4: CTLD ← Position changed!
(Frustration: High)
```
### With MenuManager
```
Player: "Need CTLD"
→ Press F10
→ Press F2
→ Found it!
(Time: 1 second, every time)
Next Mission:
Player: "Need CTLD"
→ Press F10
→ Press F2
→ Found it! (same position)
(Frustration: None, Efficiency: Max)
```
---
## Configuration Examples
### Example 1: Disable All Extra Menus (Training Mission)
```lua
-- In Moose_MenuManager.lua
MenuManager.Config.EnableMissionOptionsMenu = false
-- Result: Only CTLD and FAC appear
F10
├─ CTLD
└─ AFAC Control
```
### Example 2: Disable Specific Script Menu
```lua
-- In Moose_Intel.lua
local EnableF10Menu = false
-- Result: Intel menu hidden, others remain
F10
├─ Mission Options
│ ├─ Zone Control
│ ├─ CVN Command
│ └─ TADC Utilities
├─ CTLD
└─ AFAC Control
```
### Example 3: Custom Parent Menu Name
```lua
-- In Moose_MenuManager.lua
MenuManager.Config.MissionOptionsMenuName = "Utilities"
-- Result: Different parent menu name
F10
├─ Utilities ← Changed from "Mission Options"
│ ├─ INTEL HQ
│ └─ ...
├─ CTLD
└─ AFAC Control
```
---
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────┐
│ DCS Mission │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌────────────┐ │
│ │ Moose.lua │ ← MOOSE Framework (loaded first) │
│ └─────┬──────┘ │
│ │ │
│ ┌─────▼───────────────┐ │
│ │ MenuManager │ ← Menu System (loaded 2nd) │
│ │ - Creates F1 │ │
│ │ - Provides API │ │
│ └─────┬───────────────┘ │
│ │ │
│ ├─────────┬─────────┬─────────┬──────────┐ │
│ │ │ │ │ │ │
│ ┌─────▼───┐ ┌──▼────┐ ┌──▼────┐ ┌──▼────┐ ┌───▼──┐ │
│ │ Intel │ │ Zones │ │ CVN │ │ TADC │ │ ... │ │
│ │ Script │ │Script │ │Script │ │Script │ │ │ │
│ └─────┬───┘ └──┬────┘ └──┬────┘ └──┬────┘ └───┬──┘ │
│ │ │ │ │ │ │
│ └────────┴─────────┴─────────┴──────────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ Mission │ │
│ │ Options │ ← F1 │
│ │ (F10) │ │
│ └────────────┘ │
│ │
│ ┌─────────┐ │
│ │ CTLD │ ← Loaded after MenuManager → F2 │
│ └─────────┘ │
│ │
│ ┌─────────┐ │
│ │ FAC │ ← Loaded after CTLD → F3 │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────┘
Players press F10 and see:
F1: Mission Options (all utility scripts)
F2: CTLD (always here)
F3: FAC (always here)
```
---
## File Structure
```
Operation_Polar_Shield/
├── Core System
│ └── Moose_MenuManager.lua ← The menu manager
├── Scripts (Updated)
│ ├── Moose_Intel.lua ← Uses MenuManager
│ ├── Moose_CaptureZones.lua ← Uses MenuManager
│ ├── Moose_NavalGroup.lua ← Uses MenuManager
│ └── Moose_TADC_Load2nd.lua ← Uses MenuManager
├── Scripts (Unchanged)
│ ├── CTLD.lua ← Creates own menu (F2)
│ └── Moose_FAC2MarkRecceZone.lua ← Creates own menu (F3)
└── Documentation
├── F10_MENU_SYSTEM_GUIDE.md ← Full guide
├── F10_MENU_QUICK_REF.md ← Quick reference
├── MENUMANAGER_SUMMARY.md ← Summary
├── MENUMANAGER_TEMPLATE.lua ← Code templates
├── EXAMPLE_MISSION_SETUP.lua ← Setup example
└── MENUMANAGER_VISUAL_GUIDE.md ← This file!
```
---
## Success Metrics
### Before
- ❌ CTLD position: Variable (F4-F8)
- ❌ FAC position: Variable (F5-F9)
- ❌ Root menus: 6-8 items (cluttered)
- ❌ Player navigation time: 10-15 seconds
- ❌ Player satisfaction: Low (complaints)
### After
- ✅ CTLD position: Always F2
- ✅ FAC position: Always F3
- ✅ Root menus: 3 items (clean)
- ✅ Player navigation time: 1 second
- ✅ Player satisfaction: High (positive feedback)
---
## Quick Reference
```
┌──────────────────────────────────────┐
│ Press F10, then: │
├──────────────────────────────────────┤
│ F1 → Mission Options │
│ All utility/support features │
│ │
│ F2 → CTLD │
│ Cargo/troop transport │
│ │
│ F3 → AFAC Control │
│ Forward air controller │
└──────────────────────────────────────┘
Every mission. Every time. Consistent!
```
---
**Remember**: The key to this system working is **load order**!
1. Load MenuManager first
2. Load CTLD second (→ F2)
3. Load FAC third (→ F3)
4. Load everything else (→ under F1)
Simple, effective, professional! 🎯

View File

@ -43,7 +43,8 @@ local blueCfg = {
--DropZones = { { name = 'BRAVO', flag = 9002, activeWhen = 0 } }, --DropZones = { { name = 'BRAVO', flag = 9002, activeWhen = 0 } },
--FOBZones = { { name = 'CHARLIE', flag = 9003, activeWhen = 0 } }, --FOBZones = { { name = 'CHARLIE', flag = 9003, activeWhen = 0 } },
MASHZones = { { name = 'A1', freq = '256.0 AM', radius = 500, flag = 9010, activeWhen = 0 } }, MASHZones = { { name = 'A1', freq = '256.0 AM', radius = 500, flag = 9010, activeWhen = 0 } },
}, SalvageDropZones= { { name = 'S1', radius = 500, flag = 9011, activeWhen = 0 } },
}, -- closes Zones table
BuildRequiresGroundCrates = true, BuildRequiresGroundCrates = true,
} }
env.info('[DEBUG] blueCfg.Zones.MASHZones count: ' .. tostring(blueCfg.Zones and blueCfg.Zones.MASHZones and #blueCfg.Zones.MASHZones or 'NIL')) env.info('[DEBUG] blueCfg.Zones.MASHZones count: ' .. tostring(blueCfg.Zones and blueCfg.Zones.MASHZones and #blueCfg.Zones.MASHZones or 'NIL'))

View File

@ -1,971 +0,0 @@
.--[[
Simple AFAC System v1.0
========================
A lightweight, standalone Forward Air Controller system for DCS World.
No external dependencies required.
Features:
- Automatic AFAC aircraft detection
- Target scanning with line-of-sight verification
- Laser designation with customizable codes
- Visual marking (smoke/flares)
- F10 map markers with detailed target information
- Auto and manual targeting modes
- Clean F10 menu integration
Author: Based on original FAC script, streamlined for simplicity
]]
-- =====================================================
-- CONFIGURATION
-- =====================================================
AFAC = {}
AFAC.Config = {
-- Detection ranges
maxRange = 18520, -- Maximum AFAC detection range in meters
-- Aircraft types that auto-qualify as AFAC (perfect for dynamic spawning)
afacAircraft = {
-- Helicopters
"SA342L",
"SA342Mistral",
"SA342Minigun",
"UH-60L",
"UH-1H",
"OH-58D",
"AH-64D_BLK_II",
"Mi-8MT",
"Mi-24P",
"Ka-50",
"Ka-50_3",
"AH-1W",
-- Light Aircraft
"TF-51D",
"P-51D-30-NA",
"Bf-109K-4",
"FW-190D9",
"I-16",
"C-101EB",
"C-101CC",
"L-39ZA",
"L-39C",
-- Jets (for high-speed FAC)
--"A-10C",
--"A-10C_2",
--"AV8BNA",
--"F-16C_50",
--"FA-18C_hornet",
--"F-14",
--"A-4E-C"
},
-- Available laser codes
laserCodes = {'1688', '1677', '1666', '1113', '1115', '1111'},
-- Smoke/flare colors
smokeColors = {
GREEN = 0,
RED = 1,
WHITE = 2,
ORANGE = 3,
BLUE = 4
},
-- Default marker settings
defaultSmokeColor = {
[1] = 4, -- RED coalition uses BLUE smoke
[2] = 3 -- BLUE coalition uses ORANGE smoke
},
-- Marker duration (seconds)
mapMarkerDuration = 120,
smokeInterval = 300,
-- Target update intervals (throttled to reduce per-second scanning)
autoUpdateInterval = 2.5,
manualScanRange = 18520,
-- Debug mode
debug = true
}
-- =====================================================
-- CORE DATA STRUCTURES
-- =====================================================
AFAC.Data = {
pilots = {}, -- Active AFAC pilots
targets = {}, -- Current targets per pilot
laserPoints = {}, -- Laser designations
irPoints = {}, -- IR pointers
smokeMarks = {}, -- Smoke marker tracking
onStation = {}, -- On-station status
laserCodes = {}, -- Assigned laser codes per pilot
markerSettings = {}, -- Per-pilot marker preferences
menuIds = {}, -- F10 menu tracking
manualTargets = {}, -- Manual mode target lists
nextMarkerId = 1000 -- Map marker ID counter
}
-- =====================================================
-- UTILITY FUNCTIONS
-- =====================================================
-- Debug logging
function AFAC.log(message)
if AFAC.Config.debug then
env.info("AFAC: " .. tostring(message))
end
end
-- Check if table contains value
function AFAC.contains(table, value)
for i = 1, #table do
if table[i] == value then
return true
end
end
return false
end
-- Calculate distance between two points
function AFAC.getDistance(point1, point2)
local dx = point1.x - point2.x
local dz = point1.z - point2.z
return math.sqrt(dx * dx + dz * dz)
end
-- Get unit heading in radians
function AFAC.getHeading(unit)
local unitPos = unit:getPosition()
if unitPos then
local heading = math.atan2(unitPos.x.z, unitPos.x.x)
if heading < 0 then
heading = heading + 2 * math.pi
end
return heading
end
return 0
end
-- Convert coordinates to Lat/Long string
function AFAC.coordToString(point)
local lat, lon = coord.LOtoLL(point)
local latDeg = math.floor(lat)
local latMin = (lat - latDeg) * 60
local lonDeg = math.floor(lon)
local lonMin = (lon - lonDeg) * 60
local latDir = latDeg >= 0 and "N" or "S"
local lonDir = lonDeg >= 0 and "E" or "W"
return string.format("%02d°%06.3f'%s %03d°%06.3f'%s",
math.abs(latDeg), latMin, latDir,
math.abs(lonDeg), lonMin, lonDir)
end
-- Get MGRS coordinate string
function AFAC.getMGRS(point)
local lat, lon = coord.LOtoLL(point)
-- Simplified MGRS - in real implementation you'd want full MGRS conversion
return string.format("MGRS: %02d%s%05d%05d",
math.floor(lat), "ABC"[math.random(1,3)],
math.random(10000, 99999), math.random(10000, 99999))
end
-- Get group ID from unit
function AFAC.getGroupId(unit)
local group = unit:getGroup()
if group then
return group:getID()
end
return nil
end
-- Notify coalition
function AFAC.notifyCoalition(message, duration, coalition)
trigger.action.outTextForCoalition(coalition, message, duration or 10)
end
-- =====================================================
-- AIRCRAFT DETECTION & MANAGEMENT
-- =====================================================
-- Check if unit qualifies as AFAC (by aircraft type only)
function AFAC.isAFAC(unit)
if not unit then
AFAC.log("isAFAC: unit is nil")
return false
end
local unitType = unit:getTypeName()
AFAC.log("Checking unit type: " .. tostring(unitType))
-- Check aircraft type only - perfect for dynamic spawning
local result = AFAC.contains(AFAC.Config.afacAircraft, unitType)
AFAC.log("isAFAC result for " .. unitType .. ": " .. tostring(result))
return result
end
-- Add pilot to AFAC system
function AFAC.addPilot(unit)
local unitName = unit:getName()
local groupId = AFAC.getGroupId(unit)
if not groupId then return end
-- Initialize pilot data
AFAC.Data.pilots[unitName] = {
name = unitName,
unit = unit,
coalition = unit:getCoalition(),
groupId = groupId
}
-- Set default laser code
AFAC.Data.laserCodes[unitName] = AFAC.Config.laserCodes[1]
-- Set default marker settings
AFAC.Data.markerSettings[unitName] = {
type = "SMOKE",
color = AFAC.Config.defaultSmokeColor[unit:getCoalition()]
}
-- Set on station
AFAC.Data.onStation[unitName] = true
-- Notify player
local message = string.format("AFAC System Active\nAircraft: %s\nUse F10 menu to control targeting",
unit:getTypeName())
trigger.action.outTextForGroup(groupId, message, 15)
-- Start auto-lasing
AFAC.startAutoLasing(unitName)
AFAC.log("Added AFAC pilot: " .. unitName)
end
-- Remove pilot from system
function AFAC.removePilot(unitName)
if not AFAC.Data.pilots[unitName] then return end
-- Clean up laser points
AFAC.cancelLasing(unitName)
-- Clean up data
AFAC.Data.pilots[unitName] = nil
AFAC.Data.targets[unitName] = nil
AFAC.Data.laserPoints[unitName] = nil
AFAC.Data.irPoints[unitName] = nil
AFAC.Data.smokeMarks[unitName] = nil
AFAC.Data.onStation[unitName] = nil
AFAC.Data.laserCodes[unitName] = nil
AFAC.Data.markerSettings[unitName] = nil
AFAC.Data.manualTargets[unitName] = nil
AFAC.log("Removed AFAC pilot: " .. unitName)
end
-- =====================================================
-- TARGET DETECTION
-- =====================================================
-- Find nearest visible enemy to AFAC unit
function AFAC.findNearestTarget(afacUnit, maxRange)
local afacPoint = afacUnit:getPoint()
local afacCoalition = afacUnit:getCoalition()
local enemyCoalition = afacCoalition == 1 and 2 or 1
local nearestTarget = nil
local nearestDistance = maxRange or AFAC.Config.maxRange
-- Search for enemy units
local searchVolume = {
id = world.VolumeType.SPHERE,
params = {
point = afacPoint,
radius = nearestDistance
}
}
local function checkUnit(foundUnit)
if foundUnit:getCoalition() ~= enemyCoalition then return end
if not foundUnit:isActive() then return end
if foundUnit:inAir() then return end
if foundUnit:getLife() <= 1 then return end
local unitPoint = foundUnit:getPoint()
local distance = AFAC.getDistance(afacPoint, unitPoint)
if distance >= nearestDistance then return end
-- Check line of sight
local offsetAfacPos = {x = afacPoint.x, y = afacPoint.y + 2, z = afacPoint.z}
local offsetUnitPos = {x = unitPoint.x, y = unitPoint.y + 2, z = unitPoint.z}
if not land.isVisible(offsetAfacPos, offsetUnitPos) then return end
-- Priority system: SAMs first, then vehicles, then infantry
local priority = 1
if foundUnit:hasAttribute("SAM TR") or foundUnit:hasAttribute("IR Guided SAM") then
priority = 0.1 -- Highest priority
elseif foundUnit:hasAttribute("Vehicles") then
priority = 0.5
end
local adjustedDistance = distance * priority
if adjustedDistance < nearestDistance then
nearestDistance = adjustedDistance
nearestTarget = foundUnit
end
end
world.searchObjects(Object.Category.UNIT, searchVolume, checkUnit)
return nearestTarget
end
-- Scan area for multiple targets (manual mode)
function AFAC.scanForTargets(afacUnit, maxCount)
local afacPoint = afacUnit:getPoint()
local afacCoalition = afacUnit:getCoalition()
local enemyCoalition = afacCoalition == 1 and 2 or 1
local targets = {}
local samTargets = {}
local searchVolume = {
id = world.VolumeType.SPHERE,
params = {
point = afacPoint,
radius = AFAC.Config.manualScanRange
}
}
local function checkUnit(foundUnit)
if foundUnit:getCoalition() ~= enemyCoalition then return end
if not foundUnit:isActive() then return end
if foundUnit:inAir() then return end
if foundUnit:getLife() <= 1 then return end
local unitPoint = foundUnit:getPoint()
-- Check line of sight
local offsetAfacPos = {x = afacPoint.x, y = afacPoint.y + 2, z = afacPoint.z}
local offsetUnitPos = {x = unitPoint.x, y = unitPoint.y + 2, z = unitPoint.z}
if not land.isVisible(offsetAfacPos, offsetUnitPos) then return end
-- Separate SAMs from other targets
if foundUnit:hasAttribute("SAM TR") or foundUnit:hasAttribute("IR Guided SAM") then
table.insert(samTargets, foundUnit)
else
table.insert(targets, foundUnit)
end
end
world.searchObjects(Object.Category.UNIT, searchVolume, checkUnit)
-- Priority: SAMs first, then others
local finalTargets = {}
for i = 1, math.min(#samTargets, maxCount or 10) do
table.insert(finalTargets, samTargets[i])
end
local remainingSlots = (maxCount or 10) - #finalTargets
for i = 1, math.min(#targets, remainingSlots) do
table.insert(finalTargets, targets[i])
end
return finalTargets
end
-- =====================================================
-- LASER DESIGNATION SYSTEM
-- =====================================================
-- Start laser designation on target
function AFAC.startLasing(afacUnit, target, laserCode)
local unitName = afacUnit:getName()
-- Cancel existing lasing
AFAC.cancelLasing(unitName)
local targetPoint = target:getPoint()
local targetVector = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
-- Create laser and IR points
local success, result = pcall(function()
local laserSpot = Spot.createLaser(afacUnit, {x = 0, y = 2, z = 0}, targetVector, laserCode)
local irSpot = Spot.createInfraRed(afacUnit, {x = 0, y = 2, z = 0}, targetVector)
return {laser = laserSpot, ir = irSpot}
end)
if success and result then
AFAC.Data.laserPoints[unitName] = result.laser
AFAC.Data.irPoints[unitName] = result.ir
AFAC.log("Started lasing target for " .. unitName)
else
AFAC.log("Failed to create laser designation for " .. unitName)
end
end
-- Update laser position
function AFAC.updateLasing(unitName, target)
local laserSpot = AFAC.Data.laserPoints[unitName]
local irSpot = AFAC.Data.irPoints[unitName]
if not laserSpot or not irSpot then return end
local targetPoint = target:getPoint()
local targetVector = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
laserSpot:setPoint(targetVector)
irSpot:setPoint(targetVector)
end
-- Cancel laser designation
function AFAC.cancelLasing(unitName)
local laserSpot = AFAC.Data.laserPoints[unitName]
local irSpot = AFAC.Data.irPoints[unitName]
if laserSpot then
Spot.destroy(laserSpot)
AFAC.Data.laserPoints[unitName] = nil
end
if irSpot then
Spot.destroy(irSpot)
AFAC.Data.irPoints[unitName] = nil
end
end
-- =====================================================
-- VISUAL MARKING SYSTEM
-- =====================================================
-- Create smoke or flare marker
function AFAC.createVisualMarker(target, markerType, color)
local targetPoint = target:getPoint()
local markerPoint = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
if markerType == "SMOKE" then
trigger.action.smoke(markerPoint, color)
else -- FLARE
trigger.action.signalFlare(markerPoint, color, 0)
end
end
-- Create F10 map marker with target details
function AFAC.createMapMarker(target, spotter)
local targetPoint = target:getPoint()
local coalition = AFAC.Data.pilots[spotter].coalition
-- Get target details
local velocity = target:getVelocity()
local speed = 0
if velocity then
speed = math.sqrt(velocity.x^2 + velocity.z^2) * 2.237 -- Convert to mph
end
local heading = AFAC.getHeading(target) * 180 / math.pi
local coords = AFAC.coordToString(targetPoint)
local mgrs = AFAC.getMGRS(targetPoint)
local altitude = math.floor(targetPoint.y)
local markerText = string.format("%s\n%s\n%s\nAlt: %dm/%dft\nHdg: %03d°\nSpd: %.0f mph\nSpotter: %s",
target:getTypeName(),
coords,
mgrs,
altitude,
altitude * 3.28084,
heading,
speed,
spotter
)
local markerId = AFAC.Data.nextMarkerId
AFAC.Data.nextMarkerId = AFAC.Data.nextMarkerId + 1
trigger.action.markToCoalition(markerId, markerText, targetPoint, coalition, false, "AFAC Target")
-- Schedule marker removal
timer.scheduleFunction(
function(args)
trigger.action.removeMark(args[1])
end,
{markerId},
timer.getTime() + AFAC.Config.mapMarkerDuration
)
return markerId
end
-- =====================================================
-- AUTOMATIC LASING SYSTEM
-- =====================================================
-- Start automatic target lasing
function AFAC.startAutoLasing(unitName)
timer.scheduleFunction(AFAC.autoLaseUpdate, {unitName}, timer.getTime() + 1)
end
-- Auto-lasing update function
function AFAC.autoLaseUpdate(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
-- Check if pilot still exists and is on station
if not pilot or not AFAC.Data.onStation[unitName] then
return -- Don't reschedule
end
local afacUnit = pilot.unit
if not afacUnit or not afacUnit:isActive() or afacUnit:getLife() <= 0 then
AFAC.removePilot(unitName)
return
end
local currentTarget = AFAC.Data.targets[unitName]
local laserCode = AFAC.Data.laserCodes[unitName]
local markerSettings = AFAC.Data.markerSettings[unitName]
-- Check if current target is still valid
if currentTarget and (not currentTarget:isActive() or currentTarget:getLife() <= 1) then
-- Target destroyed
local message = string.format("[%s] Target %s destroyed. Good job! Scanning for new targets.",
unitName, currentTarget:getTypeName())
AFAC.notifyCoalition(message, 10, pilot.coalition)
AFAC.Data.targets[unitName] = nil
AFAC.cancelLasing(unitName)
currentTarget = nil
end
-- Find new target if needed
if not currentTarget then
local newTarget = AFAC.findNearestTarget(afacUnit)
if newTarget then
AFAC.Data.targets[unitName] = newTarget
-- Start lasing
AFAC.startLasing(afacUnit, newTarget, laserCode)
-- Create visual markers
AFAC.createVisualMarker(newTarget, markerSettings.type, markerSettings.color)
AFAC.createMapMarker(newTarget, unitName)
-- Notify coalition
local message = string.format("[%s] Lasing new target: %s, CODE: %s",
unitName, newTarget:getTypeName(), laserCode)
AFAC.notifyCoalition(message, 10, pilot.coalition)
currentTarget = newTarget
end
end
-- Update laser position if we have a target
if currentTarget then
AFAC.updateLasing(unitName, currentTarget)
-- Update smoke markers periodically
local nextSmokeTime = AFAC.Data.smokeMarks[unitName]
if not nextSmokeTime or nextSmokeTime < timer.getTime() then
AFAC.createVisualMarker(currentTarget, markerSettings.type, markerSettings.color)
AFAC.Data.smokeMarks[unitName] = timer.getTime() + AFAC.Config.smokeInterval
end
end
-- Schedule next update
timer.scheduleFunction(AFAC.autoLaseUpdate, args, timer.getTime() + AFAC.Config.autoUpdateInterval)
end
-- =====================================================
-- F10 MENU SYSTEM
-- =====================================================
-- Add F10 menus for AFAC pilot
function AFAC.addMenus(unitName)
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local groupId = pilot.groupId
-- Main AFAC menu
local mainMenu = missionCommands.addSubMenuForGroup(groupId, "AFAC Control")
-- Targeting mode menu
local targetMenu = missionCommands.addSubMenuForGroup(groupId, "Targeting Mode", mainMenu)
missionCommands.addCommandForGroup(groupId, "Auto Mode ON", targetMenu, AFAC.setAutoMode, {unitName, true})
missionCommands.addCommandForGroup(groupId, "Auto Mode OFF", targetMenu, AFAC.setAutoMode, {unitName, false})
-- Manual targeting
local manualMenu = missionCommands.addSubMenuForGroup(groupId, "Manual Targeting", targetMenu)
missionCommands.addCommandForGroup(groupId, "Scan for Targets", manualMenu, AFAC.manualScan, {unitName})
-- Target selection (will be populated after scan)
local selectMenu = missionCommands.addSubMenuForGroup(groupId, "Select Target", manualMenu)
for i = 1, 10 do
missionCommands.addCommandForGroup(groupId, "Target " .. i, selectMenu, AFAC.selectManualTarget, {unitName, i})
end
-- Laser code menu
local laserMenu = missionCommands.addSubMenuForGroup(groupId, "Laser Codes", mainMenu)
for _, code in ipairs(AFAC.Config.laserCodes) do
missionCommands.addCommandForGroup(groupId, "Code: " .. code, laserMenu, AFAC.setLaserCode, {unitName, code})
end
-- Marker settings
local markerMenu = missionCommands.addSubMenuForGroup(groupId, "Marker Settings", mainMenu)
-- Smoke colors
local smokeMenu = missionCommands.addSubMenuForGroup(groupId, "Smoke Color", markerMenu)
for colorName, colorValue in pairs(AFAC.Config.smokeColors) do
missionCommands.addCommandForGroup(groupId, colorName, smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", colorValue})
end
-- Flare colors (limited selection)
local flareMenu = missionCommands.addSubMenuForGroup(groupId, "Flare Color", markerMenu)
missionCommands.addCommandForGroup(groupId, "GREEN", flareMenu, AFAC.setMarkerColor, {unitName, "FLARE", 0})
missionCommands.addCommandForGroup(groupId, "WHITE", flareMenu, AFAC.setMarkerColor, {unitName, "FLARE", 2})
missionCommands.addCommandForGroup(groupId, "ORANGE", flareMenu, AFAC.setMarkerColor, {unitName, "FLARE", 3})
-- Status
missionCommands.addCommandForGroup(groupId, "AFAC Status", mainMenu, AFAC.showStatus, {unitName})
end
-- =====================================================
-- F10 MENU FUNCTIONS
-- =====================================================
-- Set auto/manual mode
function AFAC.setAutoMode(args)
local unitName = args[1]
local autoMode = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.onStation[unitName] = autoMode
if autoMode then
trigger.action.outTextForGroup(pilot.groupId, "Auto targeting mode enabled", 10)
AFAC.startAutoLasing(unitName)
else
trigger.action.outTextForGroup(pilot.groupId, "Auto targeting mode disabled", 10)
AFAC.cancelLasing(unitName)
AFAC.Data.targets[unitName] = nil
end
end
-- Manual target scan
function AFAC.manualScan(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local targets = AFAC.scanForTargets(pilot.unit, 10)
AFAC.Data.manualTargets[unitName] = targets
-- Report found targets
if #targets > 0 then
local afacPoint = pilot.unit:getPoint()
trigger.action.outTextForGroup(pilot.groupId, "Targets found:", 5)
for i, target in ipairs(targets) do
local targetPoint = target:getPoint()
local distance = AFAC.getDistance(afacPoint, targetPoint)
local bearing = math.atan2(targetPoint.z - afacPoint.z, targetPoint.x - afacPoint.x) * 180 / math.pi
if bearing < 0 then bearing = bearing + 360 end
local message = string.format("Target %d: %s, Bearing %03d°, Range %.1fkm",
i, target:getTypeName(), bearing, distance / 1000)
trigger.action.outTextForGroup(pilot.groupId, message, 15)
-- Create map marker
AFAC.createMapMarker(target, unitName)
end
else
trigger.action.outTextForGroup(pilot.groupId, "No targets found in range", 10)
end
end
-- Select manual target
function AFAC.selectManualTarget(args)
local unitName = args[1]
local targetIndex = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local targets = AFAC.Data.manualTargets[unitName]
if not targets or not targets[targetIndex] then
trigger.action.outTextForGroup(pilot.groupId, "Invalid target selection", 10)
return
end
local target = targets[targetIndex]
if not target:isActive() or target:getLife() <= 1 then
trigger.action.outTextForGroup(pilot.groupId, "Selected target is no longer active", 10)
return
end
-- Set as current target
AFAC.Data.targets[unitName] = target
-- Start lasing
local laserCode = AFAC.Data.laserCodes[unitName]
AFAC.startLasing(pilot.unit, target, laserCode)
-- Create markers
local markerSettings = AFAC.Data.markerSettings[unitName]
AFAC.createVisualMarker(target, markerSettings.type, markerSettings.color)
-- Notify
local message = string.format("Designating Target %d: %s, CODE: %s",
targetIndex, target:getTypeName(), laserCode)
trigger.action.outTextForGroup(pilot.groupId, message, 10)
AFAC.notifyCoalition("[" .. unitName .. "] " .. message, 10, pilot.coalition)
end
-- Set laser code
function AFAC.setLaserCode(args)
local unitName = args[1]
local laserCode = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.laserCodes[unitName] = laserCode
trigger.action.outTextForGroup(pilot.groupId, "Laser code set to: " .. laserCode, 10)
-- Update current lasing if active
local currentTarget = AFAC.Data.targets[unitName]
if currentTarget then
AFAC.startLasing(pilot.unit, currentTarget, laserCode)
end
end
-- Set marker color/type
function AFAC.setMarkerColor(args)
local unitName = args[1]
local markerType = args[2]
local color = args[3]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.markerSettings[unitName] = {
type = markerType,
color = color
}
local colorNames = {"GREEN", "RED", "WHITE", "ORANGE", "BLUE"}
trigger.action.outTextForGroup(pilot.groupId,
string.format("Marker set to %s %s", colorNames[color + 1] or "UNKNOWN", markerType), 10)
end
-- Show AFAC status
function AFAC.showStatus(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local status = "AFAC STATUS:\n\n"
for pilotName, pilotData in pairs(AFAC.Data.pilots) do
if pilotData.coalition == pilot.coalition thenn then
local target = AFAC.Data.targets[pilotName]
local laserCode = AFAC.Data.laserCodes[pilotName]
local onStation = AFAC.Data.onStation[pilotName]
if target and target:isActive() then
status = status .. string.format("%s: Targeting %s, CODE: %s\n",
pilotName, target:getTypeName(), laserCode)
elseif onStation then
status = status .. string.format("%s: On station, searching for targets\n", pilotName)
else
status = status .. string.format("%s: Off station\n", pilotName)
end
end
end
trigger.action.outTextForGroup(pilot.groupId, status, 30)
end
-- =====================================================
-- EVENT HANDLERS
-- =====================================================
-- Event handler for unit spawning and player connections
AFAC.EventHandler = {}
function AFAC.EventHandler:onEvent(event)
-- Handle new unit spawning
if event.id == world.event.S_EVENT_BIRTH then
AFAC.log("S_EVENT_BIRTH triggered")
local unit = event.initiator
if unit and Object.getCategory(unit) == Object.Category.UNIT then
local objDesc = unit:getDesc()
AFAC.log("Birth event - Unit category: " .. tostring(objDesc.category))
if objDesc.category == Unit.Category.AIRPLANE or objDesc.category == Unit.Category.HELICOPTER then
AFAC.log("Birth event - Aircraft detected: " .. unit:getTypeName())
if AFAC.isAFAC(unit) then
AFAC.log("Birth event - AFAC aircraft confirmed, scheduling add")
-- Delay slightly to ensure unit is fully initialized
timer.scheduleFunction(
function(args)
local u = args[1]
if u and u:isActive() then
AFAC.log("Adding pilot from birth event: " .. u:getName())
AFAC.addPilot(u)
AFAC.addMenus(u:getName())
end
end,
{unit},
timer.getTime() + 2
)
end
end
end
end
-- Debug: Log ALL player enter events to see what's happening
if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then
AFAC.log("PLAYER ENTERED UNIT - Type: " .. (event.initiator and event.initiator:getTypeName() or "UNKNOWN"))
end
-- Handle player entering existing unit (like joining a running UH-1H)
if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then
AFAC.log("S_EVENT_PLAYER_ENTER_UNIT triggered")
local unit = event.initiator
if unit and Object.getCategory(unit) == Object.Category.UNIT then
local objDesc = unit:getDesc()
AFAC.log("Player enter - Unit category: " .. tostring(objDesc.category))
if objDesc.category == Unit.Category.AIRPLANE or objDesc.category == Unit.Category.HELICOPTER then
AFAC.log("Player enter - Aircraft detected: " .. unit:getTypeName())
if AFAC.isAFAC(unit) then
local unitName = unit:getName()
AFAC.log("Player enter - AFAC aircraft confirmed: " .. unitName)
-- Check if already registered
if not AFAC.Data.pilots[unitName] then
AFAC.log("Player enter - Not registered, adding pilot")
-- Add pilot and menus for existing aircraft
timer.scheduleFunction(
function(args)
local u = args[1]
if u and u:isActive() then
AFAC.log("Adding pilot from player enter event: " .. u:getName())
AFAC.addPilot(u)
AFAC.addMenus(u:getName())
end
end,
{unit},
timer.getTime() + 1
)
else
AFAC.log("Player enter - Already registered")
end
end
end
end
end
end
-- =====================================================
-- INITIALIZATION
-- =====================================================
-- Add event handler
world.addEventHandler(AFAC.EventHandler)
-- Continuous check for new AFAC pilots (catches players joining existing aircraft)
function AFAC.checkForNewPilots()
for coalitionId = 1, 2 do
local airGroups = coalition.getGroups(coalitionId, Group.Category.AIRPLANE)
local heliGroups = coalition.getGroups(coalitionId, Group.Category.HELICOPTER)
local allGroups = {}
for _, group in ipairs(airGroups) do table.insert(allGroups, group) end
for _, group in ipairs(heliGroups) do table.insert(allGroups, group) end
for _, group in ipairs(allGroups) do
local units = group:getUnits()
if units then
for _, unit in ipairs(units) do
if unit and unit:isActive() and AFAC.isAFAC(unit) then
local unitName = unit:getName()
-- Check if this is a player-controlled AFAC that we haven't registered yet
if unit:getPlayerName() and not AFAC.Data.pilots[unitName] then
AFAC.log("Found new player AFAC: " .. unitName .. " (Player: " .. unit:getPlayerName() .. ")")
AFAC.addPilot(unit)
AFAC.addMenus(unitName)
end
end
end
end
end
end
-- Reschedule check every 10 seconds
timer.scheduleFunction(AFAC.checkForNewPilots, nil, timer.getTime() + 10)
end
-- Initialize existing units (for mission restart)
timer.scheduleFunction(
function()
for coalitionId = 1, 2 do
local airGroups = coalition.getGroups(coalitionId, Group.Category.AIRPLANE)
local heliGroups = coalition.getGroups(coalitionId, Group.Category.HELICOPTER)
local allGroups = {}
for _, group in ipairs(airGroups) do table.insert(allGroups, group) end
for _, group in ipairs(heliGroups) do table.insert(allGroups, group) end
for _, group in ipairs(allGroups) do
local units = group:getUnits()
if units then
for _, unit in ipairs(units) do
if unit and unit:isActive() and AFAC.isAFAC(unit) then
AFAC.addPilot(unit)
AFAC.addMenus(unit:getName())
end
end
end
end
end
AFAC.log("AFAC System initialized")
-- Start continuous pilot checking
AFAC.checkForNewPilots()
end,
nil,
timer.getTime() + 5
)
-- Success message
trigger.action.outText("Simple AFAC System v1.0 loaded successfully!", 10)
AFAC.log("AFAC Script fully loaded and initialized")
-- Heartbeat disabled in production to avoid periodic UI/network churn
-- To re-enable for debugging, uncomment lines below:
-- function AFAC.heartbeat()
-- AFAC.log("AFAC System heartbeat - script is running")
-- timer.scheduleFunction(AFAC.heartbeat, nil, timer.getTime() + 30)
-- end
-- timer.scheduleFunction(AFAC.heartbeat, nil, timer.getTime() + 30)

View File

@ -1,349 +0,0 @@
# Mission Setup Checklist - F10 Menu System
## Pre-Setup
- [ ] Back up your current mission file (.miz)
- [ ] Have all script files ready
- [ ] Have DCS Mission Editor open
---
## Step 1: File Preparation (5 minutes)
### Required Files
- [ ] `Moose.lua` (MOOSE Framework)
- [ ] `Moose_MenuManager.lua` (NEW - Menu System)
- [ ] `CTLD.lua`
- [ ] `Moose_FAC2MarkRecceZone.lua`
### Optional Files (Your Scripts)
- [ ] `Moose_Intel.lua`
- [ ] `Moose_CaptureZones.lua`
- [ ] `Moose_NavalGroup.lua`
- [ ] `Moose_TADC_Load2nd.lua`
- [ ] Other custom scripts...
### Copy Files
- [ ] Extract mission .miz file to folder (or use editor)
- [ ] Place all .lua files in mission folder
- [ ] Note: Mission Editor can also load scripts directly
---
## Step 2: Mission Editor Setup (5 minutes)
### Open Mission
- [ ] Open mission in DCS Mission Editor
- [ ] Go to Triggers tab
### Create Load Trigger
- [ ] Create new trigger: "Load Mission Scripts"
- [ ] Set Type: **ONCE**
- [ ] Set Event: **MISSION START**
### Add Condition
- [ ] Add condition: **TIME MORE**
- [ ] Set time: **1 second**
- [ ] (Ensures mission is initialized)
### Add Script Actions (IN THIS ORDER!)
- [ ] Action 1: DO SCRIPT FILE → `Moose.lua`
- [ ] Action 2: DO SCRIPT FILE → `Moose_MenuManager.lua` ⚠️ CRITICAL ORDER!
- [ ] Action 3: DO SCRIPT FILE → `CTLD.lua`
- [ ] Action 4: DO SCRIPT FILE → `Moose_FAC2MarkRecceZone.lua`
- [ ] Action 5: DO SCRIPT FILE → `Moose_Intel.lua`
- [ ] Action 6: DO SCRIPT FILE → `Moose_CaptureZones.lua`
- [ ] Action 7: DO SCRIPT FILE → `Moose_NavalGroup.lua`
- [ ] Action 8: DO SCRIPT FILE → `Moose_TADC_Load2nd.lua`
- [ ] Action 9+: (Any other scripts...)
### Save Mission
- [ ] Save mission
- [ ] Note the file path for testing
---
## Step 3: Testing (5 minutes)
### Basic Test
- [ ] Start mission in DCS
- [ ] Spawn as any pilot (Blue coalition recommended)
- [ ] Press **F10**
### Verify Menu Structure
- [ ] F1: "Mission Options" exists
- [ ] F1 → Contains: INTEL HQ, Zone Control, CVN Command, TADC Utilities
- [ ] F2: "CTLD" exists
- [ ] F2 → Contains: Check Cargo, Troop Transport, etc.
- [ ] F3: "AFAC Control" exists
- [ ] F3 → Contains: Targeting Mode, Laser Codes, etc.
### If Wrong Order
- [ ] Exit mission
- [ ] Check trigger action order in editor
- [ ] Verify MenuManager is action #2
- [ ] Verify CTLD is action #3
- [ ] Verify FAC is action #4
- [ ] Save and retest
---
## Step 4: Configuration (Optional)
### Global Settings
Open `Moose_MenuManager.lua`:
- [ ] Review `EnableMissionOptionsMenu` (true/false)
- [ ] Review `MissionOptionsMenuName` (change if desired)
- [ ] Review `Debug` (enable for troubleshooting)
### Individual Script Settings
For each script (Intel, Zones, CVN, TADC):
- [ ] Check `EnableF10Menu` variable (top of file)
- [ ] Set to `false` to hide that script's menu
- [ ] Useful for training missions or specific scenarios
---
## Step 5: Advanced Testing (Optional)
### Test Both Coalitions
- [ ] Spawn as Blue pilot → Verify Blue menus
- [ ] Spawn as Red pilot → Verify Red menus
- [ ] Each coalition should see their own "Mission Options"
### Test Multiple Players
- [ ] Host multiplayer server (or local)
- [ ] Have multiple clients join
- [ ] Each client sees consistent menus
- [ ] CTLD/FAC menus are per-group (expected)
### Test Debug Mode
- [ ] Enable debug in `Moose_MenuManager.lua`
- [ ] Start mission
- [ ] Check `dcs.log` file
- [ ] Location: `C:\Users\[You]\Saved Games\DCS\Logs\dcs.log`
- [ ] Look for "MenuManager:" messages
- [ ] Verify menus created successfully
---
## Step 6: Troubleshooting
### Problem: No "Mission Options" Menu
Cause: MenuManager not loaded or disabled
- [ ] Verify `Moose_MenuManager.lua` is in mission folder
- [ ] Verify it's action #2 in trigger (after Moose.lua)
- [ ] Check `EnableMissionOptionsMenu = true` in config
- [ ] Enable debug mode and check logs
### Problem: CTLD Not at F2
Cause: Load order incorrect
- [ ] Verify CTLD.lua is action #3 (after MenuManager)
- [ ] Check no other script loads between MenuManager and CTLD
- [ ] Reorder trigger actions
- [ ] Save and retest
### Problem: FAC Not at F3
Cause: Load order incorrect
- [ ] Verify FAC.lua is action #4 (after CTLD)
- [ ] Check no other script loads between CTLD and FAC
- [ ] Reorder trigger actions
- [ ] Save and retest
### Problem: Script Errors on Load
Cause: Syntax error or missing dependency
- [ ] Check `dcs.log` for error messages
- [ ] Verify all files are present
- [ ] Verify Moose.lua loads first
- [ ] Enable debug mode for detailed logging
- [ ] Check file paths in trigger actions
### Problem: Menus Appear at Root Level
Cause: Script doesn't use MenuManager
- [ ] Verify script has MenuManager integration code
- [ ] Check pattern: `if MenuManager then ... else ... end`
- [ ] Review MENUMANAGER_TEMPLATE.lua for correct pattern
- [ ] Update script accordingly
---
## Step 7: Documentation
### For Mission Makers
- [ ] Read `F10_MENU_SYSTEM_GUIDE.md` (comprehensive)
- [ ] Bookmark `F10_MENU_QUICK_REF.md` (quick reference)
- [ ] Save `EXAMPLE_MISSION_SETUP.lua` for future missions
### For Players
- [ ] Create mission briefing mentioning menu structure
- [ ] Example: "CTLD is at F10→F2, FAC is at F10→F3"
- [ ] Note any disabled menus (if applicable)
### For Server Admins
- [ ] Document any configuration changes
- [ ] Note which scripts/menus are active
- [ ] Keep backup of working configuration
---
## Step 8: Deployment
### Pre-Deployment
- [ ] Final test of all menus
- [ ] Verify no script errors
- [ ] Test with multiple players (if multiplayer)
- [ ] Backup final working version
### Deployment
- [ ] Upload mission to server (if multiplayer)
- [ ] Update mission briefing/description
- [ ] Notify players of menu structure
- [ ] Monitor first mission for issues
### Post-Deployment
- [ ] Collect player feedback
- [ ] Monitor for errors
- [ ] Adjust configuration if needed
- [ ] Document any issues for future missions
---
## Quick Reference Card (Print This!)
```
┌─────────────────────────────────────────┐
│ F10 MENU SYSTEM - LOAD ORDER │
├─────────────────────────────────────────┤
│ 1. Moose.lua │
│ 2. Moose_MenuManager.lua ← FIRST! │
│ 3. CTLD.lua ← F2 │
│ 4. Moose_FAC2MarkRecceZone.lua ← F3 │
│ 5. Other scripts... ← Under F1 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ RESULT IN GAME │
├─────────────────────────────────────────┤
│ F10 → F1: Mission Options │
│ F2: CTLD │
│ F3: AFAC Control │
└─────────────────────────────────────────┘
```
---
## Common Mistakes
### ❌ Mistake 1: Loading Scripts Before MenuManager
```
❌ Wrong:
1. Moose.lua
2. Moose_Intel.lua
3. Moose_MenuManager.lua ← Too late!
✅ Correct:
1. Moose.lua
2. Moose_MenuManager.lua ← First!
3. Moose_Intel.lua
```
### ❌ Mistake 2: Loading Other Scripts Between MenuManager and CTLD
```
❌ Wrong:
1. Moose.lua
2. Moose_MenuManager.lua
3. Moose_Intel.lua ← Pushes CTLD down!
4. CTLD.lua ← Not F2 anymore!
✅ Correct:
1. Moose.lua
2. Moose_MenuManager.lua
3. CTLD.lua ← F2!
4. Moose_Intel.lua
```
### ❌ Mistake 3: Not Using MenuManager in Script
```lua
❌ Wrong (creates root menu):
local MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
✅ Correct (uses MenuManager):
local MyMenu
if MenuManager then
MyMenu = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "My Script")
else
MyMenu = MENU_COALITION:New(coalition.side.BLUE, "My Script")
end
```
---
## Success Checklist
After setup, you should have:
- [ ] ✅ Mission loads without errors
- [ ] ✅ F1 shows "Mission Options" with submenus
- [ ] ✅ F2 shows "CTLD" (always)
- [ ] ✅ F3 shows "AFAC Control" (always)
- [ ] ✅ All menu commands work
- [ ] ✅ Both coalitions see correct menus
- [ ] ✅ Players can find CTLD/FAC quickly
- [ ] ✅ No duplicate or orphaned menus
- [ ] ✅ dcs.log shows no errors
- [ ] ✅ Professional, organized appearance
**All checked?** You're ready to go! 🎉
---
## Time Estimates
| Task | Time | Difficulty |
|------|------|-----------|
| Copy files | 2 min | Easy |
| Set up triggers | 5 min | Easy |
| Basic testing | 5 min | Easy |
| Configuration | 5 min | Medium |
| Troubleshooting | 0-15 min | Varies |
| **Total** | **15-30 min** | **Easy** |
---
## Help & Resources
**Getting Started:**
- This checklist (you are here)
- `MENUMANAGER_README.md`
**Understanding:**
- `MENUMANAGER_SUMMARY.md`
- `MENUMANAGER_VISUAL_GUIDE.md`
**Reference:**
- `F10_MENU_QUICK_REF.md`
- `F10_MENU_SYSTEM_GUIDE.md`
**Development:**
- `MENUMANAGER_TEMPLATE.lua`
- `EXAMPLE_MISSION_SETUP.lua`
---
## Final Notes
**Backup** your mission before making changes
**Test** thoroughly before deploying
**Document** your configuration
**Monitor** first live mission
**Iterate** based on feedback
**Remember**: The key is load order!
1. MenuManager first
2. CTLD second (F2)
3. FAC third (F3)
4. Everything else
**Good luck and happy mission making!** 🚁✈️
---
*Last updated: November 9, 2025*

File diff suppressed because it is too large Load Diff

View File

@ -1,965 +0,0 @@
# Universal TADC System - Mission Maker's Guide
## Tactical Air Defense Controller with Automated Logistics
---
## 📋 Table of Contents
1. [What is TADC?](#what-is-tadc)
2. [System Overview](#system-overview)
3. [Quick Start Guide](#quick-start-guide)
4. [Detailed Configuration](#detailed-configuration)
5. [Zone-Based Defense Setup](#zone-based-defense-setup)
6. [Cargo Replenishment System](#cargo-replenishment-system)
7. [Testing & Troubleshooting](#testing--troubleshooting)
8. [Advanced Features](#advanced-features)
9. [Common Scenarios](#common-scenarios)
---
## What is TADC?
**TADC (Tactical Air Defense Controller)** is an automated air defense system for DCS missions that creates realistic, dynamic fighter aircraft responses to airborne threats. Think of it as an AI commander that:
- **Detects enemy aircraft** automatically
- **Launches fighters** to intercept threats
- **Manages squadron resources** (aircraft availability, cooldowns)
- **Replenishes squadrons** through cargo aircraft deliveries
- **Operates independently** for both RED and BLUE coalitions
### Why Use TADC?
**Realistic Air Defense** - Squadrons respond intelligently to threats
**Dynamic Gameplay** - Air battles happen organically without manual triggers
**Balanced Competition** - Both sides operate with equal capabilities
**Sustainable Operations** - Cargo system allows long missions with resupply
**Easy Configuration** - Simple tables instead of complex scripting
---
## System Overview
The TADC system consists of **three main scripts** that work together:
### 1. Squadron Configuration (`Moose_TADC_SquadronConfigs_Load1st.lua`)
**Purpose:** Define all fighter squadrons for RED and BLUE coalitions
**Contains:** Aircraft templates, airbases, patrol parameters, zone assignments
**Load Order:** **FIRST** (must load before main TADC script)
### 2. Main TADC System (`Moose_TADC_Load2nd.lua`)
**Purpose:** Core threat detection and interceptor management
**Contains:** Threat scanning, squadron selection, intercept logic, F10 menus
**Load Order:** **SECOND** (after squadron config)
### 3. Cargo Dispatcher (`Moose_TADC_CargoDispatcher.lua`)
**Purpose:** Automated squadron resupply through cargo aircraft
**Contains:** Squadron monitoring, cargo spawning, delivery tracking
**Load Order:** **THIRD** (optional, only if using resupply system)
---
## Quick Start Guide
### Prerequisites
Before setting up TADC, you need:
- ✅ **MOOSE Framework** loaded in your mission (download from [MOOSE GitHub](https://github.com/FlightControl-Master/MOOSE))
- ✅ **Fighter aircraft templates** created in DCS mission editor (as GROUPS, not units)
- ✅ **Airbases** under correct coalition control
- ✅ (Optional) **Cargo aircraft templates** for resupply missions
### 5-Minute Setup
#### Step 1: Create Fighter Templates
1. Open your mission in DCS Mission Editor
2. Place fighter aircraft as **LATE ACTIVATION GROUPS** (not individual units)
3. Name them clearly (example: `RED_CAP_Kilpyavr_MiG29`)
4. Position them at or near the airbases they'll operate from
5. Set them to the correct coalition (RED or BLUE)
**Important:** Use GROUP templates, not UNIT templates!
#### Step 2: Load MOOSE Framework
1. In mission editor, go to **Triggers**
2. Create a new trigger: **MISSION START**
3. Add action: **DO SCRIPT FILE**
4. Select your MOOSE.lua file
5. This must be the FIRST script loaded
#### Step 3: Load Squadron Configuration
1. Create another **DO SCRIPT FILE** action (after MOOSE)
2. Select `Moose_TADC_SquadronConfigs_Load1st.lua`
3. Edit the file to configure your squadrons (see below)
#### Step 4: Load Main TADC System
1. Create another **DO SCRIPT FILE** action
2. Select `Moose_TADC_Load2nd.lua`
3. (Optional) Adjust settings in the file if needed
#### Step 5: (Optional) Load Cargo Dispatcher
1. If using resupply system, create another **DO SCRIPT FILE** action
2. Select `Moose_TADC_CargoDispatcher.lua`
**Load Order in Mission Editor:**
```
1. MOOSE.lua
2. Moose_TADC_SquadronConfigs_Load1st.lua
3. Moose_TADC_Load2nd.lua
4. Moose_TADC_CargoDispatcher.lua (optional)
```
---
## Detailed Configuration
### Squadron Configuration Explained
Open `Moose_TADC_SquadronConfigs_Load1st.lua` and find the squadron configuration sections.
#### Basic Squadron Example
```lua
{
templateName = "RED_CAP_Kilpyavr_MiG29", -- Must match mission editor template name
displayName = "Kilpyavr CAP MiG-29A", -- Human-readable name for logs/messages
airbaseName = "Kilpyavr", -- Exact airbase name from DCS
aircraft = 12, -- Maximum aircraft in squadron
skill = AI.Skill.EXCELLENT, -- AI pilot skill level
altitude = 20000, -- Patrol altitude (feet)
speed = 350, -- Patrol speed (knots)
patrolTime = 30, -- Time on station (minutes)
type = "FIGHTER" -- Aircraft role
}
```
#### Parameter Guide
| Parameter | Description | Example Values |
|-----------|-------------|----------------|
| **templateName** | Group name from mission editor (EXACT match) | `"RED_CAP_Base_F15"` |
| **displayName** | Friendly name shown in messages | `"Kilpyavr CAP Squadron"` |
| **airbaseName** | DCS airbase name (case sensitive) | `"Kilpyavr"`, `"Nellis AFB"` |
| **aircraft** | Max squadron size | `8`, `12`, `16` |
| **skill** | AI difficulty | `AI.Skill.AVERAGE`, `GOOD`, `HIGH`, `EXCELLENT`, `ACE` |
| **altitude** | CAP patrol altitude | `15000` (feet) |
| **speed** | CAP patrol speed | `300` (knots) |
| **patrolTime** | Minutes on station before RTB | `20`, `30`, `40` |
| **type** | Aircraft role | `"FIGHTER"` |
### Finding Airbase Names
**Method 1: Mission Editor**
1. Open mission editor
2. Click on any airbase
3. The exact name appears in the properties panel
4. Copy this name EXACTLY (case sensitive!)
**Method 2: Common Airbases**
**Kola Peninsula (Example Map):**
- RED: `"Kilpyavr"`, `"Severomorsk-1"`, `"Severomorsk-3"`, `"Murmansk International"`
- BLUE: `"Luostari Pechenga"`, `"Ivalo"`, `"Alakurtti"`
**Nevada:**
- `"Nellis AFB"`, `"McCarran International"`, `"Creech AFB"`, `"Tonopah Test Range"`
**Caucasus:**
- `"Batumi"`, `"Gudauta"`, `"Senaki-Kolkhi"`, `"Kobuleti"`, `"Kutaisi"`
### Adding Multiple Squadrons
You can add as many squadrons as you want. Just copy the squadron block and modify the values:
```lua
RED_SQUADRON_CONFIG = {
-- First Squadron
{
templateName = "RED_CAP_Base1_MiG29",
displayName = "Base 1 CAP",
airbaseName = "Kilpyavr",
aircraft = 12,
skill = AI.Skill.EXCELLENT,
altitude = 20000,
speed = 350,
patrolTime = 30,
type = "FIGHTER"
},
-- Second Squadron (different base)
{
templateName = "RED_CAP_Base2_SU27",
displayName = "Base 2 CAP",
airbaseName = "Severomorsk-1",
aircraft = 16,
skill = AI.Skill.ACE,
altitude = 25000,
speed = 380,
patrolTime = 25,
type = "FIGHTER"
},
-- Add more squadrons here...
}
```
**Repeat the same process for BLUE squadrons** in the `BLUE_SQUADRON_CONFIG` section.
---
## Zone-Based Defense Setup
Zones allow squadrons to have specific areas of responsibility, creating realistic layered defense.
### Why Use Zones?
- **Border Defense:** Squadrons patrol specific sectors
- **Layered Defense:** Multiple squadrons cover overlapping areas
- **Priority Response:** Squadrons respond differently based on threat location
- **Realistic Behavior:** Fighters don't fly across the entire map for minor threats
### Zone Types
Each squadron can have up to 3 zone types:
1. **Primary Zone** - Main area of responsibility (full response)
2. **Secondary Zone** - Support area (reduced response, 60% by default)
3. **Tertiary Zone** - Emergency fallback (enhanced response when squadron weakened)
### Creating Zones in Mission Editor
**Method: Helicopter Waypoint Method**
1. Place a **helicopter group** (late activation, any type)
2. Name it clearly (example: `"RED BORDER"`)
3. Add waypoints that outline your zone boundary
4. The script will automatically create a polygon zone from these waypoints
5. Repeat for each zone you want to create
**Example Zone Setup:**
```
Mission Editor:
- Helicopter Group: "RED BORDER" with waypoints forming a polygon
- Helicopter Group: "BLUE BORDER" with waypoints forming a polygon
- Helicopter Group: "CONTESTED ZONE" with waypoints forming a polygon
```
### Configuring Zone Response
Add zone configuration to your squadron:
```lua
{
templateName = "RED_CAP_Kilpyavr_MiG29",
displayName = "Kilpyavr CAP",
airbaseName = "Kilpyavr",
aircraft = 12,
skill = AI.Skill.EXCELLENT,
altitude = 20000,
speed = 350,
patrolTime = 30,
type = "FIGHTER",
-- Zone Configuration
primaryZone = "RED BORDER", -- Main responsibility area
secondaryZone = "CONTESTED ZONE", -- Backup coverage
tertiaryZone = nil, -- No tertiary zone
-- Optional: Customize zone behavior
zoneConfig = {
primaryResponse = 1.0, -- Full response in primary zone
secondaryResponse = 0.6, -- 60% response in secondary
tertiaryResponse = 1.4, -- 140% response in tertiary
enableFallback = false, -- Don't auto-switch to tertiary
fallbackThreshold = 0.3, -- Switch when <30% aircraft remain
secondaryLowPriorityFilter = true, -- Ignore small threats in secondary
secondaryLowPriorityThreshold = 2 -- "Small threat" = 2 or fewer aircraft
}
}
```
### Zone Behavior Examples
**Example 1: Border Defense Squadron**
```lua
primaryZone = "RED BORDER", -- Patrols the border
secondaryZone = "INTERIOR", -- Helps with interior threats if needed
tertiaryZone = nil -- No fallback
```
**Example 2: Base Defense with Fallback**
```lua
primaryZone = "NORTHERN SECTOR", -- Main patrol area
secondaryZone = nil, -- No secondary
tertiaryZone = "BASE PERIMETER", -- Falls back to defend base when weakened
enableFallback = true, -- Auto-switch to tertiary when low
fallbackThreshold = 0.4 -- Switch at 40% strength
```
**Example 3: Layered Defense**
```lua
-- Squadron A: Outer layer
primaryZone = "OUTER PERIMETER"
-- Squadron B: Middle layer
primaryZone = "MIDDLE PERIMETER"
-- Squadron C: Inner/base defense
primaryZone = "BASE DEFENSE"
```
### Global Response (No Zones)
If you **DON'T** want zone restrictions, simply leave all zones as `nil`:
```lua
{
templateName = "RED_CAP_Base_MiG29",
displayName = "Global Response CAP",
airbaseName = "Kilpyavr",
aircraft = 12,
skill = AI.Skill.EXCELLENT,
altitude = 20000,
speed = 350,
patrolTime = 30,
type = "FIGHTER",
-- No zones = responds to threats anywhere on the map
primaryZone = nil,
secondaryZone = nil,
tertiaryZone = nil
}
```
---
## Cargo Replenishment System
The cargo system automatically replenishes squadrons by spawning transport aircraft that fly supplies to airbases.
### How Cargo Works
1. **Monitoring:** Script checks squadron aircraft counts every minute
2. **Detection:** When squadron drops below threshold (90% by default), cargo is dispatched
3. **Spawning:** Transport aircraft spawns at a supply airfield
4. **Delivery:** Flies to destination airbase and lands
5. **Replenishment:** Squadron aircraft count increases upon delivery
6. **Cooldown:** 5-minute cooldown before next delivery to same base
### Cargo Aircraft Detection
The system detects cargo by aircraft name patterns:
- `CARGO`
- `TRANSPORT`
- `C130` or `C-130`
- `AN26` or `AN-26`
**Delivery Methods:**
- **Landing:** Aircraft lands at destination airbase
### Configuring Cargo Templates
Edit `Moose_TADC_CargoDispatcher.lua` and find `CARGO_SUPPLY_CONFIG`:
```lua
local CARGO_SUPPLY_CONFIG = {
red = {
cargoTemplate = "CARGO_RED_AN26_TEMPLATE", -- Template name from mission editor
supplyAirfields = {"Airbase1", "Airbase2"}, -- List of supply bases
replenishAmount = 4, -- Aircraft added per delivery
threshold = 0.90 -- Trigger at 90% capacity
},
blue = {
cargoTemplate = "CARGO_BLUE_C130_TEMPLATE",
supplyAirfields = {"Airbase3", "Airbase4"},
replenishAmount = 4,
threshold = 0.90
}
}
```
### Creating Cargo Templates
1. **In Mission Editor:**
- Place transport aircraft group (C-130, An-26, etc.)
- Name it: `CARGO_RED_AN26_TEMPLATE` or `CARGO_BLUE_C130_TEMPLATE`
- Set **LATE ACTIVATION**
- Position at any friendly airbase (starting position doesn't matter)
2. **In Configuration:**
- Use the EXACT template name in `cargoTemplate` field
- List supply airbases in `supplyAirfields` array
- Set how many aircraft each delivery adds (`replenishAmount`)
### Supply Airfield Strategy
**Choose rear/safe airbases for supplies:**
```lua
red = {
cargoTemplate = "CARGO_RED_AN26_TEMPLATE",
supplyAirfields = {
"Rear_Base_1", -- Far from frontline, safe
"Rear_Base_2", -- Alternate supply source
"Central_Logistics_Hub" -- Main supply depot
},
replenishAmount = 4,
threshold = 0.90
}
```
**Tips:**
- Use 3-5 supply airbases for redundancy
- Choose bases far from combat zones
- Ensure supply bases are well-defended
- Balance geographic coverage
### Disabling Cargo System
If you don't want automated resupply:
1. **Don't load** `Moose_TADC_CargoDispatcher.lua`
2. Squadrons will operate with their initial aircraft count only
3. System still works perfectly for shorter missions
---
## Testing & Troubleshooting
### Validation Tools
The system includes built-in validation. Check the DCS log file after mission start for:
```
[Universal TADC] ═══════════════════════════════════════
[Universal TADC] Configuration Validation Results:
[Universal TADC] ✓ All templates exist
[Universal TADC] ✓ All airbases valid
[Universal TADC] ✓ All zones found
[Universal TADC] Configuration is VALID
[Universal TADC] ═══════════════════════════════════════
```
### In-Game F10 Menu Commands
Press **F10** in-game to access TADC utilities:
**Available to Each Coalition:**
- **Show Squadron Resource Summary** - Current aircraft counts
- **Show Airbase Status Report** - Operational status of all bases
- **Show Active Interceptors** - Currently airborne fighters
- **Show Threat Summary** - Detected enemy aircraft
- **Broadcast Squadron Summary Now** - Force immediate status update
- **Show Cargo Delivery Log** - Recent supply missions
- **Show Zone Coverage Map** - Squadron zone assignments
**Available to All (Mission Commands):**
- **Emergency Cleanup Interceptors** - Remove stuck/dead groups
- **Show TADC System Status** - Uptime and system health
- **Check for Stuck Aircraft** - Manual stuck aircraft check
- **Show Airbase Health Status** - Parking/spawn issues
### Common Issues & Solutions
#### Issue: "Template not found in mission"
**Cause:** Template name in config doesn't match mission editor
**Solution:**
1. Check exact spelling and case
2. Ensure template is in mission editor
3. Verify template is a GROUP (not a unit)
4. Check template name in mission editor properties
#### Issue: "Airbase not found or wrong coalition"
**Cause:** Airbase name wrong or captured by enemy
**Solution:**
1. Check exact airbase spelling (case sensitive)
2. Verify airbase is owned by correct coalition in mission editor
3. Use `_G.TDAC_CheckAirbase("AirbaseName")` in DCS console
#### Issue: "No interceptors launching"
**Check:**
1. Are enemy aircraft detected? (F10 → Show Threat Summary)
2. Are squadrons operational? (F10 → Show Squadron Resource Summary)
3. Is airbase captured/destroyed? (F10 → Show Airbase Status Report)
4. Are squadrons on cooldown? (F10 → Show Squadron Resource Summary)
5. Check intercept ratio settings (might be too low)
#### Issue: "Cargo not delivering"
**Check:**
1. Is cargo template name correct?
2. Are supply airbases valid and friendly?
3. Is destination airbase captured/operational?
4. Check parking availability (F10 → Show Airbase Health Status)
5. Look for "Cargo delivery detected" messages in log
#### Issue: "Aircraft spawning stuck at parking"
**Cause:** Parking spots occupied or insufficient space
**Solution:**
1. Use F10 → Check for Stuck Aircraft
2. Use F10 → Emergency Cleanup Interceptors
3. Check airbase parking capacity (larger aircraft need more space)
4. Reduce squadron sizes if parking is limited
### DCS Console Diagnostics
Open DCS Lua console (**F12** or scripting console) and run:
```lua
-- Check all supply airbase ownership
_G.TDAC_CheckAirbaseOwnership()
-- Check specific airbase
_G.TDAC_CheckAirbase("Kilpyavr")
-- Validate dispatcher configuration
_G.TDAC_RunConfigCheck()
-- Check airbase parking availability
_G.TDAC_LogAirbaseParking("Kilpyavr")
-- Test cargo spawn (debugging)
_G.TDAC_CargoDispatcher_TestSpawn("CARGO_RED_AN26_TEMPLATE", "SupplyBase", "DestinationBase")
```
---
## Advanced Features
### Intercept Ratio System
The `interceptRatio` setting controls how many fighters launch per enemy aircraft.
**In `Moose_TADC_Load2nd.lua`:**
```lua
local TADC_SETTINGS = {
red = {
interceptRatio = 0.8, -- RED launches 0.8 fighters per threat
maxActiveCAP = 8, -- Max 8 groups in air simultaneously
defaultCooldown = 300, -- 5-minute cooldown after engagement
},
blue = {
interceptRatio = 1.2, -- BLUE launches 1.2 fighters per threat
maxActiveCAP = 10, -- Max 10 groups in air simultaneously
defaultCooldown = 300,
}
}
```
**Intercept Ratio Chart:**
| Ratio | 1 Enemy | 4 Enemies | 8 Enemies | Effect |
|-------|---------|-----------|-----------|--------|
| 0.5 | 1 fighter | 2 fighters | 4 fighters | Conservative response |
| 0.8 | 1 fighter | 4 fighters | 7 fighters | **Balanced (default)** |
| 1.0 | 1 fighter | 4 fighters | 8 fighters | 1:1 parity |
| 1.4 | 2 fighters | 6 fighters | 12 fighters | Strong response |
| 2.0 | 2 fighters | 8 fighters | 16 fighters | Overwhelming force |
**Tactical Effects:**
- **Low (0.5-0.8):** Sustainable defense, squadrons last longer
- **Medium (0.8-1.2):** Balanced dogfights, realistic attrition
- **High (1.4-2.0):** Strong defense, rapid squadron depletion
**Asymmetric Scenarios:**
```lua
-- RED advantage
red = { interceptRatio = 1.4 },
blue = { interceptRatio = 0.8 }
-- BLUE advantage
red = { interceptRatio = 0.8 },
blue = { interceptRatio = 1.4 }
```
### Distance-Based Engagement
Control how far squadrons will chase threats:
```lua
{
templateName = "RED_CAP_Base_MiG29",
displayName = "Base Defense",
airbaseName = "Kilpyavr",
aircraft = 12,
-- ... other settings ...
zoneConfig = {
maxEngagementRange = 50000, -- Won't engage threats >50km from base
primaryResponse = 1.0,
secondaryResponse = 0.6
}
}
```
### Cooldown System
After launching interceptors, squadrons go on cooldown to prevent spam:
```lua
local TADC_SETTINGS = {
red = {
defaultCooldown = 300, -- 5 minutes between launches
-- ... other settings ...
}
}
```
**Per-Squadron Cooldown (optional):**
```lua
{
templateName = "RED_CAP_Base_MiG29",
cooldownOverride = 600, -- This squadron: 10-minute cooldown
-- ... other settings ...
}
```
### Aircraft Skill Levels
Adjust AI difficulty per squadron:
```lua
skill = AI.Skill.AVERAGE -- Easiest, good for training
skill = AI.Skill.GOOD -- Below average
skill = AI.Skill.HIGH -- Average pilots
skill = AI.Skill.EXCELLENT -- Above average (recommended)
skill = AI.Skill.ACE -- Hardest, expert pilots
```
**Mixed Difficulty Example:**
```lua
RED_SQUADRON_CONFIG = {
{
displayName = "Elite Squadron",
skill = AI.Skill.ACE, -- Best pilots
aircraft = 8,
-- ...
},
{
displayName = "Regular Squadron",
skill = AI.Skill.GOOD, -- Average pilots
aircraft = 12,
-- ...
}
}
```
---
## Common Scenarios
### Scenario 1: Simple Border Defense
**Goal:** RED defends northern border, BLUE defends southern border
```lua
-- RED Configuration
RED_SQUADRON_CONFIG = {
{
templateName = "RED_CAP_North_MiG29",
displayName = "Northern Border CAP",
airbaseName = "Northern_Base",
aircraft = 12,
skill = AI.Skill.EXCELLENT,
altitude = 20000,
speed = 350,
patrolTime = 30,
type = "FIGHTER",
primaryZone = "RED BORDER"
}
}
-- BLUE Configuration
BLUE_SQUADRON_CONFIG = {
{
templateName = "BLUE_CAP_South_F16",
displayName = "Southern Border CAP",
airbaseName = "Southern_Base",
aircraft = 12,
skill = AI.Skill.EXCELLENT,
altitude = 20000,
speed = 350,
patrolTime = 30,
type = "FIGHTER",
primaryZone = "BLUE BORDER"
}
}
```
**In Mission Editor:**
- Create zone "RED BORDER" (helicopter waypoints on northern border)
- Create zone "BLUE BORDER" (helicopter waypoints on southern border)
---
### Scenario 2: Layered Defense Network
**Goal:** Multiple squadrons covering overlapping zones with different priorities
```lua
RED_SQUADRON_CONFIG = {
-- Outer Layer: Long-range interceptors
{
templateName = "RED_LONG_RANGE_MiG31",
displayName = "Long Range Interceptors",
airbaseName = "Forward_Base",
aircraft = 8,
skill = AI.Skill.EXCELLENT,
altitude = 35000,
speed = 450,
patrolTime = 20,
type = "FIGHTER",
primaryZone = "OUTER PERIMETER"
},
-- Middle Layer: General defense
{
templateName = "RED_CAP_MiG29",
displayName = "Middle Defense CAP",
airbaseName = "Central_Base",
aircraft = 12,
skill = AI.Skill.EXCELLENT,
altitude = 25000,
speed = 350,
patrolTime = 30,
type = "FIGHTER",
primaryZone = "MIDDLE PERIMETER",
secondaryZone = "OUTER PERIMETER"
},
-- Inner Layer: Point defense
{
templateName = "RED_BASE_DEFENSE_SU27",
displayName = "Base Defense",
airbaseName = "Main_Base",
aircraft = 16,
skill = AI.Skill.ACE,
altitude = 20000,
speed = 320,
patrolTime = 40,
type = "FIGHTER",
primaryZone = "BASE PERIMETER",
tertiaryZone = "BASE PERIMETER",
zoneConfig = {
enableFallback = true,
fallbackThreshold = 0.3
}
}
}
```
---
### Scenario 3: Sustained Operations with Resupply
**Goal:** Long mission with automated squadron replenishment
**Squadron Config:**
```lua
RED_SQUADRON_CONFIG = {
{
templateName = "RED_CAP_Frontline_MiG29",
displayName = "Frontline CAP",
airbaseName = "Frontline_Base",
aircraft = 12, -- Will be resupplied
skill = AI.Skill.EXCELLENT,
altitude = 20000,
speed = 350,
patrolTime = 30,
type = "FIGHTER",
primaryZone = "COMBAT ZONE"
}
}
```
**Cargo Config** (in `Moose_TADC_CargoDispatcher.lua`):
```lua
local CARGO_SUPPLY_CONFIG = {
red = {
cargoTemplate = "CARGO_RED_AN26",
supplyAirfields = {
"Rear_Base_1", -- Safe logistics hub
"Rear_Base_2", -- Backup supply source
"Central_Depot" -- Main supply depot
},
replenishAmount = 4, -- +4 aircraft per delivery
threshold = 0.75 -- Trigger at 75% (9/12 aircraft)
}
}
```
**Mission Flow:**
1. Frontline squadron intercepts threats
2. Squadron drops to 9 aircraft (75%)
3. Cargo automatically dispatched from rear base
4. Transport flies to frontline base
5. Cargo delivers, squadron back to 12 aircraft
6. Cycle repeats throughout mission
---
### Scenario 4: Asymmetric Warfare
**Goal:** RED has numerical superiority, BLUE has quality advantage
```lua
-- RED: More squadrons, lower skill
local TADC_SETTINGS = {
red = {
interceptRatio = 0.8, -- Conservative response
maxActiveCAP = 12, -- More groups allowed
}
}
RED_SQUADRON_CONFIG = {
{
templateName = "RED_CAP_1",
airbaseName = "Base_1",
aircraft = 16, -- Large squadron
skill = AI.Skill.GOOD, -- Average skill
-- ...
},
{
templateName = "RED_CAP_2",
airbaseName = "Base_2",
aircraft = 16,
skill = AI.Skill.GOOD,
-- ...
},
{
templateName = "RED_CAP_3",
airbaseName = "Base_3",
aircraft = 16,
skill = AI.Skill.GOOD,
-- ...
}
}
-- BLUE: Fewer squadrons, higher skill
local TADC_SETTINGS = {
blue = {
interceptRatio = 1.2, -- Aggressive response
maxActiveCAP = 8, -- Fewer groups
}
}
BLUE_SQUADRON_CONFIG = {
{
templateName = "BLUE_CAP_1",
airbaseName = "Base_1",
aircraft = 10, -- Smaller squadron
skill = AI.Skill.ACE, -- Elite pilots
-- ...
},
{
templateName = "BLUE_CAP_2",
airbaseName = "Base_2",
aircraft = 10,
skill = AI.Skill.ACE,
-- ...
}
}
```
---
## Tips for New Mission Makers
### Start Simple
1. **First Mission:** Use 1-2 squadrons per side with no zones
2. **Second Mission:** Add zone-based defense
3. **Third Mission:** Add cargo resupply system
4. **Advanced:** Multi-squadron layered defense with fallback
### Realistic Aircraft Numbers
**Small Airbase:** 6-8 aircraft per squadron
**Medium Airbase:** 10-12 aircraft per squadron
**Large Airbase:** 14-18 aircraft per squadron
**Balance across map:** If RED has 40 total aircraft, BLUE should have similar unless asymmetric
### Performance Considerations
- **Limit active groups:** Use `maxActiveCAP` to prevent FPS drops
- **Zone sizes matter:** Smaller zones = less scanning overhead
- **Cargo cooldowns:** Prevent cargo spam with reasonable cooldowns
- **Squadron counts:** 3-5 squadrons per side is a good starting point
### Testing Workflow
1. **Create minimal setup** (1 squadron each side)
2. **Test in mission editor** using "Fly Now"
3. **Check F10 menus** for squadron status
4. **Spawn enemy aircraft** to test intercepts
5. **Review DCS.log** for validation messages
6. **Expand gradually** once basic system works
### Common Mistakes to Avoid
**Using UNIT templates instead of GROUP templates**
✅ Use GROUP templates (late activation groups)
**Misspelling airbase names**
✅ Copy exact names from mission editor
**Loading scripts in wrong order**
✅ Squadron Config → Main TADC → Cargo Dispatcher
**Setting intercept ratio too high**
✅ Start with 0.8-1.0, adjust after testing
**Forgetting to load MOOSE first**
✅ MOOSE must be first script loaded
---
## Conclusion
The Universal TADC system provides mission makers with powerful, automated air defense capabilities that create dynamic, realistic air combat scenarios. By following this guide, even new mission makers can create sophisticated missions with minimal scripting knowledge.
### Key Takeaways
**Three scripts work together:** Squadron Config → Main TADC → Cargo Dispatcher
**Configuration is simple:** Edit tables, not complex code
**Both coalitions operate independently:** Balanced or asymmetric scenarios
**Zones enable tactical behavior:** Realistic area-of-responsibility system
**Cargo enables sustained operations:** Long missions with automatic resupply
**Built-in validation:** Checks configuration before mission starts
**F10 menus provide visibility:** Monitor system status in real-time
### Getting Help
If you encounter issues:
1. Check DCS.log for validation errors
2. Use F10 menu diagnostics
3. Run console commands for detailed info
4. Review this guide's troubleshooting section
5. Start simple and expand gradually
### Next Steps
1. Set up your first squadron (1 RED, 1 BLUE)
2. Test basic intercept behavior
3. Add zones for tactical depth
4. Implement cargo resupply for long missions
5. Experiment with advanced features
Happy mission making! 🚁✈️
---
*Author: F99th-TracerFacer
*Document Version: 1.0*
*Last Updated: October 2025*
*Compatible with: MOOSE Framework & DCS World*

View File

@ -124,13 +124,13 @@ CTLD.Messages = {
medevac_vectors = "MEDEVAC: {vehicle} crew bearing {brg}°, range {rng} {rng_u}. Time remaining: {time_remain} mins.", medevac_vectors = "MEDEVAC: {vehicle} crew bearing {brg}°, range {rng} {rng_u}. Time remaining: {time_remain} mins.",
medevac_salvage_status = "Coalition Salvage Points: {points}. Use salvage to build out-of-stock items.", medevac_salvage_status = "Coalition Salvage Points: {points}. Use salvage to build out-of-stock items.",
medevac_salvage_used = "Built {item} using {salvage} salvage points. Remaining: {remaining}.", medevac_salvage_used = "Built {item} using {salvage} salvage points. Remaining: {remaining}.",
medevac_salvage_insufficient = "Out of stock and insufficient salvage. Need {need} salvage points (have {have}). Deliver MEDEVAC crews to MASH to earn more.", medevac_salvage_insufficient = "Out of stock. Requires {need} salvage points (need {deficit} more). Earn salvage by delivering MEDEVAC crews to MASH or sling-loading enemy wreckage.",
medevac_crew_warn_15min = "WARNING: {vehicle} crew at {grid} - rescue window expires in 15 minutes!", medevac_crew_warn_15min = "WARNING: {vehicle} crew at {grid} - rescue window expires in 15 minutes!",
medevac_crew_warn_5min = "URGENT: {vehicle} crew at {grid} - rescue window expires in 5 minutes!", medevac_crew_warn_5min = "URGENT: {vehicle} crew at {grid} - rescue window expires in 5 minutes!",
medevac_unload_hold = "MEDEVAC: Stay grounded in the MASH zone for {seconds} seconds to offload casualties.", medevac_unload_hold = "MEDEVAC: Stay grounded in the MASH zone for {seconds} seconds to offload casualties.",
-- Sling-Load Salvage messages -- Sling-Load Salvage messages
slingload_salvage_spawned = "SALVAGE OPPORTUNITY: Enemy wreckage at {grid}. Weight: {weight}kg, Est. Value: {reward}pts. {time_remain} to collect.", slingload_salvage_spawned = "SALVAGE OPPORTUNITY: Enemy wreckage at {grid}. Weight: {weight}kg, Est. Value: {reward}pts. {time_remain} remaining to collect!",
slingload_salvage_delivered = "{player} delivered {weight}kg salvage for {reward} points ({condition})! Coalition total: {total}", slingload_salvage_delivered = "{player} delivered {weight}kg salvage for {reward} points ({condition})! Coalition total: {total}",
slingload_salvage_expired = "SALVAGE LOST: Crate {id} at {grid} deteriorated.", slingload_salvage_expired = "SALVAGE LOST: Crate {id} at {grid} deteriorated.",
slingload_salvage_damaged = "CAUTION: Salvage crate damaged in transit. Value reduced to {reward}pts.", slingload_salvage_damaged = "CAUTION: Salvage crate damaged in transit. Value reduced to {reward}pts.",
@ -236,7 +236,7 @@ CTLD.Config = {
-- Safety offsets to avoid spawning units too close to player aircraft -- Safety offsets to avoid spawning units too close to player aircraft
BuildSpawnOffset = 40, -- meters: shift build point forward from the aircraft (0 = spawn centered on aircraft) BuildSpawnOffset = 40, -- meters: shift build point forward from the aircraft (0 = spawn centered on aircraft)
TroopSpawnOffset = 40, -- meters: shift troop unload point forward from the aircraft TroopSpawnOffset = 25, -- meters: shift troop unload point forward from the aircraft
DropCrateForwardOffset = 35, -- meters: drop loaded crates this far in front of the aircraft DropCrateForwardOffset = 35, -- meters: drop loaded crates this far in front of the aircraft
-- === Build & Crate Handling === -- === Build & Crate Handling ===
@ -366,11 +366,11 @@ CTLD.Config = {
LabelOffsetX = 200, -- meters: horizontal nudge; adjust if text appears left-anchored in your DCS build LabelOffsetX = 200, -- meters: horizontal nudge; adjust if text appears left-anchored in your DCS build
-- Per-kind label prefixes -- Per-kind label prefixes
LabelPrefixes = { LabelPrefixes = {
Pickup = 'Supply Zone', Pickup = 'Supply',
Drop = 'Drop Zone', Drop = 'Drop',
FOB = 'FOB Zone', FOB = 'FOB',
MASH = 'MASH Zone', MASH = 'MASH',
SalvageDrop = 'Salvage Collection Zone', SalvageDrop = 'Salvage',
} }
}, },
@ -409,13 +409,13 @@ CTLD.Config = {
-- Spawn probability when enemy ground units die -- Spawn probability when enemy ground units die
SpawnChance = { SpawnChance = {
[coalition.side.BLUE] = 0.90, -- 90% chance when BLUE unit dies (RED can collect the salvage) [coalition.side.BLUE] = 0.15, -- 15% chance when BLUE unit dies (RED can collect the salvage)
[coalition.side.RED] = 0.90, -- 90% chance when RED unit dies (BLUE can collect the salvage) [coalition.side.RED] = 0.15, -- 90% chance when RED unit dies (BLUE can collect the salvage)
}, },
-- Weight classes with spawn probabilities and reward rates -- Weight classes with spawn probabilities and reward rates
WeightClasses = { WeightClasses = {
{ name = 'Light', min = 1500, max = 2500, probability = 0.50, rewardPer500kg = 2 }, -- Huey-capable { name = 'Light', min = 500, max = 1000, probability = 0.50, rewardPer500kg = 2 }, -- Huey-capable
{ name = 'Medium', min = 2501, max = 5000, probability = 0.30, rewardPer500kg = 3 }, -- Hip/Mi-8 { name = 'Medium', min = 2501, max = 5000, probability = 0.30, rewardPer500kg = 3 }, -- Hip/Mi-8
{ name = 'Heavy', min = 5001, max = 8000, probability = 0.15, rewardPer500kg = 5 }, -- Large helos { name = 'Heavy', min = 5001, max = 8000, probability = 0.15, rewardPer500kg = 5 }, -- Large helos
{ name = 'SuperHeavy', min = 8001, max = 12000, probability = 0.05, rewardPer500kg = 8 }, -- Chinook only { name = 'SuperHeavy', min = 8001, max = 12000, probability = 0.05, rewardPer500kg = 8 }, -- Chinook only
@ -505,8 +505,8 @@ CTLD.MEDEVAC = {
-- Crew spawning -- Crew spawning
-- Per-coalition spawn probabilities for asymmetric scenarios -- Per-coalition spawn probabilities for asymmetric scenarios
CrewSurvivalChance = { CrewSurvivalChance = {
[coalition.side.BLUE] = .50, -- probability (0.0-1.0) that BLUE crew survives to spawn MEDEVAC request. 1.0 = 100% (testing), 0.02 = 2% (production) [coalition.side.BLUE] = .30, -- probability (0.0-1.0) that BLUE crew survives to spawn MEDEVAC request. 1.0 = 100% (testing), 0.02 = 2% (production)
[coalition.side.RED] = .50, -- probability (0.0-1.0) that RED crew survives to spawn MEDEVAC request [coalition.side.RED] = .30, -- probability (0.0-1.0) that RED crew survives to spawn MEDEVAC request
}, },
ManPadSpawnChance = { ManPadSpawnChance = {
[coalition.side.BLUE] = 0.1, -- probability (0.0-1.0) that BLUE crew spawns with a MANPADS soldier. 1.0 = 100% (testing), 0.1 = 10% (production) [coalition.side.BLUE] = 0.1, -- probability (0.0-1.0) that BLUE crew spawns with a MANPADS soldier. 1.0 = 100% (testing), 0.1 = 10% (production)
@ -9203,9 +9203,10 @@ function CTLD:_TryUseSalvageForCrate(group, crateKey, catalogEntry)
local available = CTLD._salvagePoints[self.Side] or 0 local available = CTLD._salvagePoints[self.Side] or 0
if available < salvageCost then if available < salvageCost then
-- Send insufficient salvage message -- Send insufficient salvage message
local deficit = salvageCost - available
_msgGroup(group, _fmtTemplate(CTLD.Messages.medevac_salvage_insufficient, { _msgGroup(group, _fmtTemplate(CTLD.Messages.medevac_salvage_insufficient, {
need = salvageCost, need = salvageCost,
have = available deficit = deficit
})) }))
return false return false
end end
@ -10593,6 +10594,13 @@ function CTLD:_SpawnSlingLoadSalvageCrate(unitPos, unitTypeName, enemySide, even
-- Calculate expiration time -- Calculate expiration time
local lifetime = cfg.CrateLifetime or 10800 local lifetime = cfg.CrateLifetime or 10800
local timeRemainMin = math.floor(lifetime / 60) local timeRemainMin = math.floor(lifetime / 60)
local timeRemainHrs = math.floor(timeRemainMin / 60)
local timeRemainStr
if timeRemainHrs >= 1 then
timeRemainStr = string.format("%d hr%s", timeRemainHrs, timeRemainHrs > 1 and "s" or "")
else
timeRemainStr = string.format("%d min%s", timeRemainMin, timeRemainMin > 1 and "s" or "")
end
local grid = self:_GetMGRSString(spawnPos) local grid = self:_GetMGRSString(spawnPos)
-- Announce to coalition -- Announce to coalition
@ -10600,7 +10608,7 @@ function CTLD:_SpawnSlingLoadSalvageCrate(unitPos, unitTypeName, enemySide, even
grid = grid, grid = grid,
weight = weight, weight = weight,
reward = rewardValue, reward = rewardValue,
time_remain = timeRemainMin, time_remain = timeRemainStr,
}) })
_msgCoalition(enemySide, msg) _msgCoalition(enemySide, msg)

Binary file not shown.