Renamed files

This commit is contained in:
dpassoni 2023-03-16 15:34:35 +01:00
parent 64df9b142d
commit 53911cf314
51 changed files with 2483 additions and 60 deletions

View File

@ -1,6 +1,6 @@
import { AICFormationContextDataInterface, AICFormationDescriptor } from "./AICFormationDescriptor";
import { AICFormationDescriptorPhrase } from "./AICFormationDescriptorPhrase";
import { AICFormationDescriptorSection } from "./AICFormationDescriptorSection";
import { AICFormationContextDataInterface, AICFormationDescriptor } from "./aicformationdescriptor";
import { AICFormationDescriptorPhrase } from "./aicformationdescriptorphrase";
import { AICFormationDescriptorSection } from "./aicformationdescriptorsection";
export interface AICFormationInterface {
"icon" : string,

View File

@ -1,8 +1,8 @@
import { AICFormation, AICFormationInterface } from "../AICFormation";
import { AICFormationContextDataInterface } from "../AICFormationDescriptor";
import { AICFormationDescriptorSection } from "../AICFormationDescriptorSection";
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorPhrase } from "../AICFormationDescriptorPhrase";
import { AICFormation, AICFormationInterface } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormation_Azimuth extends AICFormation implements AICFormationInterface {

View File

@ -1,8 +1,8 @@
import { AICFormation, AICFormationInterface } from "../AICFormation";
import { AICFormationContextDataInterface } from "../AICFormationDescriptor";
import { AICFormationDescriptorSection } from "../AICFormationDescriptorSection";
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorPhrase } from "../AICFormationDescriptorPhrase";
import { AICFormation, AICFormationInterface } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormation_Range extends AICFormation implements AICFormationInterface {

View File

@ -1,5 +1,5 @@
import { AICFormation, AICFormationInterface } from "../AICFormation";
import { AICFormationContextDataInterface, AICFormationDescriptor } from "../AICFormationDescriptor";
import { AICFormation, AICFormationInterface } from "../aicformation";
import { AICFormationContextDataInterface, AICFormationDescriptor } from "../aicformationdescriptor";
export class AICFormation_Single extends AICFormation implements AICFormationInterface {

View File

@ -1,9 +1,9 @@
import { AICFormation } from "./AICFormation";
import { AICFormationDescriptorSection } from "./AICFormationDescriptorSection";
import { AICFormationDescriptorSection_Formation } from "./AICFormationDescriptorSection/Formation";
import { AICFormationDescriptorSection_Unit } from "./AICFormationDescriptorSection/Unit";
import { AICFormationDescriptorSection_NumGroups } from "./AICFormationDescriptorSection/NumGroups";
import { AICFormationDescriptorSection_Who } from "./AICFormationDescriptorSection/Who";
import { AICFormation } from "./aicformation";
import { AICFormationDescriptorSection } from "./aicformationdescriptorsection";
import { AICFormationDescriptorSection_Formation } from "./aicformationdescriptorsection/formation";
import { AICFormationDescriptorSection_Unit } from "./aicformationdescriptorsection/unit";
import { AICFormationDescriptorSection_NumGroups } from "./aicformationdescriptorsection/numgroups";
import { AICFormationDescriptorSection_Who } from "./aicformationdescriptorsection/who";
export interface AICFormationContextDataInterface {

View File

@ -1,4 +1,4 @@
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
export abstract class AICFormactionDescriptorComponent_Distance extends AICFormationDescriptorComponent {

View File

@ -1,4 +1,4 @@
import { AICFormactionDescriptorComponent_Distance } from "../Distance";
import { AICFormactionDescriptorComponent_Distance } from "../distance";
export class AICFormationDescriptorComponent_Distance_Range extends AICFormactionDescriptorComponent_Distance {

View File

@ -1,6 +1,6 @@
import { AICFormation } from "./AICFormation";
import { AICFormationContextDataInterface } from "./AICFormationDescriptor";
import { AICFormationDescriptorComponent } from "./AICFormationDescriptorComponent";
import { AICFormation } from "./aicformation";
import { AICFormationContextDataInterface } from "./aicformationdescriptor";
import { AICFormationDescriptorComponent } from "./aicformationdescriptorcomponent";
export interface AICFormationDescriptorPhraseInterface {
"generate" : CallableFunction,

View File

@ -1,6 +1,6 @@
import { AICFormation } from "./AICFormation";
import { AICFormationContextDataInterface } from "./AICFormationDescriptor";
import { AICFormationDescriptorPhrase } from "./AICFormationDescriptorPhrase";
import { AICFormation } from "./aicformation";
import { AICFormationContextDataInterface } from "./aicformationdescriptor";
import { AICFormationDescriptorPhrase } from "./aicformationdescriptorphrase";
export interface AICFormationDescriptorSectionInterface {
"generate" : CallableFunction,

View File

@ -1,8 +1,8 @@
import { AICFormation } from "../AICFormation";
import { AICFormationContextDataInterface } from "../AICFormationDescriptor";
import { AICFormationDescriptorSection } from "../AICFormationDescriptorSection";
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorPhrase } from "../AICFormationDescriptorPhrase";
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormationDescriptorSection_Formation extends AICFormationDescriptorSection {

View File

@ -1,8 +1,8 @@
import { AICFormation } from "../AICFormation";
import { AICFormationContextDataInterface } from "../AICFormationDescriptor";
import { AICFormationDescriptorSection } from "../AICFormationDescriptorSection";
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorPhrase } from "../AICFormationDescriptorPhrase";
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormationDescriptorSection_NumGroups extends AICFormationDescriptorSection {

View File

@ -1,8 +1,8 @@
import { AICFormation } from "../AICFormation";
import { AICFormationContextDataInterface } from "../AICFormationDescriptor";
import { AICFormationDescriptorSection } from "../AICFormationDescriptorSection";
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorPhrase } from "../AICFormationDescriptorPhrase";
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
interface addUnitInformationInterface {
omitTrack?: boolean

View File

@ -1,8 +1,8 @@
import { AICFormation } from "../AICFormation";
import { AICFormationContextDataInterface } from "../AICFormationDescriptor";
import { AICFormationDescriptorSection } from "../AICFormationDescriptorSection";
import { AICFormationDescriptorComponent } from "../AICFormationDescriptorComponent";
import { AICFormationDescriptorPhrase } from "../AICFormationDescriptorPhrase";
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormationDescriptorSection_Who extends AICFormationDescriptorSection {

View File

@ -1,8 +1,8 @@
import { ToggleableFeature } from "../ToggleableFeature";
import { AICFormation_Azimuth } from "./AICFormation/Azimuth";
import { AICFormation_Range } from "./AICFormation/Range";
import { AICFormation_Single } from "./AICFormation/Single";
import { AICFormationDescriptorSection } from "./AICFormationDescriptorSection";
import { ToggleableFeature } from "../toggleablefeature";
import { AICFormation_Azimuth } from "./aicformation/azimuth";
import { AICFormation_Range } from "./aicformation/range";
import { AICFormation_Single } from "./aicformation/single";
import { AICFormationDescriptorSection } from "./aicformationdescriptorsection";
export class AIC extends ToggleableFeature {

View File

@ -0,0 +1,54 @@
import { AICFormationContextDataInterface, AICFormationDescriptor } from "./aicformationdescriptor";
import { AICFormationDescriptorPhrase } from "./aicformationdescriptorphrase";
import { AICFormationDescriptorSection } from "./aicformationdescriptorsection";
export interface AICFormationInterface {
"icon" : string,
"label" : string,
"name" : string,
"numGroups" : number,
"summary" : string,
"unitBreakdown" : string[]
}
export abstract class AICFormation {
"icon" = "";
"label" = "";
"name" = "";
"numGroups" = 1;
"summary" = "";
"unitBreakdown":string[] = []
constructor() {
this.unitBreakdown = [];
}
addToDescriptorPhrase( section: AICFormationDescriptorSection, phrase: AICFormationDescriptorPhrase, contextData: AICFormationContextDataInterface ) {
return phrase;
}
getDescriptor( contextData: AICFormationContextDataInterface ) {
return new AICFormationDescriptor().generate( this, contextData );
}
hasUnitBreakdown() {
return this.unitBreakdown.length > 0;
}
showFormationNameInDescriptor() {
return true;
}
}

View File

@ -0,0 +1,38 @@
import { AICFormation, AICFormationInterface } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormation_Azimuth extends AICFormation implements AICFormationInterface {
"icon" = "azimuth.png";
"label" = "Azimuth";
"name" = "azimuth";
"numGroups" = 2;
"summary" = "Two contacts, side-by-side in a line perpedicular to the perspective.";
"unitBreakdown" = [ "<compass> group", "<compass> group" ];
constructor() {
super();
}
addToDescriptorPhrase( section: AICFormationDescriptorSection, phrase: AICFormationDescriptorPhrase, contextData: AICFormationContextDataInterface ) {
switch ( section.name ) {
case "formation":
phrase.addComponent( new AICFormationDescriptorComponent( "<distance>" ) );
phrase.addComponent( new AICFormationDescriptorComponent( "track <compass>" ) );
}
return phrase;
}
}

View File

@ -0,0 +1,38 @@
import { AICFormation, AICFormationInterface } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormation_Range extends AICFormation implements AICFormationInterface {
"icon" = "range.png";
"label" = "Range";
"name" = "range";
"numGroups" = 2;
"summary" = "Two contacts, one behind the other";
"unitBreakdown" = [ "Lead group", "Trail group" ];
constructor() {
super();
}
addToDescriptorPhrase( section: AICFormationDescriptorSection, phrase: AICFormationDescriptorPhrase, contextData: AICFormationContextDataInterface ) {
switch ( section.name ) {
case "formation":
phrase.addComponent( new AICFormationDescriptorComponent( "<distance>" ) );
phrase.addComponent( new AICFormationDescriptorComponent( "track <compass>" ) );
}
return phrase;
}
}

View File

@ -0,0 +1,24 @@
import { AICFormation, AICFormationInterface } from "../aicformation";
import { AICFormationContextDataInterface, AICFormationDescriptor } from "../aicformationdescriptor";
export class AICFormation_Single extends AICFormation implements AICFormationInterface {
"icon" = "single.png";
"label" = "Single";
"name" = "single";
"numGroups" = 1;
"summary" = "One contact on its own";
"unitBreakdown" = [];
constructor() {
super();
}
showFormationNameInDescriptor() {
return false;
}
}

View File

@ -0,0 +1,55 @@
import { AICFormation } from "./aicformation";
import { AICFormationDescriptorSection } from "./aicformationdescriptorsection";
import { AICFormationDescriptorSection_Formation } from "./aicformationdescriptorsection/formation";
import { AICFormationDescriptorSection_Unit } from "./aicformationdescriptorsection/unit";
import { AICFormationDescriptorSection_NumGroups } from "./aicformationdescriptorsection/numgroups";
import { AICFormationDescriptorSection_Who } from "./aicformationdescriptorsection/who";
export interface AICFormationContextDataInterface {
"aicCallsign" : string,
"bullseyeName" : string,
"control" : "broadcast" | "tactical",
"numGroups" : number
}
export class AICFormationDescriptor {
#sections:AICFormationDescriptorSection[] = [
new AICFormationDescriptorSection_Who(),
new AICFormationDescriptorSection_NumGroups(),
new AICFormationDescriptorSection_Formation(),
new AICFormationDescriptorSection_Unit()
]
constructor() {
}
addSection( section:AICFormationDescriptorSection ) {
this.#sections.push( section );
}
getSections() {
return this.#sections;
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
let output:object[] = [];
for ( const section of this.#sections ) {
output.push(
section.generate( formation, contextData )
);
}
return output;
}
}

View File

@ -0,0 +1,18 @@
interface ComponentInterface {
"label" : string;
"value" : string;
}
export class AICFormationDescriptorComponent implements ComponentInterface {
label = "(not set)";
value = "(not set)";
constructor( value:any, label?:string ) {
this.label = label || "(not set)";
this.value = value;
}
}

View File

@ -0,0 +1,9 @@
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
export abstract class AICFormactionDescriptorComponent_Distance extends AICFormationDescriptorComponent {
constructor( value:string, label?:string ) {
super( value, label );
}
}

View File

@ -0,0 +1,9 @@
import { AICFormactionDescriptorComponent_Distance } from "../distance";
export class AICFormationDescriptorComponent_Distance_Range extends AICFormactionDescriptorComponent_Distance {
constructor( value:string, label?:string ) {
super( value, label );
}
}

View File

@ -0,0 +1,40 @@
import { AICFormation } from "./aicformation";
import { AICFormationContextDataInterface } from "./aicformationdescriptor";
import { AICFormationDescriptorComponent } from "./aicformationdescriptorcomponent";
export interface AICFormationDescriptorPhraseInterface {
"generate" : CallableFunction,
"label" : string,
"name" : string
}
export class AICFormationDescriptorPhrase {
#components : AICFormationDescriptorComponent[] = [];
label = "";
name = "";
constructor() {
}
addComponent( component:AICFormationDescriptorComponent ) {
this.#components.push( component );
return this;
}
getComponents() {
return this.#components;
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
return this;
}
}

View File

@ -0,0 +1,40 @@
import { AICFormation } from "./aicformation";
import { AICFormationContextDataInterface } from "./aicformationdescriptor";
import { AICFormationDescriptorPhrase } from "./aicformationdescriptorphrase";
export interface AICFormationDescriptorSectionInterface {
"generate" : CallableFunction,
"label" : string,
"name" : string,
"omitSection" : boolean
}
export abstract class AICFormationDescriptorSection {
#phrases : AICFormationDescriptorPhrase[] = [];
label = "";
name = "";
omitSection = false;
constructor() {
}
addPhrase( phrase:AICFormationDescriptorPhrase ) {
this.#phrases.push( phrase );
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
return this;
}
getPhrases() {
return this.#phrases;
}
}

View File

@ -0,0 +1,39 @@
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormationDescriptorSection_Formation extends AICFormationDescriptorSection {
label = "Formation";
name = "formation";
constructor() {
super();
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
if ( !formation.showFormationNameInDescriptor() ) {
this.omitSection = true;
return this;
}
let phrase = new AICFormationDescriptorPhrase();
phrase.addComponent( new AICFormationDescriptorComponent( formation.label, "Formation" ) );
phrase = formation.addToDescriptorPhrase( this, phrase, contextData );
this.addPhrase( phrase );
return this;
}
}

View File

@ -0,0 +1,35 @@
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormationDescriptorSection_NumGroups extends AICFormationDescriptorSection {
label = "Groups";
name = "numgroups";
constructor() {
super();
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
let value = "Single group";
if ( contextData.numGroups > 1 ) {
value = contextData.numGroups + " groups";
}
let phrase = new AICFormationDescriptorPhrase();
phrase.addComponent( new AICFormationDescriptorComponent( value, "Number of groups" ) );
this.addPhrase( phrase );
return this;
}
}

View File

@ -0,0 +1,83 @@
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
interface addUnitInformationInterface {
omitTrack?: boolean
}
export class AICFormationDescriptorSection_Unit extends AICFormationDescriptorSection {
label = "Unit";
name = "unit";
constructor() {
super();
}
addUnitInformation( formation:AICFormation, contextData: AICFormationContextDataInterface, phrase: AICFormationDescriptorPhrase, options?:addUnitInformationInterface ) {
options = options || {};
const originPoint = ( contextData.control === "broadcast" ) ? contextData.bullseyeName : "BRAA";
phrase.addComponent( new AICFormationDescriptorComponent( originPoint, "Bearing origin point" ) );
phrase.addComponent( new AICFormationDescriptorComponent( "<bearing>", "Bearing" ) );
phrase.addComponent( new AICFormationDescriptorComponent( "<range>", "Range" ) );
phrase.addComponent( new AICFormationDescriptorComponent( "<altitude>", "Altitude" ) );
if ( contextData.control === "broadcast" ) {
if ( !options.hasOwnProperty( "omitTrack" ) || options.omitTrack !== true ) {
phrase.addComponent( new AICFormationDescriptorComponent( "track <compass>", "Tracking" ) );
}
} else {
phrase.addComponent( new AICFormationDescriptorComponent( "[hot|flanking [left|right]|beam <compass>|cold]", "Azimuth" ) );
}
return phrase;
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
if ( formation.hasUnitBreakdown() ) {
for ( const [ i, unitRef ] of formation.unitBreakdown.entries() ) {
let phrase = new AICFormationDescriptorPhrase();
phrase.addComponent( new AICFormationDescriptorComponent( unitRef, "Unit reference" ) );
if ( i === 0 ) {
this.addUnitInformation( formation, contextData, phrase, { "omitTrack": true } );
} else {
phrase.addComponent( new AICFormationDescriptorComponent( "<altitude>" ) );
}
phrase.addComponent( new AICFormationDescriptorComponent( "hostile" ) );
this.addPhrase( phrase );
}
} else {
this.addPhrase(
this.addUnitInformation( formation, contextData, new AICFormationDescriptorPhrase() )
);
}
return this;
}
}

View File

@ -0,0 +1,35 @@
import { AICFormation } from "../aicformation";
import { AICFormationContextDataInterface } from "../aicformationdescriptor";
import { AICFormationDescriptorSection } from "../aicformationdescriptorsection";
import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent";
import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase";
export class AICFormationDescriptorSection_Who extends AICFormationDescriptorSection {
label = "Who";
name = "who";
constructor() {
super();
}
generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) {
let phrase = new AICFormationDescriptorPhrase();
if ( contextData.control === "tactical" ) {
phrase.addComponent( new AICFormationDescriptorComponent( "<their callsign>", "Their callsign" ) );
}
phrase.addComponent( new AICFormationDescriptorComponent( contextData.aicCallsign, "Your callsign" ) );
this.addPhrase( phrase );
return this;
}
}

View File

@ -1,6 +1,6 @@
import { ToggleableFeature } from "../ToggleableFeature";
import { ToggleableFeature } from "../toggleablefeature";
import Sortable from 'sortablejs';
import { ATCFLightList } from "./FlightList";
import { ATCFLightList } from "./flightlist";
export class ATC extends ToggleableFeature {

View File

@ -1,4 +1,4 @@
import { ATCMockAPI } from "../ATCMockAPI";
import { ATCMockAPI } from "../atcmockapi";
export class ATCMockAPI_Flights extends ATCMockAPI {

View File

@ -1,4 +1,4 @@
import { ATCMockAPI_Flights } from "./ATCMockAPI/Flights";
import { ATCMockAPI_Flights } from "./atcmockapi/flights";
export class ATCFLightList {

87
client/src/atc/atc.ts Normal file
View File

@ -0,0 +1,87 @@
import { ToggleableFeature } from "../toggleablefeature";
import Sortable from 'sortablejs';
import { ATCFLightList } from "./flightlist";
export class ATC extends ToggleableFeature {
constructor() {
super( true );
//this.#generateFlightList();
let $list = document.getElementById( "atc-strip-board-arrivals" );
if ( $list instanceof HTMLElement ) {
Sortable.create( $list, {
"handle": ".handle"
});
}
}
#generateFlightList() {
const flightList = new ATCFLightList();
const flights:any = flightList.getFlights( true );
const $tbody = document.getElementById( "atc-flight-list-table-body" );
if ( $tbody instanceof HTMLElement ) {
if ( flights.length > 0 ) {
let flight:any = {};
let $button, i;
for ( [ i, flight ] of flights.entries() ) {
const $row = document.createElement( "tr" );
$row.dataset.status = flight.status
let $td = document.createElement( "td" );
$td.innerText = flight.name;
$row.appendChild( $td );
$td = document.createElement( "td" );
$td.innerText = flight.takeOffTime;
$row.appendChild( $td );
$td = document.createElement( "td" );
$td.innerText = "00:0" + ( 5 + i );
$row.appendChild( $td );
$td = document.createElement( "td" );
$td.innerText = flight.status;
$row.appendChild( $td );
$td = document.createElement( "td" );
$button = document.createElement( "button" );
$button.innerText = "...";
$td.appendChild( $button );
$row.appendChild( $td );
$tbody.appendChild( $row );
}
}
}
}
protected onStatusUpdate(): void {
document.body.classList.toggle( "atc-enabled", this.getStatus() );
}
}

View File

@ -0,0 +1,7 @@
export abstract class ATCMockAPI {
constructor() {}
generateMockData() {}
}

View File

@ -0,0 +1,40 @@
import { ATCMockAPI } from "../atcmockapi";
export class ATCMockAPI_Flights extends ATCMockAPI {
generateMockData() {
let data = [];
const statuses = [ "unknown", "checkedIn", "readyToTaxi" ]
for ( const [ i, flightName ] of [ "Shark", "Whale", "Dolphin" ].entries() ) {
data.push({
"name": flightName,
"status": statuses[ i ],
"takeOffTime": "18:0" + i
});
}
localStorage.setItem( "flightList", JSON.stringify( data ) );
}
get( generateMockDataIfEmpty?:boolean ) : object {
generateMockDataIfEmpty = generateMockDataIfEmpty || false;
let data = localStorage.getItem( "flightList" ) || "[]";
if ( data === "[]" && generateMockDataIfEmpty ) {
this.generateMockData();
}
return JSON.parse( data );
}
}

View File

@ -0,0 +1,18 @@
import { ATCMockAPI_Flights } from "./atcmockapi/flights";
export class ATCFLightList {
constructor() {
}
getFlights( generateMockDataIfEmpty?:boolean ) {
let api = new ATCMockAPI_Flights();
return api.get( generateMockDataIfEmpty );
}
}

View File

@ -0,0 +1,161 @@
export interface FeatureSwitchInterface {
"defaultEnabled": boolean, // default on/off state (if allowed by masterSwitch)
"label": string,
"masterSwitch": boolean, // on/off regardless of user preference
"name": string,
"onEnabled"?: CallableFunction,
"options"?: object,
"removeArtifactsIfDisabled"?: boolean
}
class FeatureSwitch {
// From config param
defaultEnabled;
label;
masterSwitch;
name;
onEnabled;
removeArtifactsIfDisabled = true;
// Self-set
userPreference;
constructor( config:FeatureSwitchInterface ) {
this.defaultEnabled = config.defaultEnabled;
this.label = config.label;
this.masterSwitch = config.masterSwitch;
this.name = config.name;
this.onEnabled = config.onEnabled;
this.userPreference = this.getUserPreference();
}
getUserPreference() {
let preferences = JSON.parse( localStorage.getItem( "featureSwitches" ) || "{}" );
return ( preferences.hasOwnProperty( this.name ) ) ? preferences[ this.name ] : this.defaultEnabled;
}
isEnabled() {
if ( !this.masterSwitch ) {
return false;
}
return this.userPreference;
}
}
export class FeatureSwitches {
#featureSwitches:FeatureSwitch[] = [
new FeatureSwitch({
"defaultEnabled": false,
"label": "AIC",
"masterSwitch": true,
"name": "aic"
}),
new FeatureSwitch({
"defaultEnabled": false,
"label": "AI Formations",
"masterSwitch": true,
"name": "ai-formations",
"removeArtifactsIfDisabled": false
}),
new FeatureSwitch({
"defaultEnabled": false,
"label": "ATC",
"masterSwitch": true,
"name": "atc"
}),
new FeatureSwitch({
"defaultEnabled": false,
"label": "Force show unit control panel",
"masterSwitch": true,
"name": "forceShowUnitControlPanel"
}),
new FeatureSwitch({
"defaultEnabled": false,
"label": "Show splash screen",
"masterSwitch": true,
"name": "splashScreen"
})
];
constructor() {
this.#testSwitches();
this.savePreferences();
}
getSwitch( switchName:string ) {
return this.#featureSwitches.find( featureSwitch => featureSwitch.name === switchName );
}
#testSwitches() {
for ( const featureSwitch of this.#featureSwitches ) {
if ( featureSwitch.isEnabled() ) {
if ( typeof featureSwitch.onEnabled === "function" ) {
featureSwitch.onEnabled();
}
} else {
document.querySelectorAll( "[data-feature-switch='" + featureSwitch.name + "']" ).forEach( el => {
if ( featureSwitch.removeArtifactsIfDisabled === false ) {
el.remove();
} else {
el.classList.add( "hide" );
}
});
}
document.body.classList.toggle( "feature-" + featureSwitch.name, featureSwitch.isEnabled() );
}
}
savePreferences() {
let preferences:any = {};
for ( const featureSwitch of this.#featureSwitches ) {
preferences[ featureSwitch.name ] = featureSwitch.isEnabled();
}
localStorage.setItem( "featureSwitches", JSON.stringify( preferences ) );
}
}

View File

@ -6,8 +6,8 @@ import { MissionHandler } from "./missionhandler/missionhandler";
import { UnitControlPanel } from "./panels/unitcontrolpanel";
import { MouseInfoPanel } from "./panels/mouseinfopanel";
import { AIC } from "./aic/aic";
import { ATC } from "./atc/ATC";
import { FeatureSwitches } from "./FeatureSwitches";
import { ATC } from "./atc/atc";
import { FeatureSwitches } from "./featureswitches";
import { LogPanel } from "./panels/logpanel";
import { getAirbases, getBulllseye as getBulllseyes, getMission, getUnits, toggleDemoEnabled } from "./server/server";

View File

@ -0,0 +1,35 @@
export abstract class ToggleableFeature {
#status:boolean = false;
constructor( defaultStatus:boolean ) {
this.#status = defaultStatus;
this.onStatusUpdate();
}
getStatus() : boolean {
return this.#status;
}
protected onStatusUpdate() {}
toggleStatus( force?:boolean ) : void {
if ( force ) {
this.#status = force;
} else {
this.#status = !this.#status;
}
this.onStatusUpdate();
}
}

BIN
missions/olympus.miz Normal file

Binary file not shown.

275
src/core/include/commands.h Normal file
View File

@ -0,0 +1,275 @@
#pragma once
#include "framework.h"
#include "luatools.h"
#include "utils.h"
namespace CommandPriority {
enum CommandPriorities { LOW, MEDIUM, HIGH };
};
namespace CommandType {
enum CommandTypes { NO_TYPE, MOVE, SMOKE, SPAWN_AIR, SPAWN_GROUND, CLONE, FOLLOW, RESET_TASK, SET_OPTION, SET_COMMAND, SET_TASK };
};
namespace SetCommandType {
enum SetCommandTypes {
ROE = 0,
REACTION_ON_THREAT = 1,
RADAR_USING = 3,
FLARE_USING = 4,
FORMATION = 5,
RTB_ON_BINGO = 6,
SILENCE = 7,
RTB_ON_OUT_OF_AMMO = 10,
ECM_USING = 13,
PROHIBIT_AA = 14,
PROHIBIT_JETT = 15,
PROHIBIT_AB = 16,
PROHIBIT_AG = 17,
MISSILE_ATTACK = 18,
PROHIBIT_WP_PASS_REPORT = 19,
OPTION_RADIO_USAGE_CONTACT = 21,
OPTION_RADIO_USAGE_ENGAGE = 22,
OPTION_RADIO_USAGE_KILL = 23,
JETT_TANKS_IF_EMPTY = 25,
FORCED_ATTACK = 26
};
}
namespace ROE {
enum ROEs {
WEAPON_FREE = 0,
OPEN_FIRE_WEAPON_FREE = 1,
OPEN_FIRE = 2,
RETURN_FIRE = 3,
WEAPON_HOLD = 4,
};
}
namespace ReactionToThreat {
enum ReactionToThreats {
NO_REACTION = 0,
PASSIVE_DEFENCE = 1,
EVADE_FIRE = 2,
BYPASS_AND_ESCAPE = 3,
ALLOW_ABORT_MISSION = 4
};
}
/* Base command class */
class Command
{
public:
int getPriority() { return priority; }
int getType() { return type; }
virtual wstring getString(lua_State* L) = 0;
virtual int getLoad() = 0;
protected:
int priority = CommandPriority::LOW;
int type = CommandType::NO_TYPE;
};
/* Simple low priority move command (from user click) */
class Move : public Command
{
public:
Move(int ID, Coords destination, double speed, double altitude, wstring unitCategory, wstring taskOptions):
ID(ID),
destination(destination),
speed(speed),
altitude(altitude),
unitCategory(unitCategory),
taskOptions(taskOptions)
{
priority = CommandPriority::HIGH;
type = CommandType::MOVE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 5; }
private:
const int ID;
const Coords destination;
const wstring unitCategory;
const double speed;
const double altitude;
const wstring taskOptions;
};
/* Smoke command */
class Smoke : public Command
{
public:
Smoke(wstring color, Coords location) :
color(color),
location(location)
{
priority = CommandPriority::LOW;
type = CommandType::SMOKE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 5; }
private:
const wstring color;
const Coords location;
};
/* Spawn ground unit command */
class SpawnGroundUnit : public Command
{
public:
SpawnGroundUnit(wstring coalition, wstring unitType, Coords location) :
coalition(coalition),
unitType(unitType),
location(location)
{
priority = CommandPriority::LOW;
type = CommandType::SPAWN_GROUND;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; }
private:
const wstring coalition;
const wstring unitType;
const Coords location;
};
/* Spawn air unit command */
class SpawnAircraft : public Command
{
public:
SpawnAircraft(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) :
coalition(coalition),
unitType(unitType),
location(location),
payloadName(payloadName),
airbaseName(airbaseName)
{
priority = CommandPriority::LOW;
type = CommandType::SPAWN_AIR;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; }
private:
const wstring coalition;
const wstring unitType;
const Coords location;
const wstring payloadName;
const wstring airbaseName;
};
/* Clone unit command */
class Clone : public Command
{
public:
Clone(int ID, Coords location) :
ID(ID),
location(location)
{
priority = CommandPriority::LOW;
type = CommandType::CLONE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; }
private:
const int ID;
const Coords location;
};
/* Delete unit command */
class Delete : public Command
{
public:
Delete(int ID) :
ID(ID)
{
priority = CommandPriority::HIGH;
type = CommandType::CLONE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 20; }
private:
const int ID;
};
/* Follow command */
class SetTask : public Command
{
public:
SetTask(int ID, wstring task) :
ID(ID),
task(task)
{
priority = CommandPriority::MEDIUM;
type = CommandType::FOLLOW;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
const wstring task;
};
/* Reset task command */
class ResetTask : public Command
{
public:
ResetTask(int ID) :
ID(ID)
{
priority = CommandPriority::HIGH;
type = CommandType::RESET_TASK;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
};
/* Set command */
class SetCommand : public Command
{
public:
SetCommand(int ID, wstring command) :
ID(ID),
command(command)
{
priority = CommandPriority::HIGH;
type = CommandType::RESET_TASK;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
const wstring command;
};
/* Set option command */
class SetOption : public Command
{
public:
SetOption(int ID, int optionID, int optionValue) :
ID(ID),
optionID(optionID),
optionValue(optionValue)
{
priority = CommandPriority::HIGH;
type = CommandType::RESET_TASK;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
const int optionID;
const int optionValue;
};

View File

@ -0,0 +1,20 @@
#pragma once
#include "framework.h"
#include "luatools.h"
#include "commands.h"
class Scheduler
{
public:
Scheduler(lua_State* L);
~Scheduler();
void appendCommand(Command* command);
void execute(lua_State* L);
void handleRequest(wstring key, json::value value);
private:
list<Command*> commands;
int load;
};

184
src/core/include/unit.h Normal file
View File

@ -0,0 +1,184 @@
#pragma once
#include "framework.h"
#include "utils.h"
#include "dcstools.h"
#include "luatools.h"
#include "measure.h"
namespace State
{
enum States
{
IDLE,
REACH_DESTINATION,
ATTACK,
WINGMAN,
FOLLOW,
LAND,
REFUEL,
AWACS,
EWR,
TANKER,
RUN_AWAY
};
};
class Unit
{
public:
Unit(json::value json, int ID);
~Unit();
/********** Public methods **********/
int getID() { return ID; }
void updateExportData(json::value json);
void updateMissionData(json::value json);
json::value getData(long long time);
/********** Base data **********/
void setAI(bool newAI) { AI = newAI; addMeasure(L"AI", json::value(newAI)); }
void setName(wstring newName) { name = newName; addMeasure(L"name", json::value(newName));}
void setUnitName(wstring newUnitName) { unitName = newUnitName; addMeasure(L"unitName", json::value(newUnitName));}
void setGroupName(wstring newGroupName) { groupName = newGroupName; addMeasure(L"groupName", json::value(newGroupName));}
void setAlive(bool newAlive) { alive = newAlive; addMeasure(L"alive", json::value(newAlive));}
void setType(json::value newType) { type = newType; addMeasure(L"type", newType);}
void setCountry(int newCountry) { country = newCountry; addMeasure(L"country", json::value(newCountry));}
bool getAI() { return AI; }
wstring getName() { return name; }
wstring getUnitName() { return unitName; }
wstring getGroupName() { return groupName; }
bool getAlive() { return alive; }
json::value getType() { return type; }
int getCountry() { return country; }
/********** Flight data **********/
void setLatitude(double newLatitude) {latitude = newLatitude; addMeasure(L"latitude", json::value(newLatitude));}
void setLongitude(double newLongitude) {longitude = newLongitude; addMeasure(L"longitude", json::value(newLongitude));}
void setAltitude(double newAltitude) {altitude = newAltitude; addMeasure(L"altitude", json::value(newAltitude));}
void setHeading(double newHeading) {heading = newHeading; addMeasure(L"heading", json::value(newHeading));}
void setSpeed(double newSpeed) {speed = newSpeed; addMeasure(L"speed", json::value(newSpeed));}
double getLatitude() { return latitude; }
double getLongitude() { return longitude; }
double getAltitude() { return altitude; }
double getHeading() { return heading; }
double getSpeed() { return speed; }
/********** Mission data **********/
void setFuel(double newFuel) { fuel = newFuel; addMeasure(L"fuel", json::value(newFuel));}
void setAmmo(json::value newAmmo) { ammo = newAmmo; addMeasure(L"ammo", json::value(newAmmo));}
void setTargets(json::value newTargets) {targets = newTargets; addMeasure(L"targets", json::value(newTargets));}
void setHasTask(bool newHasTask) { hasTask = newHasTask; addMeasure(L"hasTask", json::value(newHasTask));}
void setCoalitionID(int newCoalitionID);
void setFlags(json::value newFlags) { flags = newFlags; addMeasure(L"flags", json::value(newFlags));}
double getFuel() { return fuel; }
json::value getAmmo() { return ammo; }
json::value getTargets() { return targets; }
bool getHasTask() { return hasTask; }
wstring getCoalition() { return coalition; }
int getCoalitionID();
json::value getFlags() { return flags; }
/********** Formation data **********/
void setIsLeader(bool newIsLeader);
void setIsWingman(bool newIsWingman);
void setLeader(Unit* newLeader);
void setWingmen(vector<Unit*> newWingmen);
void setFormation(wstring newFormation) { formation = newFormation; addMeasure(L"formation", json::value(formation));}
void setFormationOffset(Offset formationOffset);
bool getIsLeader() { return isLeader; }
bool getIsWingman() { return isWingman; }
Unit* getLeader() { return leader; }
vector<Unit*> getWingmen() { return wingmen; }
wstring getFormation() { return formation; }
Offset getFormationoffset() { return formationOffset; }
/********** Task data **********/
void setCurrentTask(wstring newCurrentTask) { currentTask = newCurrentTask;addMeasure(L"currentTask", json::value(newCurrentTask)); }
virtual void setTargetSpeed(double newTargetSpeed) { targetSpeed = newTargetSpeed; addMeasure(L"targetSpeed", json::value(newTargetSpeed));}
virtual void setTargetAltitude(double newTargetAltitude) { targetAltitude = newTargetAltitude; addMeasure(L"targetAltitude", json::value(newTargetAltitude));} //TODO fix, double definition
void setActiveDestination(Coords newActiveDestination) { activeDestination = newActiveDestination; addMeasure(L"activeDestination", json::value("")); } // TODO fix
void setActivePath(list<Coords> newActivePath);
void setTargetID(int newTargetID) { targetID = newTargetID; addMeasure(L"targetID", json::value(newTargetID));}
wstring getCurrentTask() { return currentTask; }
virtual double getTargetSpeed() { return targetSpeed; };
virtual double getTargetAltitude() { return targetAltitude; };
Coords getActiveDestination() { return activeDestination; }
list<Coords> getActivePath() { return activePath; }
int getTargetID() { return targetID; }
/********** Options data **********/
void setROE(wstring newROE);
void setReactionToThreat(wstring newReactionToThreat);
wstring getROE() { return ROE; }
wstring getReactionToThreat() {return reactionToThreat;}
/********** Control functions **********/
void landAt(Coords loc);
virtual void changeSpeed(wstring change){};
virtual void changeAltitude(wstring change){};
void resetActiveDestination();
virtual void setState(int newState) { state = newState; };
void resetTask();
protected:
int ID;
map<wstring, Measure*> measures;
/********** Base data **********/
bool AI = false;
wstring name = L"undefined";
wstring unitName = L"undefined";
wstring groupName = L"undefined";
bool alive = true;
json::value type = json::value::null();
int country = NULL;
/********** Flight data **********/
double latitude = NULL;
double longitude = NULL;
double altitude = NULL;
double speed = NULL;
double heading = NULL;
/********** Mission data **********/
double fuel = 0;
json::value ammo = json::value::null();
json::value targets = json::value::null();
bool hasTask = false;
wstring coalition = L"";
json::value flags = json::value::null();
/********** Formation data **********/
bool isLeader = false;
bool isWingman = false;
wstring formation = L"";
Unit *leader = nullptr;
vector<Unit *> wingmen;
Offset formationOffset = Offset(NULL);
/********** Task data **********/
wstring currentTask = L"";
double targetSpeed = 0;
double targetAltitude = 0;
list<Coords> activePath;
Coords activeDestination = Coords(0);
int targetID = NULL;
/********** Options data **********/
wstring ROE = L"";
wstring reactionToThreat = L"";
/********** State machine **********/
int state = State::IDLE;
/********** Other **********/
Coords oldPosition = Coords(0); // Used to approximate speed
/********** Functions **********/
virtual wstring getCategory() { return L"No category"; };
wstring getTargetName();
bool isTargetAlive();
virtual void AIloop() = 0;
void addMeasure(wstring key, json::value value);
};

135
src/core/src/commands.cpp Normal file
View File

@ -0,0 +1,135 @@
#include "commands.h"
#include "logger.h"
#include "dcstools.h"
/* Move command */
wstring Move::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.move, "
<< ID << ", "
<< destination.lat << ", "
<< destination.lng << ", "
<< altitude << ", "
<< speed << ", "
<< "\"" << unitCategory << "\"" << ", "
<< taskOptions;
return commandSS.str();
}
/* Smoke command */
wstring Smoke::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.smoke, "
<< "\"" << color << "\"" << ", "
<< location.lat << ", "
<< location.lng;
return commandSS.str();
}
/* Spawn ground command */
wstring SpawnGroundUnit::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.spawnGroundUnit, "
<< "\"" << coalition << "\"" << ", "
<< "\"" << unitType << "\"" << ", "
<< location.lat << ", "
<< location.lng;
return commandSS.str();
}
/* Spawn air command */
wstring SpawnAircraft::getString(lua_State* L)
{
std::wostringstream optionsSS;
optionsSS.precision(10);
optionsSS << "{"
<< "payloadName = \"" << payloadName << "\", "
<< "airbaseName = \"" << airbaseName << "\","
<< "}";
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.spawnAircraft, "
<< "\"" << coalition << "\"" << ", "
<< "\"" << unitType << "\"" << ", "
<< location.lat << ", "
<< location.lng << ","
<< optionsSS.str();
return commandSS.str();
}
/* Clone unit command */
wstring Clone::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.clone, "
<< ID << ", "
<< location.lat << ", "
<< location.lng;
return commandSS.str();
}
/* Delete unit command */
wstring Delete::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.delete, "
<< ID;
return commandSS.str();
}
/* Set task command */
wstring SetTask::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.setTask, "
<< ID << ","
<< task;
return commandSS.str();
}
/* Reset task command */
wstring ResetTask::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.resetTask, "
<< ID;
return commandSS.str();
}
/* Set command command */
wstring SetCommand::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.setCommand, "
<< ID << ","
<< command;
return commandSS.str();
}
/* Set option command */
wstring SetOption::getString(lua_State* L)
{
std::wostringstream commandSS;
commandSS.precision(10);
commandSS << "Olympus.setOption, "
<< ID << ","
<< optionID << ","
<< optionValue;
return commandSS.str();
}

263
src/core/src/scheduler.cpp Normal file
View File

@ -0,0 +1,263 @@
#include "scheduler.h"
#include "logger.h"
#include "dcstools.h"
#include "unitsManager.h"
#include "utils.h"
#include "unit.h"
extern UnitsManager* unitsManager;
Scheduler::Scheduler(lua_State* L):
load(0)
{
LogInfo(L, "Scheduler constructor called successfully");
}
Scheduler::~Scheduler()
{
}
void Scheduler::appendCommand(Command* command)
{
commands.push_back(command);
}
void Scheduler::execute(lua_State* L)
{
/* Decrease the active computation load. New commands can be sent only if the load has reached 0.
This is needed to avoid server lag. */
if (load > 0) {
load--;
return;
}
int priority = CommandPriority::HIGH;
while (priority >= CommandPriority::LOW)
{
for (auto command : commands)
{
if (command->getPriority() == priority)
{
wstring commandString = L"Olympus.protectedCall(" + command->getString(L) + L")";
if (dostring_in(L, "server", to_string(commandString)))
log(L"Error executing command " + commandString);
load = command->getLoad();
commands.remove(command);
return;
}
}
priority--;
}
}
void Scheduler::handleRequest(wstring key, json::value value)
{
Command* command = nullptr;
log(L"Received request with ID: " + key);
if (key.compare(L"setPath") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
{
wstring unitName = unit->getUnitName();
json::value path = value[L"path"];
list<Coords> newPath;
for (int i = 1; i <= path.as_object().size(); i++)
{
wstring WP = to_wstring(i);
double lat = path[WP][L"lat"].as_double();
double lng = path[WP][L"lng"].as_double();
log(unitName + L" set path destination " + WP + L" (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
Coords dest; dest.lat = lat; dest.lng = lng;
newPath.push_back(dest);
}
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
{
unit->setActivePath(newPath);
unit->setState(State::REACH_DESTINATION);
log(unitName + L" new path set successfully");
}
else
log(unitName + L" not found, request will be discarded");
}
}
else if (key.compare(L"smoke") == 0)
{
wstring color = value[L"color"].as_string();
double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double();
log(L"Adding " + color + L" smoke at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new Smoke(color, loc));
}
else if (key.compare(L"spawnGround") == 0)
{
wstring coalition = value[L"coalition"].as_string();
wstring type = value[L"type"].as_string();
double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double();
log(L"Spawning " + coalition + L" ground unit of type " + type + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new SpawnGroundUnit(coalition, type, loc));
}
else if (key.compare(L"spawnAir") == 0)
{
wstring coalition = value[L"coalition"].as_string();
wstring type = value[L"type"].as_string();
double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double();
Coords loc; loc.lat = lat; loc.lng = lng;
wstring payloadName = value[L"payloadName"].as_string();
wstring airbaseName = value[L"airbaseName"].as_string();
log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L" " + airbaseName + L")");
command = dynamic_cast<Command*>(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName));
}
else if (key.compare(L"attackUnit") == 0)
{
int ID = value[L"ID"].as_integer();
int targetID = value[L"targetID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
Unit* target = unitsManager->getUnit(targetID);
wstring unitName;
wstring targetName;
if (unit != nullptr)
unitName = unit->getUnitName();
else
return;
if (target != nullptr)
targetName = target->getUnitName();
else
return;
log(L"Unit " + unitName + L" attacking unit " + targetName);
unit->setTargetID(targetID);
unit->setState(State::ATTACK);
}
else if (key.compare(L"stopAttack") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
unit->setState(State::REACH_DESTINATION);
else
return;
}
else if (key.compare(L"changeSpeed") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
unit->changeSpeed(value[L"change"].as_string());
}
else if (key.compare(L"changeAltitude") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
unit->changeAltitude(value[L"change"].as_string());
}
else if (key.compare(L"setSpeed") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
unit->setTargetSpeed(value[L"speed"].as_double());
}
else if (key.compare(L"setAltitude") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
if (unit != nullptr)
unit->setTargetAltitude(value[L"altitude"].as_double());
}
else if (key.compare(L"cloneUnit") == 0)
{
int ID = value[L"ID"].as_integer();
double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double();
Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new Clone(ID, loc));
log(L"Cloning unit " + to_wstring(ID));
}
else if (key.compare(L"setLeader") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
bool isLeader = value[L"isLeader"].as_bool();
if (isLeader)
{
json::value wingmenIDs = value[L"wingmenIDs"];
vector<Unit*> wingmen;
if (unit != nullptr)
{
for (auto itr = wingmenIDs.as_array().begin(); itr != wingmenIDs.as_array().end(); itr++)
{
Unit* wingman = unitsManager->getUnit(itr->as_integer());
if (wingman != nullptr)
wingmen.push_back(wingman);
}
unit->setFormation(L"Line abreast");
unit->setIsLeader(true);
unit->setWingmen(wingmen);
log(L"Setting " + unit->getName() + L" as formation leader");
}
}
else {
unit->setIsLeader(false);
}
}
else if (key.compare(L"setFormation") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
wstring formation = value[L"formation"].as_string();
unit->setFormation(formation);
}
else if (key.compare(L"setROE") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
wstring ROE = value[L"ROE"].as_string();
unit->setROE(ROE);
}
else if (key.compare(L"setReactionToThreat") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
wstring reactionToThreat = value[L"reactionToThreat"].as_string();
unit->setReactionToThreat(reactionToThreat);
}
else if (key.compare(L"landAt") == 0)
{
int ID = value[L"ID"].as_integer();
Unit* unit = unitsManager->getUnit(ID);
double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double();
Coords loc; loc.lat = lat; loc.lng = lng;
unit->landAt(loc);
}
else if (key.compare(L"deleteUnit") == 0)
{
int ID = value[L"ID"].as_integer();
unitsManager->deleteUnit(ID);
}
else
{
log(L"Unknown command: " + key);
}
if (command != nullptr)
{
appendCommand(command);
}
}

322
src/core/src/unit.cpp Normal file
View File

@ -0,0 +1,322 @@
#include "unit.h"
#include "utils.h"
#include "logger.h"
#include "commands.h"
#include "scheduler.h"
#include "defines.h"
#include "unitsManager.h"
#include <chrono>
using namespace std::chrono;
#include <GeographicLib/Geodesic.hpp>
using namespace GeographicLib;
extern Scheduler* scheduler;
extern UnitsManager* unitsManager;
Unit::Unit(json::value json, int ID) :
ID(ID)
{
log("Creating unit with ID: " + to_string(ID));
}
Unit::~Unit()
{
}
void Unit::addMeasure(wstring key, json::value value)
{
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
if (measures.find(key) == measures.end())
measures[key] = new Measure(value, ms.count());
else
{
if (measures[key]->getValue() != value)
{
measures[key]->setValue(value);
measures[key]->setTime(ms.count());
}
}
}
void Unit::updateExportData(json::value json)
{
/* Compute speed (loGetWorldObjects does not provide speed, we compute it for better performance instead of relying on many lua calls) */
if (oldPosition != NULL)
{
double dist = 0;
Geodesic::WGS84().Inverse(latitude, longitude, oldPosition.lat, oldPosition.lng, dist);
setSpeed(getSpeed() * 0.95 + (dist / UPDATE_TIME_INTERVAL) * 0.05);
}
oldPosition = Coords(latitude, longitude, altitude);
if (json.has_string_field(L"Name"))
setName(json[L"Name"].as_string());
if (json.has_string_field(L"UnitName"))
setUnitName(json[L"UnitName"].as_string());
if (json.has_string_field(L"GroupName"))
setGroupName(json[L"GroupName"].as_string());
if (json.has_object_field(L"Type"))
setType(json[L"Type"]);
if (json.has_number_field(L"Country"))
setCountry(json[L"Country"].as_number().to_int32());
if (json.has_number_field(L"CoalitionID"))
setCoalitionID(json[L"CoalitionID"].as_number().to_int32());
if (json.has_object_field(L"LatLongAlt"))
{
setLatitude(json[L"LatLongAlt"][L"Lat"].as_number().to_double());
setLongitude(json[L"LatLongAlt"][L"Long"].as_number().to_double());
setAltitude(json[L"LatLongAlt"][L"Alt"].as_number().to_double());
}
if (json.has_number_field(L"Heading"))
setHeading(json[L"Heading"].as_number().to_double());
if (json.has_object_field(L"Flags"))
setFlags(json[L"Flags"]);
/* All units which contain the name "Olympus" are automatically under AI control */
/* TODO: I don't really like using this method */
setAI(getUnitName().find(L"Olympus") != wstring::npos);
/* If the unit is alive and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
if (getAI() && getAlive() && getFlags()[L"Human"].as_bool() == false)
AIloop();
}
void Unit::updateMissionData(json::value json)
{
if (json.has_number_field(L"fuel"))
setFuel(int(json[L"fuel"].as_number().to_double() * 100));
if (json.has_object_field(L"ammo"))
setAmmo(json[L"ammo"]);
if (json.has_object_field(L"targets"))
setTargets(json[L"targets"]);
if (json.has_boolean_field(L"hasTask"))
setHasTask(json[L"hasTask"].as_bool());
}
json::value Unit::getData(long long time)
{
auto json = json::value::object();
/********** Base data **********/
json[L"baseData"] = json::value::object();
for (auto key : { L"AI", L"name", L"unitName", L"groupName", L"alive", L"category"})
{
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
json[L"baseData"][key] = measures[key]->getValue();
}
/********** Flight data **********/
json[L"flightData"] = json::value::object();
for (auto key : { L"latitude", L"longitude", L"altitude", L"speed", L"heading"})
{
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
json[L"flightData"][key] = measures[key]->getValue();
}
/********** Mission data **********/
json[L"missionData"] = json::value::object();
for (auto key : { L"fuel", L"ammo", L"targets", L"hasTask", L"coalition", L"flags"})
{
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
json[L"missionData"][key] = measures[key]->getValue();
}
/********** Formation data **********/
json[L"formationData"] = json::value::object();
for (auto key : { L"isLeader", L"isWingman", L"formation", L"wingmenIDs", L"leaderID" })
{
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
json[L"missionData"][key] = measures[key]->getValue();
}
/********** Task data **********/
json[L"taskData"] = json::value::object();
for (auto key : { L"currentTask", L"targetSpeed", L"targetAltitude", L"activePath" })
{
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
json[L"taskData"][key] = measures[key]->getValue();
}
/********** Options data **********/
json[L"optionsData"] = json::value::object();
for (auto key : { L"ROE", L"reactionToThreat" })
{
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
json[L"optionsData"][key] = measures[key]->getValue();
}
return json;
}
void Unit::setActivePath(list<Coords> newPath)
{
if (state != State::WINGMAN && state != State::FOLLOW)
{
activePath = newPath;
resetActiveDestination();
}
auto path = json::value::object();
if (activePath.size() > 0) {
int count = 1;
for (auto& destination : activePath)
{
auto json = json::value::object();
json[L"lat"] = destination.lat;
json[L"lng"] = destination.lng;
json[L"alt"] = destination.alt;
path[to_wstring(count++)] = json;
}
}
addMeasure(L"activePath", path);
}
void Unit::setCoalitionID(int newCoalitionID)
{
if (newCoalitionID == 0)
coalition = L"neutral";
else if (newCoalitionID == 1)
coalition = L"red";
else
coalition = L"blue";
addMeasure(L"coalition", json::value(coalition));
}
int Unit::getCoalitionID()
{
if (coalition == L"neutral")
return 0;
else if (coalition == L"red")
return 1;
else
return 2;
}
void Unit::setLeader(Unit* newLeader)
{
leader = newLeader;
if (leader != nullptr)
addMeasure(L"leaderID", json::value(leader->getID()));
}
void Unit::setWingmen(vector<Unit*> newWingmen) {
wingmen = newWingmen;
auto wingmenIDs = json::value::object();
int i = 0;
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)
wingmenIDs[i++] = (*itr)->getID();
addMeasure(L"wingmen", wingmenIDs);
}
wstring Unit::getTargetName()
{
if (isTargetAlive())
{
Unit* target = unitsManager->getUnit(targetID);
if (target != nullptr)
return target->getUnitName();
}
return L"";
}
bool Unit::isTargetAlive()
{
if (targetID == NULL)
return false;
Unit* target = unitsManager->getUnit(targetID);
if (target != nullptr)
return target->alive;
else
return false;
}
void Unit::resetActiveDestination()
{
activeDestination = Coords(NULL);
}
void Unit::resetTask()
{
Command* command = dynamic_cast<Command*>(new ResetTask(ID));
scheduler->appendCommand(command);
}
void Unit::setIsLeader(bool newIsLeader) {
isLeader = newIsLeader;
if (!isLeader) {
for (auto wingman : wingmen)
{
wingman->setFormation(L"");
wingman->setIsWingman(false);
wingman->setLeader(nullptr);
}
}
addMeasure(L"isLeader", json::value(newIsLeader));
}
void Unit::setIsWingman(bool newIsWingman)
{
isWingman = newIsWingman;
if (isWingman)
setState(State::WINGMAN);
else
setState(State::IDLE);
addMeasure(L"isWingman", json::value(isWingman));
}
void Unit::setFormationOffset(Offset newFormationOffset)
{
formationOffset = newFormationOffset;
resetTask();
}
void Unit::setROE(wstring newROE) {
ROE = newROE;
int ROEEnum;
if (newROE.compare(L"Free") == 0)
ROEEnum = ROE::WEAPON_FREE;
else if (newROE.compare(L"Designated free") == 0)
ROEEnum = ROE::OPEN_FIRE_WEAPON_FREE;
else if (newROE.compare(L"Designated") == 0)
ROEEnum = ROE::OPEN_FIRE;
else if (newROE.compare(L"Return") == 0)
ROEEnum = ROE::RETURN_FIRE;
else if (newROE.compare(L"Hold") == 0)
ROEEnum = ROE::WEAPON_HOLD;
else
return;
Command* command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::ROE, ROEEnum));
scheduler->appendCommand(command);
addMeasure(L"ROE", json::value(newROE));
}
void Unit::setReactionToThreat(wstring newReactionToThreat) {
reactionToThreat = newReactionToThreat;
int reactionToThreatEnum;
if (newReactionToThreat.compare(L"None") == 0)
reactionToThreatEnum = ReactionToThreat::NO_REACTION;
else if (newReactionToThreat.compare(L"Passive") == 0)
reactionToThreatEnum = ReactionToThreat::PASSIVE_DEFENCE;
else if (newReactionToThreat.compare(L"Evade") == 0)
reactionToThreatEnum = ReactionToThreat::EVADE_FIRE;
else if (newReactionToThreat.compare(L"Escape") == 0)
reactionToThreatEnum = ReactionToThreat::BYPASS_AND_ESCAPE;
else if (newReactionToThreat.compare(L"Abort") == 0)
reactionToThreatEnum = ReactionToThreat::ALLOW_ABORT_MISSION;
else
return;
Command* command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum));
scheduler->appendCommand(command);
addMeasure(L"reactionToThreat", json::value(newReactionToThreat));
}
void Unit::landAt(Coords loc) {
activePath.clear();
activePath.push_back(loc);
setState(State::LAND);
}

42
src/olympus.sln Normal file
View File

@ -0,0 +1,42 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "core\core.vcxproj", "{8A48D855-0E01-42BA-BD8C-07B0877C68DF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logger", "logger\logger.vcxproj", "{873ECABE-FCFE-4217-AC15-91959C3CF1C6}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dcstools", "dcstools\dcstools.vcxproj", "{2B255368-39A0-431A-A6DE-CC739AC70DC1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "luatools", "luatools\luatools.vcxproj", "{DE139EC1-4F88-47D5-BE73-F41915FE14A3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utils", "utils\utils.vcxproj", "{B85009CE-4A5C-4A5A-B85D-001B3A2651B2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "olympus", "olympus\olympus.vcxproj", "{5F3FC91E-1FBC-4223-8011-9708DE913474}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Release|x64.ActiveCfg = Release|x64
{8A48D855-0E01-42BA-BD8C-07B0877C68DF}.Release|x64.Build.0 = Release|x64
{873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Release|x64.ActiveCfg = Release|x64
{873ECABE-FCFE-4217-AC15-91959C3CF1C6}.Release|x64.Build.0 = Release|x64
{2B255368-39A0-431A-A6DE-CC739AC70DC1}.Release|x64.ActiveCfg = Release|x64
{2B255368-39A0-431A-A6DE-CC739AC70DC1}.Release|x64.Build.0 = Release|x64
{DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Release|x64.ActiveCfg = Release|x64
{DE139EC1-4F88-47D5-BE73-F41915FE14A3}.Release|x64.Build.0 = Release|x64
{B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Release|x64.ActiveCfg = Release|x64
{B85009CE-4A5C-4A5A-B85D-001B3A2651B2}.Release|x64.Build.0 = Release|x64
{5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x64.ActiveCfg = Release|x64
{5F3FC91E-1FBC-4223-8011-9708DE913474}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FAB9F592-7511-4EB9-B365-078842ED9BDD}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="inc">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>inc</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>inc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
</Project>

120
src/olympus/olympus.vcxproj Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\olympus.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\dcstools\dcstools.vcxproj">
<Project>{2b255368-39a0-431a-a6de-cc739ac70dc1}</Project>
</ProjectReference>
<ProjectReference Include="..\logger\logger.vcxproj">
<Project>{873ecabe-fcfe-4217-ac15-91959c3cf1c6}</Project>
</ProjectReference>
<ProjectReference Include="..\utils\utils.vcxproj">
<Project>{b85009ce-4a5c-4a5a-b85d-001b3a2651b2}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{5f3fc91e-1fbc-4223-8011-9708de913474}</ProjectGuid>
<RootNamespace>Olympus</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>olympus</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>.\..\..\bin\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>.\..\..\bin\</OutDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgAutoLink>true</VcpkgAutoLink>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS; _DEBUG;OLYMPUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>include; ..\..\third-party\lua\include; ..\utils\include; ..\shared\include</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<AssemblerOutput>AssemblyCode</AssemblerOutput>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>lua.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\third-party\lua</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS; NDEBUG;OLYMPUS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>include;..\..\third-party\lua\include;..\utils\include;..\shared\include;..\dcstools\include;..\logger\include;..\luatools\include</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp20</LanguageStandard>
<AssemblerOutput>NoListing</AssemblerOutput>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>lua.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\third-party\lua</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{c021f649-26ec-4040-abd4-13132def4a81}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{2809fbbc-77d2-465e-afef-230525a60c66}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\olympus.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

152
src/olympus/src/olympus.cpp Normal file
View File

@ -0,0 +1,152 @@
#include "framework.h"
#include "dcstools.h"
#include "logger.h"
#include "utils.h"
/* Run-time linking to core dll allows for "hot swap". This is useful for development but could be removed when stable.*/
HINSTANCE hGetProcIDDLL = NULL;
typedef int(__stdcall* f_coreInit)(lua_State* L);
typedef int(__stdcall* f_coreDeinit)(lua_State* L);
typedef int(__stdcall* f_coreFrame)(lua_State* L);
typedef int(__stdcall* f_coreMissionData)(lua_State* L);
f_coreInit coreInit = nullptr;
f_coreDeinit coreDeinit = nullptr;
f_coreFrame coreFrame = nullptr;
f_coreMissionData coreMissionData = nullptr;
static int onSimulationStart(lua_State* L)
{
log("onSimulationStart callback called successfully");
string modLocation;
string dllLocation;
char* buf = nullptr;
size_t sz = 0;
if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr)
{
modLocation = buf;
free(buf);
}
else
{
log("DCSOLYMPUS_PATH environment variable is missing");
goto error;
}
dllLocation = modLocation + "\\bin\\core.dll";
log("Loading core.dll");
hGetProcIDDLL = LoadLibrary(to_wstring(dllLocation).c_str());
if (!hGetProcIDDLL) {
LogError(L, "Error loading core DLL");
goto error;
}
log("Core DLL loaded successfully");
coreInit = (f_coreInit)GetProcAddress(hGetProcIDDLL, "coreInit");
if (!coreInit)
{
LogError(L, "Error getting coreInit ProcAddress from DLL");
goto error;
}
coreDeinit = (f_coreDeinit)GetProcAddress(hGetProcIDDLL, "coreDeinit");
if (!coreInit)
{
LogError(L, "Error getting coreDeinit ProcAddress from DLL");
goto error;
}
coreFrame = (f_coreFrame)GetProcAddress(hGetProcIDDLL, "coreFrame");
if (!coreFrame)
{
LogError(L, "Error getting coreFrame ProcAddress from DLL");
goto error;
}
coreMissionData = (f_coreFrame)GetProcAddress(hGetProcIDDLL, "coreMissionData");
if (!coreFrame)
{
LogError(L, "Error getting coreMissionData ProcAddress from DLL");
goto error;
}
coreInit(L);
LogInfo(L, "Module loaded and started successfully.");
return 0;
error:
LogError(L, "Error while loading module, see Olympus.log in temporary folder for additional details.");
return 0;
}
static int onSimulationFrame(lua_State* L)
{
if (coreFrame)
{
coreFrame(L);
}
return 0;
}
static int onSimulationStop(lua_State* L)
{
log("onSimulationStop callback called successfully");
if (hGetProcIDDLL)
{
log("Trying to unload core DLL");
if (coreDeinit)
{
coreDeinit(L);
}
if (FreeLibrary(hGetProcIDDLL))
{
log("Core DLL unloaded successfully");
}
else
{
LogError(L, "Error unloading DLL");
goto error;
}
coreInit = nullptr;
coreDeinit = nullptr;
coreFrame = nullptr;
coreMissionData = nullptr;
}
hGetProcIDDLL = NULL;
return 0;
error:
LogError(L, "Error while unloading module, see Olympus.log in temporary folder for additional details.");
return 0;
}
static int setMissionData(lua_State* L)
{
if (coreMissionData)
{
coreMissionData(L);
}
return 0;
}
static const luaL_Reg Map[] = {
{"onSimulationStart", onSimulationStart},
{"onSimulationFrame", onSimulationFrame},
{"onSimulationStop", onSimulationStop},
{"setMissionData", setMissionData },
{NULL, NULL}
};
extern "C" DllExport int luaopen_olympus(lua_State * L)
{
luaL_register(L, "olympus", Map);
return 1;
}