mirror of
https://github.com/ciribob/DCS-CTLD.git
synced 2026-08-11 19:57:28 +00:00
Compare commits
178 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9302203f09 | |||
| e59bc50e6c | |||
| c12c0b242d | |||
| 659268d3f5 | |||
| 041a9ab6af | |||
| 8d73c966db | |||
| af3e4fab17 | |||
| 97e7c72144 | |||
| 65336fdab8 | |||
| 61db3e9f93 | |||
| a1da0ea1c4 | |||
| 782940265d | |||
| b5cd8929f3 | |||
| 1826d28a86 | |||
| 359bf1418d | |||
| e3424c6af5 | |||
| 1ac3b3f8a9 | |||
| e13e630830 | |||
| 7bfed7ef55 | |||
| 8bfaba399f | |||
| 4dada3d24d | |||
| 74b40fe3cb | |||
| aa8bc78b27 | |||
| 7286a6a010 | |||
| c8bb41648c | |||
| 08635622b0 | |||
| 42e8549649 | |||
| 5c48a9d756 | |||
| f3983c6a90 | |||
| 1c66408127 | |||
| 3ac2b3b22c | |||
| c576268d87 | |||
| 35f52d543a | |||
| c608d00d15 | |||
| d2789f554a | |||
| fcef6a66d1 | |||
| dd71451194 | |||
| 807e81e389 | |||
| 33d8810ebb | |||
| 87e8a31f31 | |||
| e4a0fff931 | |||
| ca5bf76c78 | |||
| f8659c5de6 | |||
| 9a0cc8eeee | |||
| a706890cb6 | |||
| 815684dd77 | |||
| 0ccb2ba7bb | |||
| 1e471df264 | |||
| cc8b916870 | |||
| cd62d0c60a | |||
| a35e023752 | |||
| f71e7f3213 | |||
| a468faefec | |||
| 99f2c7aec0 | |||
| 7db8e2d772 | |||
| 026099bd12 | |||
| 82a330e61a | |||
| b3470250e4 | |||
| bbde4ae62b | |||
| 540a316754 | |||
| 574217d931 | |||
| 1fce52088d | |||
| 4d704a489e | |||
| da80f9ea06 | |||
| 93260908c4 | |||
| 3c261e8dc2 | |||
| 7b7eb62bf0 | |||
| 951ee37903 | |||
| 3a7ab50834 | |||
| 9a69d4b52c | |||
| 0345d16150 | |||
| d071fba20f | |||
| 1a32ce6b57 | |||
| 85275ad84b | |||
| 819ba2de53 | |||
| 8f34ad3902 | |||
| 16f366d039 | |||
| 2524320096 | |||
| e6b8c9ba14 | |||
| a5b47503be | |||
| aaa25dcd41 | |||
| e834dd1395 | |||
| 774e499759 | |||
| 0a1f4de471 | |||
| f9cd8b8b51 | |||
| 3eef8d9a5d | |||
| e1b9e1d7d8 | |||
| ca976f4387 | |||
| 91f74b73ed | |||
| f8df243043 | |||
| 29b19d5262 | |||
| 48aca9d488 | |||
| 12741d0532 | |||
| 6df583b9f1 | |||
| ca99c58f98 | |||
| 879272a2e8 | |||
| 267156e04e | |||
| 6fa12ca46f | |||
| eb22f5dc67 | |||
| 601805abb0 | |||
| e0e4279f20 | |||
| 9023c571ba | |||
| 72aab26e43 | |||
| aef146a6d8 | |||
| d8db662dc5 | |||
| e4f13f98b1 | |||
| c1aba95b6f | |||
| d50bcfccd9 | |||
| f0f095e274 | |||
| 69f04dfd57 | |||
| 546f3feaae | |||
| 23e83bf53a | |||
| ee07423588 | |||
| 96d801c342 | |||
| b1818d9fb3 | |||
| 2bc6e76516 | |||
| 61a1fd42ee | |||
| 954a6b0fad | |||
| 159915a6ac | |||
| d029e2479b | |||
| 251467fee4 | |||
| f3a9b44388 | |||
| f0adb72c49 | |||
| ecc47bb247 | |||
| 0c74729837 | |||
| ef1d3d57e5 | |||
| b69f20eb6b | |||
| d80a70c0ee | |||
| 80fe5d59b0 | |||
| 3eaa08b74e | |||
| f83365ec63 | |||
| 0b3b1ea1f8 | |||
| 888d949234 | |||
| 75c9d0bf68 | |||
| 7517eaf188 | |||
| 1181fcfff7 | |||
| 9def2f1a3a | |||
| 9c25240c43 | |||
| 8a17ff52aa | |||
| d125cdbe80 | |||
| 8dcaa3e0bb | |||
| baf1231141 | |||
| d1d9b55769 | |||
| ff06dcecf7 | |||
| dcdf6438a1 | |||
| 4d0062dee3 | |||
| c618f4b282 | |||
| b390d5dabf | |||
| f4b81fd101 | |||
| 78aa8a010e | |||
| 6996769cec | |||
| 0d9c01283c | |||
| fa1553a83e | |||
| 4db3c71728 | |||
| 0180cf33d2 | |||
| c51a3d80ca | |||
| 9eb4fc66b5 | |||
| 8f2a335609 | |||
| c5bae34ed1 | |||
| 6fc9f146ae | |||
| b6232d3c15 | |||
| 111fd73b94 | |||
| 61d9e206b2 | |||
| 08ebd7e82d | |||
| f152f58a97 | |||
| 38e86bead3 | |||
| b0d8f8b5c7 | |||
| a58efad985 | |||
| 0793d3bda5 | |||
| c397bad107 | |||
| 791ee4e63e | |||
| 5aa37c52a4 | |||
| fb26645996 | |||
| 3a3bcaf568 | |||
| fb804b4a22 | |||
| 665bdddbdd | |||
| 6beaedb79a | |||
| 9c2f982d03 |
@@ -0,0 +1,265 @@
|
||||
# DCS-CTLD — As-Is Analysis
|
||||
|
||||
> This document captures the current state of the CTLD codebase prior to
|
||||
> the v2 modernization effort. It serves as a reference and justification
|
||||
> for the changes outlined in `MODERNIZATION-PLAN.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
| Metric | Value |
|
||||
| ------ | ----- |
|
||||
| Primary script | `CTLD.lua` |
|
||||
| Lines of code | ~8 700 |
|
||||
| i18n script | `CTLD-i18n.lua` |
|
||||
| Bundled dependency | `mist.lua` (MIST 4.5 build 128-DYNSLOTS-02) |
|
||||
| Lua version | 5.1 (DCS sandbox) |
|
||||
| Public functions (`ctld.*`) | 100+ |
|
||||
| Local / private functions | 0 (everything is public on the `ctld` table) |
|
||||
| OOP / metatables usage | None — fully procedural |
|
||||
| Unit tests | None |
|
||||
|
||||
---
|
||||
|
||||
## 2. File structure
|
||||
|
||||
```text
|
||||
.
|
||||
├── CTLD.lua -- Main script (monolith)
|
||||
├── CTLD-i18n.lua -- Translation tables (FR, ES, KO)
|
||||
├── mist.lua -- MIST library (stock, not customized)
|
||||
├── beacon.ogg -- Radio beacon sound
|
||||
├── beaconsilent.ogg -- Silent beacon sound (FC3)
|
||||
├── README.md -- User / mission-maker guide
|
||||
├── demo-mission.miz
|
||||
├── test-mission.miz
|
||||
├── test-dev-dynamic.miz
|
||||
├── test-dev-static.miz
|
||||
├── test_witchcraft_#131.miz
|
||||
└── test_witchcraft_#137 et #149.miz
|
||||
```
|
||||
|
||||
No `src/`, no `test/`, no `docs/`, no CI configuration.
|
||||
|
||||
---
|
||||
|
||||
## 3. CTLD.lua section breakdown
|
||||
|
||||
| Section | Lines | Content |
|
||||
| ------- | ----- | ------- |
|
||||
| Header + contributors | 1–35 | Credits, links |
|
||||
| i18n table setup | 36–360 | English reference keys (all `= ""`), `i18n_translate()` |
|
||||
| USER CONFIGURATION | 403–600 | 50+ tunable parameters |
|
||||
| Crate configuration | 601–1 200 | `ctld.spawnableCrates`, AA system templates |
|
||||
| Public API functions | 1 200–1 950 | Functions usable from DO SCRIPT triggers |
|
||||
| Core gameplay logic | 1 950–4 600 | Troops, vehicles, crates, FOBs, beacons |
|
||||
| JTAC subsystem | 4 600–7 000 | Targeting, lasing, IR points, F10 menus |
|
||||
| AI processing + menus | 7 000–8 200 | AI auto-load/unload, F10 menu building |
|
||||
| `ctld.initialize()` | 8 190–8 400 | State tables init, zone parsing |
|
||||
| Event handler + tools | 8 400–8 705 | DCS event handler, `ctld.tools` utilities |
|
||||
|
||||
---
|
||||
|
||||
## 4. State management
|
||||
|
||||
34 global tables inside the `ctld` namespace track runtime state.
|
||||
Most are duplicated per coalition.
|
||||
|
||||
### 4.1 — Coalition-duplicated tables (RED / BLUE pairs)
|
||||
|
||||
| Table pair | Content |
|
||||
| ---------- | ------- |
|
||||
| `ctld.spawnedCratesRED` / `BLUE` | Spawned crate statics |
|
||||
| `ctld.droppedTroopsRED` / `BLUE` | Deployed infantry groups |
|
||||
| `ctld.droppedVehiclesRED` / `BLUE` | Deployed vehicles |
|
||||
| `ctld.droppedFOBCratesRED` / `BLUE` | FOB crates on ground |
|
||||
|
||||
This duplication creates **50+ `if coalition == 1 then … RED … else … BLUE`** branches.
|
||||
|
||||
### 4.2 — Shared tables
|
||||
|
||||
| Table | Content |
|
||||
| ----- | ------- |
|
||||
| `ctld.inTransitTroops` | Cargo currently onboard (keyed by unit name) |
|
||||
| `ctld.inTransitSlingLoadCrates` | Simulated sling-load crates |
|
||||
| `ctld.builtFOBS` | Completed FOB positions |
|
||||
| `ctld.completeAASystems` | Assembled AA system groups |
|
||||
| `ctld.deployedRadioBeacons` | Active beacons |
|
||||
| `ctld.fobBeacons` | FOB beacon cache (refreshed every 60 s) |
|
||||
| `ctld.extractZones` | Extract zone definitions |
|
||||
| `ctld.hoverStatus` | Hover-over-crate tracking |
|
||||
| `ctld.crateLookupTable` | Crate weight → type lookup |
|
||||
| `ctld.callbacks` | Registered callback functions |
|
||||
| `ctld.jtacUnits` | JTAC unit references |
|
||||
| `ctld.jtacCurrentTargets` | JTAC active targets |
|
||||
| `ctld.jtacSelectedTarget` | Player-selected JTAC targets |
|
||||
| `ctld.jtacGeneratedLaserCodes` | Allocated laser codes |
|
||||
| `ctld.usedUHFFrequencies` / `VHF` / `FM` | Radio frequency pools |
|
||||
|
||||
---
|
||||
|
||||
## 5. Code duplication patterns
|
||||
|
||||
### 5.1 — RED/BLUE branching (most pervasive)
|
||||
|
||||
```lua
|
||||
-- This pattern appears 50+ times
|
||||
if _heli:getCoalition() == 1 then
|
||||
_list = ctld.droppedTroopsRED
|
||||
else
|
||||
_list = ctld.droppedTroopsBLUE
|
||||
end
|
||||
```
|
||||
|
||||
### 5.2 — Troop / Vehicle symmetry
|
||||
|
||||
Many functions have near-identical troop and vehicle variants differentiated
|
||||
only by a boolean parameter (`true` = troops, `false` = vehicles).
|
||||
|
||||
### 5.3 — Sling load vs simulated load
|
||||
|
||||
Two code paths for crate pickup depending on `ctld.slingLoad`:
|
||||
|
||||
- Real sling load (DCS native, crashy)
|
||||
- Simulated hover load (custom implementation)
|
||||
|
||||
### 5.4 — Zone handling
|
||||
|
||||
Pickup zones, dropoff zones, and waypoint zones share similar parsing
|
||||
(smoke color, coalition check, active flag) but are implemented separately.
|
||||
|
||||
---
|
||||
|
||||
## 6. MIST dependency
|
||||
|
||||
91 calls to `mist.*` functions across the codebase.
|
||||
|
||||
| Category | Functions | Call count |
|
||||
| -------- | --------- | ---------- |
|
||||
| Utility | `deepCopy`, `round`, `makeVec2/3` | ~28 |
|
||||
| Distance / vectors | `get2DDist`, `vec.mag`, `vec.dp`, `vec.sub` | ~15 |
|
||||
| Heading / bearing | `getHeading` | ~8 |
|
||||
| Coordinate format | `tostringLL`, `tostringMGRS` | ~4 |
|
||||
| Spawning | `dynAdd`, `dynAddStatic` | ~8 |
|
||||
| Route / waypoints | `buildWP`, `getGroupRoute` | ~4 |
|
||||
| LOS / search | `getUnitsLOS`, `getAvgPos`, `makeUnitTable` | ~4 |
|
||||
| Database | `mist.DBs.unitsByName` | iterated |
|
||||
| Scheduling | `mist.scheduleFunction` | ~1 |
|
||||
| Other | misc | ~19 |
|
||||
|
||||
No functions provide a fallback if MIST is absent — CTLD hard-crashes on
|
||||
`assert(mist ~= nil)` during initialization.
|
||||
|
||||
---
|
||||
|
||||
## 7. Scheduling and timers
|
||||
|
||||
20+ `timer.scheduleFunction` calls drive recurring behavior.
|
||||
All timers reschedule themselves recursively; there are no continuous
|
||||
polling loops.
|
||||
|
||||
| Timer | Interval | Purpose |
|
||||
| ----- | -------- | ------- |
|
||||
| `checkTransportStatus` | 3 s | Monitor transport helicopters |
|
||||
| `checkHoverStatus` | 1 s | Track hover-over-crate countdown |
|
||||
| `refreshRadioBeacons` | 60 s | Update beacon battery status |
|
||||
| `refreshSmoke` | 300 s | Refresh smoke markers |
|
||||
| `checkAIStatus` | 2 s | AI troop behavior check |
|
||||
| `timerJTACAutoLase` | variable | JTAC targeting loop |
|
||||
| `timerLaseUnit` | variable | Single-unit lasing |
|
||||
| `autoUpdateRepackMenu` | 1 s | Refresh vehicle repacking F10 menu |
|
||||
| `reconRefreshTargetsInLosOnF10Map` | variable | RECON map markers |
|
||||
|
||||
---
|
||||
|
||||
## 8. Error handling
|
||||
|
||||
- **pcall usage**: 12 instances, mainly around crate spawning and hover math.
|
||||
- **Nil checks**: Extensive `if X == nil then return end` before DCS API calls.
|
||||
- **No structured error propagation**: errors are logged via `env.error()` /
|
||||
`ctld.logError()` and silently swallowed.
|
||||
- **No stack traces** or exception chaining.
|
||||
|
||||
---
|
||||
|
||||
## 9. Internationalization (i18n)
|
||||
|
||||
| Language | Code | `translation_version` | Completeness |
|
||||
| -------- | ---- | --------------------- | ------------ |
|
||||
| English | `en` | 1.6 | Reference (keys only, values = `""`) |
|
||||
| French | `fr` | 1.6 | Partial — many crate/equipment names empty |
|
||||
| Spanish | `es` | 1.6 | Similar to French |
|
||||
| Korean | `ko` | **1.1** | Severely outdated, many missing keys |
|
||||
|
||||
Issues:
|
||||
|
||||
- No tooling to detect missing keys or version drift.
|
||||
- Empty string (`""`) is a valid Lua value, so missing translations silently
|
||||
fall back to the English key (which is also `""` for reference entries).
|
||||
- All translations live in a single `CTLD-i18n.lua` file — no per-language
|
||||
separation.
|
||||
|
||||
---
|
||||
|
||||
## 10. Documentation
|
||||
|
||||
| Audience | Coverage | Location |
|
||||
| -------- | -------- | -------- |
|
||||
| Mission maker | Partial | `README.md` (setup, config, API examples) |
|
||||
| Player | Minimal | `README.md` (gameplay mechanics mentioned briefly) |
|
||||
| Developer | None | No architecture doc, no contributing guide |
|
||||
|
||||
README strengths:
|
||||
|
||||
- Good coverage of pickup/dropoff zone configuration.
|
||||
- Code examples for DO SCRIPT functions.
|
||||
- Dynamic loading workflow documented.
|
||||
|
||||
README gaps:
|
||||
|
||||
- JTAC advanced features poorly explained.
|
||||
- No API reference table (signatures, parameters, return values).
|
||||
- No architecture or data flow diagrams.
|
||||
- Some examples use different defaults than the current code.
|
||||
|
||||
---
|
||||
|
||||
## 11. Testing
|
||||
|
||||
- **No automated tests** of any kind.
|
||||
- **Manual smoke testing** via `.miz` missions in the repository:
|
||||
- `test-mission.miz` — full feature demonstration
|
||||
- `test-dev-dynamic.miz` — dynamic script loading for fast iteration
|
||||
- `test-dev-static.miz` — static loading variant
|
||||
- `test_witchcraft_#131.miz`, `test_witchcraft_#137 et #149.miz` — bug repro missions
|
||||
- No test framework, no mocks, no CI.
|
||||
|
||||
---
|
||||
|
||||
## 12. Dead code and technical debt
|
||||
|
||||
| Item | Location | Detail |
|
||||
| ---- | -------- | ------ |
|
||||
| Commented-out blocks | 18+ blocks across file | Old implementations, debug markers, disabled alternatives |
|
||||
| Unused function | `ctld.tools.getRelativeBearing` | Defined but never called |
|
||||
| Unused function | `ctld.tools.isValueInIpairTable` | Called once, could be inlined |
|
||||
| Commented event handler | `ctld.initialize()` | Old `ctld.eventHandler` pattern, replaced but not removed |
|
||||
| Disabled vehicle types | Crate config tables | Many entries commented out (`-- BUK`, `-- Strela`, etc.) |
|
||||
| Debug markers | Various | `--ctld.logTrace("FG_ XXXX...")` left in code |
|
||||
| Alternate coordinate | `ctld.listFOBS()` | MGRS conversion commented out |
|
||||
|
||||
---
|
||||
|
||||
## 13. Summary of pain points
|
||||
|
||||
1. **Monolithic file**: 8 700 lines, impossible to navigate or review efficiently.
|
||||
2. **No encapsulation**: 100+ public functions, 0 local functions, all state is global.
|
||||
3. **RED/BLUE duplication**: 50+ coalition branches + paired tables.
|
||||
4. **MIST hard dependency**: 91 calls, no fallback, no abstraction layer.
|
||||
5. **No tests**: zero automated tests, zero mocks, zero CI.
|
||||
6. **No OOP**: purely procedural, no classes, no metatables.
|
||||
7. **Dead code**: 18+ commented blocks, unused functions.
|
||||
8. **i18n drift**: Korean 3 versions behind, many empty translations.
|
||||
9. **No developer documentation**: new contributors must read 8 700 lines to understand the architecture.
|
||||
10. **No build system**: the source file IS the deliverable.
|
||||
@@ -0,0 +1,364 @@
|
||||
# DCS-CTLD Modernization Plan
|
||||
|
||||
> Status: **Draft**
|
||||
> Branch: `next` (git flow, `main` stays stable)
|
||||
> Target: CTLD v2.0
|
||||
|
||||
---
|
||||
|
||||
## Vision
|
||||
|
||||
Rewrite CTLD as a modern, modular, and testable Lua project while
|
||||
temporarily preserving backward compatibility with existing missions.
|
||||
The deliverable remains a single `.lua` file produced by an automated build.
|
||||
|
||||
---
|
||||
|
||||
## Architectural decisions
|
||||
|
||||
Key decisions made during the planning phase, for reference.
|
||||
|
||||
| # | Topic | Decision |
|
||||
| - | ----- | -------- |
|
||||
| 1 | Module split | Separate source files in `src/`, concatenated into a single `dist/CTLD.lua` by a build script (CI) |
|
||||
| 2 | OOP level | Full OOP Lua 5.1 with metatables and classes — ready to break backward compat when needed |
|
||||
| 3 | MIST dependency | Middleware layer (`src/mist_compat/`): CTLD never calls `mist.*` directly. Long-term goal: eliminate MIST entirely |
|
||||
| 4 | API breaking changes | Short/medium term: legacy wrappers with deprecation warnings. Long term (v3): legacy API removed |
|
||||
| 5 | Lua environment | Lua 5.1 DCS sandbox, desanitized server assumed (`io`, `os`, `lfs` accessible) |
|
||||
| 6 | Testing | Both: busted + DCS/MIST mocks in CI, and in-game test missions for integration |
|
||||
| 7 | Documentation | 3 audiences (player, mission maker, developer). Markdown in `docs/`, auto-published to GitHub Pages via MkDocs |
|
||||
| 8 | i18n | Included in plan: audit missing keys, CI lint for version drift, cleanup empty translations |
|
||||
| 9 | Priorities | 1-Cleanup, 2-OOP, 3-MIST middleware, 4-Legacy compat, 5-Tests, 6-CI, 7-i18n, 8-Docs |
|
||||
| 10 | Branching | `next` branch for v2, git flow. `main` stays stable. Feature branches: `feature/<phase>-<description>` |
|
||||
|
||||
---
|
||||
|
||||
## Guiding principles
|
||||
|
||||
- **OOP Lua 5.1**: metatables, classes, state encapsulation.
|
||||
- **Separate modules** in development, single file in delivery.
|
||||
- **Zero direct MIST dependency**: all MIST usage goes through a middleware layer.
|
||||
- **Automated testing**: pure logic tested outside DCS, integration tested in-game.
|
||||
- **Auto-generated documentation**: from the repo to GitHub Pages.
|
||||
- **Transitional legacy compatibility**: deprecated wrappers with warnings, removed in v3.
|
||||
|
||||
---
|
||||
|
||||
## Phases
|
||||
|
||||
### Phase 1 — Dead code cleanup
|
||||
|
||||
**Goal**: Start from a clean, noise-free baseline.
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 1.1 | Remove the 18+ identified commented-out code blocks |
|
||||
| 1.2 | Remove unused functions (`ctld.tools.getRelativeBearing`, etc.) |
|
||||
| 1.3 | Remove state variables that are never read |
|
||||
| 1.4 | Clean up obsolete `--TODO`/`--FIXME` markers |
|
||||
| 1.5 | Validate with existing test missions that nothing breaks |
|
||||
|
||||
**Deliverable**: A lighter CTLD.lua, functionally identical.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2 — Module split + OOP
|
||||
|
||||
**Goal**: Transform the procedural monolith into a modular, object-oriented architecture.
|
||||
|
||||
#### 2.1 — Lua 5.1 class system
|
||||
|
||||
Define a micro class framework based on metatables:
|
||||
|
||||
```lua
|
||||
-- src/lib/class.lua
|
||||
local function class(base)
|
||||
local cls = {}
|
||||
cls.__index = cls
|
||||
if base then setmetatable(cls, { __index = base }) end
|
||||
function cls:new(...)
|
||||
local instance = setmetatable({}, cls)
|
||||
if instance.init then instance:init(...) end
|
||||
return instance
|
||||
end
|
||||
return cls
|
||||
end
|
||||
```
|
||||
|
||||
#### 2.2 — Module layout
|
||||
|
||||
Target structure for the `src/` directory:
|
||||
|
||||
```text
|
||||
src/
|
||||
├── lib/
|
||||
│ ├── class.lua -- OOP micro-framework
|
||||
│ ├── utils.lua -- Utilities (deepCopy, formatText, logging)
|
||||
│ └── vec.lua -- Vector math (2D/3D distance, bearing)
|
||||
├── core/
|
||||
│ ├── config.lua -- User configuration (USER CONFIGURATION)
|
||||
│ ├── state.lua -- State manager (replaces 34 global tables)
|
||||
│ ├── coalition.lua -- Coalition class (eliminates RED/BLUE duplication)
|
||||
│ ├── i18n.lua -- i18n system
|
||||
│ └── events.lua -- Event handler + callback system
|
||||
├── transport/
|
||||
│ ├── transport.lua -- Transport class (heli/plane)
|
||||
│ ├── troops.lua -- Troop management (load/unload/extract)
|
||||
│ └── vehicles.lua -- Vehicle management
|
||||
├── logistics/
|
||||
│ ├── crate.lua -- Crate class
|
||||
│ ├── crate_manager.lua -- Spawning, hover, sling load, unpack
|
||||
│ ├── fob.lua -- FOB construction and management
|
||||
│ └── beacon.lua -- Radio beacons
|
||||
├── jtac/
|
||||
│ ├── jtac_controller.lua -- JTACController class
|
||||
│ ├── targeting.lua -- Target acquisition, LOS, priorities
|
||||
│ ├── lasing.lua -- Laser/IR point, spot corrections
|
||||
│ └── menu.lua -- F10 JTAC menus
|
||||
├── ui/
|
||||
│ ├── f10_menu.lua -- F10 menu construction
|
||||
│ ├── messages.lua -- Player messages (displayMessageToGroup)
|
||||
│ └── smoke.lua -- Smoke markers
|
||||
├── recon/
|
||||
│ └── recon.lua -- Target recognition on F10 map
|
||||
├── ai/
|
||||
│ └── ai_transport.lua -- AI auto-load/unload logic
|
||||
├── compat/
|
||||
│ └── legacy_api.lua -- Deprecated ctld.* wrappers
|
||||
├── mist_compat/
|
||||
│ └── mist_middleware.lua -- MIST middleware (see Phase 3)
|
||||
└── ctld.lua -- Entry point, initialization, orchestration
|
||||
```
|
||||
|
||||
#### 2.3 — Core classes
|
||||
|
||||
| Class | Responsibility | Replaces |
|
||||
| ----- | -------------- | -------- |
|
||||
| `Coalition` | Encapsulates side (1/2) and per-coalition state | `ctld.spawnedCratesRED/BLUE`, `ctld.droppedTroopsRED/BLUE`, etc. (34 tables → 2 instances) |
|
||||
| `Transport` | State of a transport helicopter/plane | `ctld.inTransitTroops[name]`, `ctld.hoverStatus[name]` |
|
||||
| `Crate` | Represents a logistics crate | Entries from `ctld.spawnableCrates`, `ctld.crateLookupTable` |
|
||||
| `CrateManager` | Spawn, pickup, drop, unpack crates | `ctld.spawnCrate`, `ctld.unpackCrates`, `ctld.checkHoverStatus` |
|
||||
| `FOB` | A built Forward Operating Base | `ctld.builtFOBS[name]` |
|
||||
| `JTACController` | An active JTAC with its lasing state | `ctld.jtacUnits`, `ctld.jtacCurrentTargets`, `ctld.jtacSelectedTarget` |
|
||||
| `Beacon` | A deployed radio beacon | `ctld.deployedRadioBeacons[name]` |
|
||||
| `StateManager` | Central registry of all active objects | Replaces 34 global tables |
|
||||
| `Config` | Typed user configuration | Current USER CONFIGURATION section |
|
||||
|
||||
#### 2.4 — Eliminating RED/BLUE duplication
|
||||
|
||||
Before (50+ branches):
|
||||
|
||||
```lua
|
||||
if _heli:getCoalition() == 1 then
|
||||
_extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED)
|
||||
else
|
||||
_extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE)
|
||||
end
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```lua
|
||||
local side = stateManager:getCoalition(heli:getCoalition())
|
||||
local extract = side:findNearestDroppedTroop(heli)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3 — MIST middleware
|
||||
|
||||
**Goal**: Isolate MIST behind a facade. CTLD never references `mist.*` directly.
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 3.1 | Inventory the 91 MIST calls and categorize them (trivial / medium / complex) |
|
||||
| 3.2 | Create `src/mist_compat/mist_middleware.lua` exposing a clean API |
|
||||
| 3.3 | Rewrite trivial functions: `deepCopy`, `get2DDist`, `round`, `makeVec2/3` |
|
||||
| 3.4 | Wrap medium functions: `getHeading`, `vec.*`, `tostringLL/MGRS` |
|
||||
| 3.5 | Delegate complex functions to MIST: `dynAdd`, `dynAddStatic`, `getUnitsLOS` |
|
||||
| 3.6 | Replace all `mist.*` calls in modules with middleware calls |
|
||||
| 3.7 | (Long term) Rewrite complex functions to eliminate MIST entirely |
|
||||
|
||||
**Deliverable**: CTLD works with or without MIST (graceful degradation when MIST is absent, eventually).
|
||||
|
||||
---
|
||||
|
||||
### Phase 4 — Legacy API compatibility layer
|
||||
|
||||
**Goal**: Existing missions continue to work without modification.
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 4.1 | List all public `ctld.*` functions documented in the README |
|
||||
| 4.2 | Create `src/compat/legacy_api.lua` with wrappers that call the new API |
|
||||
| 4.3 | Each wrapper logs a deprecated warning with the new API name |
|
||||
| 4.4 | Document old → new migration in `docs/migration-v2.md` |
|
||||
| 4.5 | Announce the policy: legacy maintained in v2, removed in v3 |
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
-- legacy_api.lua
|
||||
function ctld.spawnCrateAtZone(_side, _weight, _zone)
|
||||
ctld.logWarning("DEPRECATED: ctld.spawnCrateAtZone() -> use CrateManager:spawnAtZone()")
|
||||
local side = (_side == "red") and 1 or 2
|
||||
return ctld.crateManager:spawnAtZone(side, _weight, _zone)
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 5 — Unit tests
|
||||
|
||||
**Goal**: Coverage of pure logic outside DCS + in-game smoke tests.
|
||||
|
||||
#### 5.1 — Framework and mocks
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 5.1.1 | Set up busted (Lua 5.1) + luarocks in the repo |
|
||||
| 5.1.2 | Create `test/mocks/dcs_env.lua`: stubs for `trigger`, `Unit`, `Group`, `coalition`, `land`, `timer`, `missionCommands`, `coord`, `world`, `env` |
|
||||
| 5.1.3 | Create `test/mocks/mist_env.lua`: stubs for MIST functions used |
|
||||
| 5.1.4 | Configure busted to load mocks before tests |
|
||||
|
||||
#### 5.2 — Unit tests (outside DCS)
|
||||
|
||||
| Module | Priority tests |
|
||||
| ------ | -------------- |
|
||||
| `lib/class.lua` | Inheritance, instantiation, override |
|
||||
| `lib/vec.lua` | 2D/3D distance, bearing, clock direction |
|
||||
| `lib/utils.lua` | deepCopy, formatText |
|
||||
| `core/coalition.lua` | Per-coalition state management, lookup |
|
||||
| `core/i18n.lua` | Translation, fallback, parameters |
|
||||
| `core/config.lua` | Default values, override |
|
||||
| `logistics/crate.lua` | Weight uniqueness, lookup |
|
||||
| `compat/legacy_api.lua` | Wrappers call the correct new API |
|
||||
|
||||
#### 5.3 — In-game tests
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 5.3.1 | Create an automated test mission (DO SCRIPT running a test suite) |
|
||||
| 5.3.2 | Write results to a file via `io.open` (desanitized server) |
|
||||
| 5.3.3 | Validate: troop load/unload, crate spawn/unpack, JTAC lasing, FOB build, beacons |
|
||||
|
||||
---
|
||||
|
||||
### Phase 6 — CI infrastructure
|
||||
|
||||
**Goal**: Automate build, tests, and release.
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 6.1 | **Build script**: Lua (or Python/Bash) script that concatenates `src/**/*.lua` → `dist/CTLD.lua` in the correct order |
|
||||
| 6.2 | **GitHub Actions — Tests**: install Lua 5.1 + luarocks + busted, run `busted test/` |
|
||||
| 6.3 | **GitHub Actions — Build**: generate `dist/CTLD.lua` and `dist/CTLD-i18n.lua` |
|
||||
| 6.4 | **GitHub Actions — Release**: on tag, publish the `.lua` artifact as a GitHub release |
|
||||
| 6.5 | **GitHub Actions — Docs**: MkDocs build + deploy to GitHub Pages |
|
||||
| 6.6 | **i18n lint**: CI script comparing keys across languages and checking `translation_version` |
|
||||
|
||||
---
|
||||
|
||||
### Phase 7 — i18n cleanup + tooling
|
||||
|
||||
**Goal**: Reliable i18n with automatic drift detection.
|
||||
|
||||
| Task | Detail |
|
||||
| ---- | ------ |
|
||||
| 7.1 | Full audit: list EN vs FR vs ES vs KO keys, identify missing ones |
|
||||
| 7.2 | Fill empty FR keys (crate/equipment names) |
|
||||
| 7.3 | Update KO (1.1 → 1.6+) or mark it as `incomplete` |
|
||||
| 7.4 | CI lint script for i18n (see 6.6) |
|
||||
| 7.5 | Document the process of adding a language in `docs/contributing/i18n.md` |
|
||||
| 7.6 | Consider extracting i18n into per-language files (`i18n/fr.lua`, `i18n/ko.lua`) |
|
||||
|
||||
---
|
||||
|
||||
### Phase 8 — Documentation
|
||||
|
||||
**Goal**: 3 guides for 3 audiences, auto-published.
|
||||
|
||||
#### 8.1 — `docs/` structure
|
||||
|
||||
```text
|
||||
docs/
|
||||
├── index.md -- Landing page
|
||||
├── user-guide/
|
||||
│ ├── getting-started.md -- For the player: F10 menus, in-game workflow
|
||||
│ ├── troops.md -- Troop loading/unloading
|
||||
│ ├── crates.md -- Crate system
|
||||
│ ├── jtac.md -- Using JTACs
|
||||
│ ├── fob.md -- FOB construction
|
||||
│ └── beacons.md -- Radio beacons
|
||||
├── mission-maker/
|
||||
│ ├── setup.md -- Mission installation
|
||||
│ ├── configuration.md -- All configurable options
|
||||
│ ├── api-reference.md -- Public API (DO SCRIPT)
|
||||
│ ├── zones.md -- Pickup/dropoff/waypoint zones
|
||||
│ ├── jtac-setup.md -- Advanced JTAC configuration
|
||||
│ └── examples.md -- Mission examples
|
||||
├── developer/
|
||||
│ ├── architecture.md -- Module, class, and flow diagrams
|
||||
│ ├── contributing.md -- How to contribute
|
||||
│ ├── building.md -- Build script, CI
|
||||
│ ├── testing.md -- Running tests
|
||||
│ ├── i18n.md -- Adding/maintaining a translation
|
||||
│ └── migration-v2.md -- v1 → v2 migration guide
|
||||
└── mkdocs.yml -- MkDocs configuration
|
||||
```
|
||||
|
||||
#### 8.2 — Publishing
|
||||
|
||||
- MkDocs Material theme
|
||||
- GitHub Actions: build + deploy to `gh-pages` on every push to `next`
|
||||
- Slimmed-down README.md: links to the full documentation site
|
||||
|
||||
---
|
||||
|
||||
## Indicative timeline
|
||||
|
||||
| Phase | Dependencies | Complexity |
|
||||
| ----- | ------------ | ---------- |
|
||||
| 1 — Dead code cleanup | None | Low |
|
||||
| 2 — Modules + OOP | Phase 1 | **High** |
|
||||
| 3 — MIST middleware | Phase 2 | Medium |
|
||||
| 4 — Legacy compat | Phase 2 | Medium |
|
||||
| 5 — Tests | Phases 2-3 | Medium |
|
||||
| 6 — CI | Phases 2, 5 | Medium |
|
||||
| 7 — i18n | Phase 2 | Low |
|
||||
| 8 — Documentation | Phases 2-4 | Medium |
|
||||
|
||||
> Phases 3 and 4 can be worked on in parallel.
|
||||
> Phase 6 can start as early as Phase 2 (build script) and grow incrementally.
|
||||
|
||||
---
|
||||
|
||||
## Branching strategy
|
||||
|
||||
```text
|
||||
main (stable, v1.x)
|
||||
└── next (v2 development)
|
||||
├── feature/phase1-cleanup
|
||||
├── feature/phase2-oop-core
|
||||
├── feature/phase2-oop-jtac
|
||||
├── feature/phase3-mist-middleware
|
||||
├── feature/phase4-legacy-compat
|
||||
├── feature/phase5-tests
|
||||
└── ...
|
||||
```
|
||||
|
||||
- `main`: stable v1.x releases, urgent hotfixes only.
|
||||
- `next`: v2 integration, git flow (feature → next → release).
|
||||
- Tags: `v2.0-alpha.1`, `v2.0-beta.1`, `v2.0-rc.1`, `v2.0`.
|
||||
|
||||
---
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
| ---- | ------ | ---------- |
|
||||
| Gameplay regressions after OOP refactoring | High | Unit tests + systematic test missions |
|
||||
| DCS mock divergence from real API | Medium | In-game tests as complement, versioned mocks |
|
||||
| Incomplete MIST middleware | Medium | Progressive approach, MIST remains as fallback |
|
||||
| Mission maker resistance to API changes | Medium | Legacy layer + communication + migration guide |
|
||||
| Build script complexity (module ordering) | Low | Explicit dependencies, build tested in CI |
|
||||
@@ -0,0 +1,75 @@
|
||||
# Copilot Instructions for DCS-CTLD
|
||||
|
||||
## Working mode / Mode de travail
|
||||
|
||||
- **Communication language**: French (français) for all conversations and commit messages.
|
||||
- **Documentation language**: English for all technical documentation (code comments, docs/, README). French summaries in docs are acceptable but English is the primary language.
|
||||
- **Development methodology**: TDD (Test-Driven Development) — write tests before implementation when possible.
|
||||
- **Programming paradigm**: OOP Lua 5.1 with metatables and classes. No procedural spaghetti.
|
||||
- **Branching strategy**: Git flow on the `next` branch. Feature branches named `feature/<phase>-<description>`. Never commit directly to `main` or `next`.
|
||||
- **Commit style**: Conventional Commits (`feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`).
|
||||
- **Build target**: Individual source modules in `src/` are concatenated into a single `dist/CTLD.lua` deliverable by a build script. Never edit the built file directly.
|
||||
- **MIST dependency**: CTLD code must never call `mist.*` directly. All MIST usage goes through the middleware layer (`src/mist_compat/`).
|
||||
- **Legacy API**: When refactoring a public `ctld.*` function, always create a deprecated wrapper in `src/compat/legacy_api.lua` that logs a warning and delegates to the new API.
|
||||
- **Refer to the modernization plan**: See `.github/MODERNIZATION-PLAN.md` for the full roadmap and architectural decisions.
|
||||
|
||||
## Project context
|
||||
- This repository contains Lua mission scripts for DCS World.
|
||||
- The primary script is CTLD.lua.
|
||||
- Internationalization tables are split between CTLD.lua (English reference keys) and CTLD-i18n.lua (translated values).
|
||||
- Runtime is DCS mission scripting environment (Lua 5.1, metatables available, no LuaJIT, no goto).
|
||||
- Assumes desanitized server (io, os, lfs accessible).
|
||||
- MIST is being progressively replaced by an internal middleware layer.
|
||||
|
||||
## Architecture (v2 target)
|
||||
|
||||
- Source modules live in `src/` organized by domain: `lib/`, `core/`, `transport/`, `logistics/`, `jtac/`, `ui/`, `recon/`, `ai/`, `compat/`, `mist_compat/`.
|
||||
- OOP via a micro class system using metatables (`src/lib/class.lua`).
|
||||
- State is managed by a `StateManager` singleton and per-coalition `Coalition` instances — no more `ctld.xxxRED`/`ctld.xxxBLUE` table pairs.
|
||||
- Tests live in `test/` and use busted with DCS/MIST mocks.
|
||||
|
||||
## Compatibility (v1 legacy)
|
||||
- Preserve existing public ctld.* APIs used by Mission Editor DO SCRIPT triggers.
|
||||
- Do not rename exported ctld functions or change their parameter semantics unless explicitly requested.
|
||||
- Keep behavior compatible with existing .miz missions in this repository.
|
||||
- Keep script load order assumptions intact: MIST first, CTLD-i18n.lua before CTLD.lua.
|
||||
|
||||
## CTLD.lua editing rules
|
||||
- Keep user-tunable options in the USER CONFIGURATION section with clear comments and safe defaults.
|
||||
- Keep coalition conventions consistent:
|
||||
- 1 = red
|
||||
- 2 = blue
|
||||
- 0 = both (where applicable)
|
||||
- Keep pickup/dropoff/waypoint zone tuple formats compatible with current parser logic.
|
||||
- Keep crate weight values unique in ctld.spawnableCrates.
|
||||
- Preserve callback behavior and action names used by ctld.processCallback.
|
||||
- Use defensive nil checks before calling methods on DCS objects (Unit/Group/zone lookups).
|
||||
- Prefer existing logging patterns (ctld.logInfo, env.info, env.error) over ad-hoc prints.
|
||||
- Avoid adding heavy polling loops; prefer bounded searches and scheduled checks.
|
||||
|
||||
## I18N rules
|
||||
- For every new player-visible string, add an English reference key and use ctld.i18n_translate at call sites.
|
||||
- Do not hardcode new menu or gameplay text without i18n_translate.
|
||||
- When adding keys, keep translation_version alignment in mind and add placeholders in CTLD-i18n.lua tables.
|
||||
- Do not remove or rename existing i18n keys unless all language tables are updated.
|
||||
|
||||
## Testing expectations
|
||||
- Prefer the dynamic loading workflow documented in README for fast iteration.
|
||||
- After gameplay logic changes, validate at least:
|
||||
- troop load/unload flows
|
||||
- crate spawn/load/unpack flows
|
||||
- JTAC-related flows (if touched)
|
||||
- Use repository missions for smoke tests when relevant:
|
||||
- test-dev-dynamic.miz
|
||||
- test-dev-static.miz
|
||||
- test-mission.miz
|
||||
- If user-facing behavior or configuration changes, update README examples in the same change.
|
||||
|
||||
## Code style conventions
|
||||
|
||||
- Follow existing file style and indentation.
|
||||
- OOP Lua 5.1: use the project class system (`src/lib/class.lua`) for new classes.
|
||||
- For internal helpers, follow existing naming patterns (local variables often prefixed with _).
|
||||
- Keep mission-designer guidance comments concise and practical.
|
||||
- No direct `mist.*` calls — use the middleware.
|
||||
- Every new public function should have at least one unit test.
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Lua.diagnostics.globals": [
|
||||
"ctld"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
--[[
|
||||
--[[ ! IMPORTANT : You must must use the version of MIST supplied in the CTLD pack to correctly manage dynamic spwans
|
||||
|
||||
Combat Troop and Logistics Drop
|
||||
|
||||
Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads
|
||||
@@ -40,7 +41,7 @@ end
|
||||
ctld.Id = "CTLD - "
|
||||
|
||||
--- Version.
|
||||
ctld.Version = "1.5.2"
|
||||
ctld.Version = "1.6.0-alpha"
|
||||
|
||||
-- To add debugging messages to dcs.log, change the following log levels to `true`; `Debug` is less detailed than `Trace`
|
||||
ctld.Debug = false
|
||||
@@ -817,7 +818,9 @@ ctld.vehicleTransportEnabled = {
|
||||
-- We will also use this to make modifications to the menu and other checks and messages
|
||||
ctld.dynamicCargoUnits = {
|
||||
"CH-47Fbl1",
|
||||
--"UH-1H",
|
||||
"UH-1H",
|
||||
"Mi-8MT",
|
||||
"Mi-24P",
|
||||
}
|
||||
|
||||
-- ************** Maximum Units SETUP for UNITS ******************
|
||||
@@ -888,6 +891,7 @@ ctld.internalCargoLimits = {
|
||||
-- Remove the -- below to turn on options
|
||||
["Mi-8MT"] = 2,
|
||||
["CH-47Fbl1"] = 8,
|
||||
["UH-1H"] = 3, -- to remove after debug
|
||||
}
|
||||
|
||||
|
||||
@@ -2617,10 +2621,19 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime)
|
||||
|
||||
-- add to move table
|
||||
ctld.crateMove[_name] = _name
|
||||
|
||||
local refPoint = _heli:getPoint()
|
||||
local refLat, refLon = coord.LOtoLL(refPoint)
|
||||
local unitPos = _heli:getPosition()
|
||||
local refHeading = math.deg(math.atan2(unitPos.x.z, unitPos.x.x))
|
||||
|
||||
local destLat, destLon, destAlt = coord.LOtoLL(_point)
|
||||
|
||||
local relativePos, forma = ctld.tools.getRelativeBearing(refLat, refLon, refHeading, destLat, destLon, 'clock')
|
||||
|
||||
ctld.displayMessageToGroup(_heli,
|
||||
ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ",
|
||||
_crateType.desc, _crateType.weight, _position), 20)
|
||||
_crateType.desc, _crateType.weight, relativePos), 20)
|
||||
else
|
||||
env.info("Couldn't find crate item to spawn")
|
||||
end
|
||||
@@ -4223,8 +4236,7 @@ function ctld.dropSlingCrate(_args)
|
||||
_position = "6"
|
||||
end
|
||||
ctld.displayMessageToGroup(_heli,
|
||||
ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc,
|
||||
_position), 10)
|
||||
ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, _position), 10)
|
||||
elseif _heightDiff > 7.5 and _heightDiff <= 40.0 then
|
||||
ctld.displayMessageToGroup(_heli,
|
||||
ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10)
|
||||
@@ -5746,7 +5758,7 @@ function ctld.unitDynamicCargoCapable(_unit)
|
||||
--ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type))
|
||||
for _, _name in ipairs(ctld.dynamicCargoUnits) do
|
||||
local _nameLower = string.lower(_name)
|
||||
if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character
|
||||
if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character
|
||||
result = true
|
||||
break
|
||||
end
|
||||
@@ -8540,7 +8552,7 @@ function ctld.eventHandler:onEvent(event)
|
||||
for _, aircraftType in pairs(ctld.aircraftTypeTable) do
|
||||
if aircraftType == playerTypeName then
|
||||
ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName))
|
||||
if ctld.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then
|
||||
if ctld.tools.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then
|
||||
table.insert(ctld.transportPilotNames, unitName) -- add transport unit to the list
|
||||
end
|
||||
if ctld.addedTo[tostring(_groupId)] == nil then -- only if menu not already set up
|
||||
@@ -8618,7 +8630,8 @@ function ctld.RandomReal(mini, maxi)
|
||||
end
|
||||
|
||||
-- Tools
|
||||
function ctld.isValueInIpairTable(tab, value)
|
||||
ctld.tools = {}
|
||||
function ctld.tools.isValueInIpairTable(tab, value)
|
||||
for i, v in ipairs(tab) do
|
||||
if v == value then
|
||||
return true -- La valeur existe
|
||||
@@ -8626,6 +8639,58 @@ function ctld.isValueInIpairTable(tab, value)
|
||||
end
|
||||
return false -- La valeur n'existe pas
|
||||
end
|
||||
------------------------------------------------------------------------------------
|
||||
--- Calculates the orientation of an end point relative to a reference point.
|
||||
--- The calculation takes into account the current orientation of the reference point.
|
||||
---
|
||||
--- @param refLat number Latitude of the reference point in degrees.
|
||||
--- @param refLon number Longitude of the reference point in degrees.
|
||||
--- @param refHeading number Current orientation of the reference point in degrees (0 = North, 90 = East).
|
||||
--- @param destLat number Latitude of the arrival point in degrees.
|
||||
--- @param destLon number Longitude of the arrival point in degrees.
|
||||
--- @param resultFormat string The desired output format: "radian", "degree" or "clock".
|
||||
--- @return number The relative orientation in the specified resultFormat.
|
||||
function ctld.tools.getRelativeBearing(refLat, refLon, refHeading, destLat, destLon, resultFormat)
|
||||
-- Converting degrees to radians for geometric calculations
|
||||
local radrefLat = math.rad(refLat)
|
||||
local raddestLat = math.rad(destLat)
|
||||
local radrefLon = math.rad(refLon)
|
||||
local raddestLon = math.rad(destLon)
|
||||
local radrefHeading = math.rad(refHeading)
|
||||
|
||||
-- Calculating the longitude difference between the two points
|
||||
local deltaLon = raddestLon - radrefLon
|
||||
|
||||
-- Using the great circle formula for azimuth (bearing)
|
||||
-- This formula is based on spherical trigonometry and uses atan2
|
||||
-- to correctly handle all quadrants.
|
||||
local y = math.sin(deltaLon) * math.cos(raddestLat)
|
||||
local x = math.cos(radrefLat) * math.sin(raddestLat) - math.sin(radrefLat) * math.cos(raddestLat) * math.cos(deltaLon)
|
||||
local absoluteBearingRad = math.atan2(y, x)
|
||||
|
||||
-- Calculate relative orientation by subtracting the reference refHeading
|
||||
local relativeBearingRad = absoluteBearingRad - radrefHeading
|
||||
|
||||
-- Normalizes the angle to be in the range [-pi, pi]
|
||||
-- This ensures a consistent angle, whether positive or negative.
|
||||
local normalizedRad = (relativeBearingRad + math.pi) % (2 * math.pi) - math.pi
|
||||
|
||||
-- Returns the value in the requested resultFormat
|
||||
if resultFormat == "radian" then
|
||||
return normalizedRad, resultFormat
|
||||
elseif resultFormat == "clock" then
|
||||
-- Convert to clock position (12h = front, 3h = right, 6h = back, etc..)
|
||||
local bearingDeg = math.deg(normalizedRad)
|
||||
local clockPosition = ((bearingDeg + 360) % 360) / 30
|
||||
clockPosition = clockPosition >= 0 and math.floor(clockPosition + 0.5) or math.ceil(clockPosition - 0.5), resultFormat -- rounded clockPosition
|
||||
if clockPosition == 0 then clockPosition = 12 end
|
||||
return clockPosition, resultFormat
|
||||
else -- By default, the resultFormat is "degree"
|
||||
resultFormat = "degree"
|
||||
local bearingDeg = math.deg(normalizedRad)
|
||||
return (bearingDeg + 360) % 360, resultFormat
|
||||
end
|
||||
end
|
||||
|
||||
--- Enable/Disable error boxes displayed on screen.
|
||||
env.setErrorMessageBoxEnabled(false)
|
||||
|
||||
@@ -132,7 +132,7 @@ Optionaly, you can disable the STTS (text to speech over SRS) feature (4).
|
||||
![dynamic_loading]
|
||||
|
||||
### Script Setup
|
||||
**This script requires MIST version 4.0.57 or above: https://github.com/mrSkortch/MissionScriptingTools**
|
||||
🔴IMPORTANT: This script requires MIST supplied in the CTLD zipped pack to correctly manage dynamic spwans.
|
||||
|
||||
First make sure MIST is loaded, either as an Initialization Script for the mission or the first DO SCRIPT with a "TIME MORE" of 1. "TIME MORE" means run the actions after X seconds into the mission.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ mist = {}
|
||||
-- don't change these
|
||||
mist.majorVersion = 4
|
||||
mist.minorVersion = 5
|
||||
mist.build = "128-DYNSLOTS-01"
|
||||
mist.build = "128-DYNSLOTS-02"
|
||||
|
||||
-- forward declaration of log shorthand
|
||||
local log
|
||||
@@ -826,11 +826,11 @@ do -- the main scope
|
||||
if zone.verticies then
|
||||
local offset = {}
|
||||
for i = 1, #zone.verticies do
|
||||
table.insert(offset, {dist = mist.utils.get2DDist(uRef.point, zone.verticies[i]), heading = mist.getHeadingPoints(uRef.point, zone.verticies[i]) + uRef.heading})
|
||||
table.insert(offset, {dist = mist.utils.get2DDist(uRef.point, zone.verticies[i]), heading = mist.utils.getHeadingPoints(uRef.point, zone.verticies[i]) + uRef.heading})
|
||||
end
|
||||
zone.offset = offset
|
||||
else
|
||||
zone.offset = {dist = mist.utils.get2DDist(uRef.point, zone.point), heading = mist.getHeadingPoints(uRef.point, zone.point) + uRef.heading}
|
||||
zone.offset = {dist = mist.utils.get2DDist(uRef.point, zone.point), heading = mist.utils.getHeadingPoints(uRef.point, zone.point) + uRef.heading}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user