Star Trek

Manipulate[DynamicModule[{starDate, endStarDate, currentQuadCoords, enterpriseSectCoords, dockedQ,kMaxEntEnergy,kMaxKlingEnergy,
entPhotons, entEnergy, entShields, entShieldPCT,shipSystems,entDamConStatus, entCondition, theGalaxy, nStarBases, nKlingons,currentQuadrant,entWarpFactor, entDirection,
currQuadNKlingons, currQuadNStarBases, currQuadNStars, currQuadBaseCoords, currQuadKlingonsArray,entShipStatus, phaserEnergy,aimedPhotonDir,driveType,
 capLog, gameOverQ},
(* new 23 Sept *)


gameOverQ = False;

(* ---------------------------- Start of game proper  -------------------------------------- *)

{starDate, endStarDate, currentQuadCoords, dockedQ, kMaxEntEnergy, kMaxKlingEnergy, capLog, driveType} = initializeVariables[];
{entPhotons, entEnergy,entShields,entShieldPCT, entDirection, entWarpFactor, aimedPhotonDir } = setUpEnterprise[kMaxEntEnergy];
entDamConStatus = setUpShipStatus[];

{theGalaxy, nKlingons, nStarBases} = makeGalaxy[currentQuadCoords];
{currentQuadrant, currQuadNKlingons, currQuadNStarBases, currQuadNStars, enterpriseSectCoords, currQuadBaseCoords, currQuadKlingonsArray} = createQuadrantA[theGalaxy[[currentQuadCoords[[1]], currentQuadCoords[[2]]]]];

dockedQ = enterpriseDockedQ[currentQuadrant, enterpriseSectCoords];
entCondition = enterpriseCondition[dockedQ, currQuadNKlingons, entEnergy/kMaxEntEnergy];

capLog = capLog <>initialLogMessageC[starDate, endStarDate, nKlingons, nStarBases ] ;

Deploy[Panel[Row[{Column[{TabView[{"Short Range Scan" ->  Panel[Dynamic[shortRangeScan[currentQuadrant, deviceStatus[entDamConStatus, 2],starDate, entCondition, currentQuadCoords, enterpriseSectCoords, entEnergy, entPhotons, entShields, nKlingons, endStarDate]], ImageSize->{630,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ],
 "Long Range Scan" -> Panel[Dynamic[LongRangeSensorScanOutputA[theGalaxy, currentQuadCoords, deviceStatus[entDamConStatus, 3]]], ImageSize->{630,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ],
"Galaxy" -> Panel[Dynamic[galaxyStatusReport[theGalaxy,deviceStatus[entDamConStatus, 8],  nStarBases,nKlingons,entDamConStatus,starDate, entCondition, currentQuadCoords,endStarDate ]], ImageSize->{630,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ],
"Damage Control Report" -> Panel[Dynamic[damageControlReport[entDamConStatus]],
 ImageSize->{630,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ]}, Background -> Black, LabelStyle->{FontFamily->"Courier New", 13, Bold}],


 Pane[Dynamic[Style[capLog,White,FontFamily->"Courier New", 12 ]], ImageSize -> {630, 260}, DefaultBaseStyle -> "Text", Scrollbars->Automatic]}],

Dynamic[If[gameOverQ,
Panel[Column[
{Style["Game Over", FontFamily->"Courier New", 20, Bold, White ],

Button[Row[{Style["■" , Green],Style[" New Game", Italic, FontFamily->"Courier New"]}], 
(
gameOverQ = False;
{starDate, endStarDate, currentQuadCoords, dockedQ, kMaxEntEnergy, kMaxKlingEnergy, capLog, driveType} = initializeVariables[];
{entPhotons, entEnergy,entShields,entShieldPCT, entDirection, entWarpFactor, aimedPhotonDir } = setUpEnterprise[kMaxEntEnergy];
entDamConStatus = setUpShipStatus[];

{theGalaxy, nKlingons, nStarBases} = makeGalaxy[currentQuadCoords];
{currentQuadrant, currQuadNKlingons, currQuadNStarBases, currQuadNStars, enterpriseSectCoords, currQuadBaseCoords, currQuadKlingonsArray} = createQuadrantA[theGalaxy[[currentQuadCoords[[1]], currentQuadCoords[[2]]]]];

dockedQ = enterpriseDockedQ[currentQuadrant, enterpriseSectCoords];
entCondition = enterpriseCondition[dockedQ, currQuadNKlingons, entEnergy/kMaxEntEnergy];

capLog = capLog <>initialLogMessageC[starDate, endStarDate, nKlingons, nStarBases ] ;
), Enabled -> True, BaseStyle -> {White,20}, Appearance -> Frameless]


}], ImageSize -> {240, 600}, Alignment -> Center


]
,
Column[{

Panel[Column[
{
Row[{Style["    ",FontFamily->"Courier New",If[deviceStatus[entDamConStatus, 1],13,1] ], setDriveType[driveType, deviceStatus[entDamConStatus, 1]]}]
,
Row[{Style["        ",FontFamily->"Courier New", 13 ], setDriveDirection[entDirection], setDriveVelocity[entWarpFactor, driveType, entEnergy]}, Alignment->{Center, Baseline}]

,

Button[Row[{Style["■" , Green],Style[" Engage!", Italic, FontFamily->"Courier New"]}], moveEnterprise[currentQuadrant, currentQuadCoords, entWarpFactor, entDirection,enterpriseSectCoords,
theGalaxy,currQuadNKlingons, currQuadNStarBases, currQuadNStars, currQuadBaseCoords, currQuadKlingonsArray, dockedQ, entCondition,
 entEnergy, kMaxEntEnergy, entShields,starDate, capLog,entDamConStatus ,entPhotons,endStarDate, nKlingons, gameOverQ], Enabled -> True, BaseStyle -> {White,20}, Appearance -> Frameless]
}],"Navigation", Top, LabelStyle->{20,White, Bold, FontFamily -> "Courier New"}, ImageSize -> {240, 180}, FrameMargins -> 0, Background -> Black]

,


Panel[Column[{photonControl[aimedPhotonDir, entPhotons, deviceStatus[entDamConStatus, 5] ],


If[(entPhotons > 0 && deviceStatus[entDamConStatus, 5]),
Button[Row[{Style["■" , Green],Style[" Fire Torpedo!", Italic,FontFamily -> "Courier New"]}],firePhoton[entEnergy,aimedPhotonDir,capLog, currQuadNKlingons, currQuadKlingonsArray,
 entDamConStatus,entShields,dockedQ, enterpriseSectCoords, nKlingons, currentQuadrant, theGalaxy,
 currentQuadCoords, kMaxEntEnergy, entCondition, entPhotons, starDate, endStarDate, gameOverQ],Enabled -> True,BaseStyle -> {White, 20}, Appearance -> Frameless],
Button[Row[{Style["■" , Red],Style[" Fire Torpedo!", Italic,FontFamily -> "Courier New"]}],Beep[],Enabled -> False,BaseStyle -> {White, 20}, Appearance -> Frameless]]


}],"Photon Torpedoes", Top, LabelStyle->{20,White, Bold, FontFamily -> "Courier New"}, ImageSize -> {240, 135},FrameMargins -> 0, Background -> Black]
,

Panel[Column[{phaserControl[deviceStatus[entDamConStatus, 4], phaserEnergy,entEnergy],

If[deviceStatus[entDamConStatus, 4],
Button[Row[{Style["■" , Green],Style[" Fire Phasers!", Italic, FontFamily->"Courier New"]}], firePhasers[entEnergy,phaserEnergy,capLog, currQuadNKlingons, currQuadKlingonsArray,
 entDamConStatus,entShields,dockedQ, enterpriseSectCoords, nKlingons, currentQuadrant, theGalaxy,
 currentQuadCoords, kMaxEntEnergy, entCondition, entPhotons, starDate, endStarDate, gameOverQ], Enabled -> True, BaseStyle -> {White,20}, Appearance -> Frameless],
Button[Row[{Style["■" , Red],Style[" Fire Phasers!", Italic, FontFamily->"Courier New"]}], Beep[], Enabled -> False, BaseStyle -> {White,20}, Appearance -> Frameless]]
}],"Phasers", Top, LabelStyle->{20,White, Bold, FontFamily -> "Courier New"}, ImageSize -> {240, 100}, FrameMargins -> 0, Background -> Black]

,


Panel[Column[{shieldControl[entShieldPCT,entEnergy, entShields, deviceStatus[entDamConStatus, 7]],

If[deviceStatus[entDamConStatus, 7],
Button[Row[{Style["■" , Green],Style[" Set Shields!", Italic,FontFamily -> "Courier New"]}],setShields[entShieldPCT, entEnergy, entShields,
 entCondition, currQuadNKlingons,dockedQ,kMaxEntEnergy],Enabled -> True,BaseStyle -> {White, 20}, Appearance -> Frameless],
Button[Row[{Style["■" , Red],Style[" Set Shields!", Italic,FontFamily -> "Courier New"]}],Beep[],Enabled -> False,BaseStyle -> {White, 20}, Appearance -> Frameless]]

}]
,"Shield Control", Top, LabelStyle->{20, White, Bold, FontFamily -> "Courier New"}, ImageSize -> {240, 110}, FrameMargins -> 0, Background -> Black]


}]

]]
  }, Frame -> All, Background->Black],ImageSize->{910, 635}, Background->Black]]

],
{driveType, 0,1, ControlType -> None},
{entDirection, 0, 360, ControlType->None},
{entWarpFactor, 0.01, 8, ControlType->None},
{aimedPhotonDir, 0, 360, ControlType->None},
{phaserEnergy, 0, 3000, ControlType->None},
{entShieldPCT, 0, 100, ControlType->None},
{entEnergy, 0, 3000, ControlType->None},
{entShields, 0, 3000, ControlType->None},

Paneled->False,


SaveDefinitions->True
]

Implementation

setUpEnterprise[maxEnergy_] := Module[{maxPhotons, energy, shieldStrength, shieldPct, direction, velocity},
maxPhotons = 10;
energy = maxEnergy * 1.0;
shieldStrength = 0.0;
shieldPct = 0.0;
direction = 0.0;
velocity = 0.01;

{maxPhotons, energy,shieldStrength, shieldPct, direction, velocity, direction}
]
srStatus[sDate_,eCondition_, currQuad_, eSector_, eEnergy_, nPhotons_, shields_, totKlingons_, endDate_] :=
Module[{myGrid},
myGrid = Style[Grid[{{"=================================", SpanFromLeft},
{"     SHORT   RANGE   REPORT     ", SpanFromLeft},
{"=================================", SpanFromLeft},
{"STARDATE", sDate},
 {"CONDITION", eCondition},
 {"QUADRANT", currQuad},
 {"SECTOR", eSector},
{"TOTAL ENERGY", NumberForm[eEnergy,{4, 0}]},
 {"PHOTON TORPEDOES", nPhotons},
{"SHIELDS", If[shields > 0, Row[{"UP: ",NumberForm[shields, {4,0}]}], "DOWN"]},
{"KLINGONS LEFT", totKlingons}, {"TIME LEFT", endDate - sDate},
{"=================================", SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"},
 Background->Black, Spacings -> {2,Automatic}], 14];
myGrid

]
shortRangeScan[theQuadrant_,srUPQ_, sDate_, eCondition_, currQuad_, eSector_, eEnergy_, nPhotons_, shields_, totKlingons_, endDate_] :=
 Module[{scan,status},
scan = quadrantDisplay[theQuadrant, currQuad,srUPQ];
status = srStatus[sDate,eCondition, currQuad, eSector, eEnergy, nPhotons, shields, totKlingons, endDate];
Grid[{{status, scan}},Background->Black, Frame-> True]
]
initializeVariables[] := Module[ {sDate, currQuad, dockedQ, maxEEnergy, klingMaxE, logStart, driveT},
sDate = Floor[(RandomReal[] * 20 + 20) *100];
currQuad = RandomInteger[{1,8},2];
dockedQ = False;
maxEEnergy = 3000.0;
klingMaxE = 200.0;
driveT = 1;
logStart = "v1.1.0            ---- S  T  A  R    T  R  E  K ----\r\r Captain's Log: ";
{sDate, sDate + 40, currQuad, dockedQ, maxEEnergy, klingMaxE, logStart, driveT}
]

SetAttributes[longRangeScanUpdateGalaxy, HoldFirst];

longRangeScanUpdateGalaxy[theGalaxy_, theEntQuadrant_] := Module[{ rowE = theEntQuadrant[[1]], colE = theEntQuadrant[[2]], i, j, startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
For[i = startRow, i ≤ endRow, i++,
For[j = startCol, j ≤ endCol, j++,
theGalaxy[[i, j]] = makeQuadrantVisible[theGalaxy, {i,j}]
]
];
]

longRangeScanDisplay[theGalaxy_, theEntQuadrant_] := Module[{LRarray,LRgrid,topLegend, leftLegend, rowE = theEntQuadrant[[1]], colE = theEntQuadrant[[2]], i, j, startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
LRarray = theGalaxy[[startRow ;; endRow, startCol ;; endCol]];
LRgrid = Grid[Map[formatLongRangeElement,LRarray, {2}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin,Dashed, Gray}, ItemSize -> 3, Spacings -> {0, 2},
 Background -> Black];
leftLegend = Table[i, {i,startRow , endRow}];
topLegend = Table[i, {i, startCol, endCol}];
Style[Grid[{{Style["LR", White, Bold],Grid[ {topLegend}, ItemSize -> 3, Spacings -> {0,2}, ItemStyle -> {Italic, White}]}, {Grid[Transpose[{leftLegend}], Spacings -> {0,2}, ItemStyle -> {Italic, White}], LRgrid}}, Background->Black], 13]
]

(* for now, when LR sensors are down, show the grid with "???" *)

longRangeScanDispDOWN[theGalaxy_, theEntQuadrant_] := Module[{LRarray,LRgrid,topLegend, leftLegend, rowE = theEntQuadrant[[1]], colE = theEntQuadrant[[2]], i, j, startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
LRarray = Table["???", {i,startRow , endRow}, {j,startCol , endCol}];
LRgrid = Grid[LRarray, Alignment -> Center, Frame -> All,FrameStyle -> {Thin,Dashed, Gray}, ItemSize -> 3, Spacings -> {0, 2}, Background -> {None, None, {{1+ rowE - startRow,1 + colE - startCol} -> LightGray}}];
leftLegend = Table[i, {i,startRow , endRow}];
topLegend = Table[i, {i, startCol, endCol}];
Grid[{{"LR",Grid[ {topLegend}, ItemSize -> 3, Spacings -> {0,2}, ItemStyle -> Italic]}, {Grid[Transpose[{leftLegend}], Spacings -> {0,2}, ItemStyle -> Italic], LRgrid}}]
]


SetAttributes[longRangeGalaxyUpdate, HoldFirst];
longRangeGalaxyUpdate[theGalaxy_, theEntQuadrant_, LRscannersWorkingQ_] :=
Module[{},
If[LRscannersWorkingQ, 
longRangeScanUpdateGalaxy[theGalaxy, theEntQuadrant];

,
theGalaxy[[theEntQuadrant[[1]], theEntQuadrant[[2]]]] = makeQuadrantVisible[theGalaxy, theEntQuadrant];
];
]

SetAttributes[LongRangeSensorScanOutputA, HoldFirst];

LongRangeSensorScanOutputA[theGalaxy_, theEntQuadrant_, LRscannersWorkingQ_] :=
Module[{myGrid,scanOutput, mySeparator},
mySeparator = "======================";
myGrid = Style[Grid[{{mySeparator, SpanFromLeft},
{" LONG  RANGE  SCAN  ", SpanFromLeft},
{mySeparator, SpanFromLeft},
{"   QUADRANT", theEntQuadrant},
{mySeparator, SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}},
 Background->Black], White, FontFamily -> "Courier New", 14 ];

longRangeGalaxyUpdate[theGalaxy, theEntQuadrant, LRscannersWorkingQ];
If[LRscannersWorkingQ, 

scanOutput = longRangeScanDisplay[theGalaxy, theEntQuadrant];
,

scanOutput = longRangeScanDispDOWN[theGalaxy, theEntQuadrant];];
Grid[{myGrid,scanOutput}]]

createQuadrantA[galaxyElement_] := Module[{quadData, nK, nB, nS, entCoord, baseCoord, klingons, theQuadrant, i}, 
quadData = Take[IntegerDigits[1000 + galaxyElement], -3];
{nK,nB,nS} = quadData;
theQuadrant =Partition[RandomSample[Join[Table[i, {i, 1,nK}],Table["B", {nB}],Table["*", {nS}],{"E"}, Table["", {63 - Total[quadData]}]]],8];

entCoord = Position[theQuadrant, "E"][[1]];
If[nB > 0, baseCoord = Position[theQuadrant, "B"][[1]], baseCoord ={0,0}];

(* Bug Fix: 29 Sept 2013 need to create a table of 3 Klingons no matter what. 
Rely on energy ≠ 0 to flag that the ship is real. Otherwise we'll exceed loops.
The coordinates set are legal but meaningless *)
klingons = Table[{{1,1},0}, {3}];
i = 1;
While[i ≤ nK, (klingons[[i, 1]] = Position[theQuadrant, i][[1]];
klingons[[i, 2]] = 200.0;);
i++];
{theQuadrant, nK, nB, nS, entCoord, baseCoord, klingons}
]

(* Note that we handle SR sensors down within the function *)

quadrantDisplay[theQuadrant_,theEntQuadrant_,  srUPQ_] :=Module[{hdr, qdrnt, i,j},
If[srUPQ,(qdrnt =Grid[Map[formatSectorElementA,theQuadrant, {2}], Alignment -> Center, Frame -> All,
FrameStyle -> {Thin, RGBColor[.15, .15, .15]},
 Dividers -> {{1 -> If[theEntQuadrant[[2]] == 1,{ Darker[Red],Thick, DotDashed}, {Thin, RGBColor[.15, .15, .15]}] ,
 -1 -> If[theEntQuadrant[[2]] == 8, { Darker[Red], Thick, DotDashed}, {Thin, RGBColor[.15, .15, .15]}] },
 {1 -> If[theEntQuadrant[[1]] == 1, { Darker[Red],Thick, DotDashed}, {Thin, RGBColor[.15, .15, .15]}] ,
 -1 -> If[theEntQuadrant[[1]] == 8, { Darker[Red],Thick, DotDashed}, {Thin, RGBColor[.15, .15, .15]}] }},
 ItemSize -> 2, Spacings -> {0, 1}, Background -> Black];
)
,
(qdrnt = Grid[Table[Style["?", Gray, FontSize->7], {i,1,8}, {j, 1,8}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 2, Spacings -> {0, 1}, Background -> Black];
)];
hdr = Table[i, {i, 1,8}];
Style[Grid[{{Style["SR", White, Bold],Grid[ {hdr}, ItemSize -> 2, Spacings -> {0,2}, ItemStyle -> {Italic, White}]}, 
{Grid[Transpose[{hdr}], ItemSize -> 2, Spacings -> {0,1}, ItemStyle -> {Italic, White}, Background->Black], qdrnt}}, Background->Black, Frame -> True],14]

]
(* In keeping with the ASCII tradition, we draw the elements with characters. *)

formatSectorElementA[element_] := Module[{retString = ""},
If[element == "B", retString = Style["B", FontFamily->"Courier New", Bold, Green]];
If[element == "E", retString = Style["E", FontFamily->"Courier New", Bold, White]];
If[IntegerQ[element], retString = Style["K", FontFamily->"Courier New", Bold, Red]];
If [element == "*", retString = Style["*", FontFamily->"Courier New", Bold, Yellow]];

retString
]
(* -------------------------------------------------   makeGalaxy   ------------------------------------------------- *)
(* We make separate 8 by 8 arrays for klingons, starbases, and stars. We make sure that there is at least 1 starbase *)
(* We also need the total number of klingons and starbases. We make the galaxy by adding the separate arrays. *)
(* Finally, we make the quadrant that the Enterprise is in visible *)

makeGalaxy[currentQuadrantCoords_] := Module[{klArray, sbArray, stArray, nKlingons, nStarBases, galaxy},
klArray = Table[makeKlingons[], {i, 1, 8}, {j, 1, 8}];
nKlingons = Total[klArray, 2];
sbArray = Table[makeStarBases[], {i, 1, 8}, {j, 1, 8}];
nStarBases = Total[sbArray, 2];
If[nStarBases == 0, (sbArray[[RandomInteger[{1,8}],RandomInteger[{1,8}]]] = 1;
nStarBases = 1;)];
stArray = Table[RandomInteger[{1,8}], {i, 1, 8}, {j, 1, 8}];
galaxy = 100 * klArray + 10 * sbArray + stArray;
galaxy[[currentQuadrantCoords[[1]], currentQuadrantCoords[[2]] ]] += 1000;
{galaxy, nKlingons, nStarBases}]

makeStarBases[] := If[RandomReal[] > 0.96, 1, 0]

(* We made it easier to make more Klingons. The original was .8, .95, .98 *)
makeKlingons[] := Module[{rNum, nKlingons}, rNum = RandomReal[];
nKlingons = 0;
If[rNum > 0.8, nKlingons ++];
If[rNum > 0.92, nKlingons ++];
If[rNum > 0.955, nKlingons ++];
nKlingons]


(* -------------------------------------------------   galaxyDisplay   ------------------------------------------------- *)


galaxyDisplay[theGalaxy_, computerUPQ_] :=Module[{hdr, galax, i},

If[computerUPQ,
( galax =Style[Grid[Map[formatLongRangeElement,theGalaxy, {2}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 3, Spacings -> {0, 1}, Background -> Black], 13];)
,
(galax = Style[Grid[Table[Style["?", Gray, FontSize->7], {i,1,8}, {j, 1,8}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 3, Spacings -> {0, 1}, Background -> Black], 13];
)];
hdr = Table[i, {i, 1,8}];
Style[Grid[{{Style[ "ST", FontFamily->"Courier New", White, Bold],Grid[ {hdr}, ItemSize -> 3, Spacings -> {0,1}, ItemStyle -> { Italic, White}]}, 
{Grid[Transpose[{hdr}], ItemSize -> 2, Spacings -> {0,1}, ItemStyle -> {Italic, White}, Background->Black], galax}}, Background->Black, Frame -> True], 13]
]

(* if there is a 1 in the 1000 place, return the value of the array mod 1000. This way we only show quadrants 'seen' by a LR scan *)
formatLongRangeElement[element_] := Module[{retString = ""},
 If[element >= 1000, retString = IntegerString[Mod[element, 1000]]];
Style[retString, FontFamily->"Courier New", Bold, White]
]

makeQuadrantVisible[theGalaxy_, currentQuadrant_] := Module[{qData}, 
qData = theGalaxy[[currentQuadrant[[1]], currentQuadrant[[2]]]];
1000 + Mod[qData,1000]]

upDateGalaxy[theGalaxy_, x_, y_] := Module[{testGal = theGalaxy},testGal[[x,y]]= makeQuadrantVisible[testGal, {x,y}];
testGal]

(* -------------------------------------------------   galaxyStatus   ------------------------------------------------- *)


galaxyStatus[ totSBs_, totKlingons_, entCompnentStatus_,sDate_, eCondition_, currQuad_, endDate_, computerUPQ_] :=
Module[{myGrid, mySeparator},
mySeparator = "=================================";
If[computerUPQ,
(myGrid = Style[Grid[{{mySeparator, SpanFromLeft},
{"G A L A X Y      R E P O R T  ", SpanFromLeft},
{mySeparator, SpanFromLeft}, 
{"STARDATE", sDate}, 
{"CONDITION", eCondition}, 
{"QUADRANT", currQuad}, 
{"STAR BASES", totSBs},
{"KLINGONS LEFT", totKlingons},
{"TIME LEFT", endDate - sDate}}, Alignment -> {{Right, Left}, {Center, Center}},
  Background->Black, Spacings -> {2,Automatic}],White, FontFamily -> "Courier New", 13];)
,
(myGrid = Style[Grid[{
{mySeparator, SpanFromLeft},
{"G A L A X Y      R E P O R T  ", SpanFromLeft},
{mySeparator,SpanFromLeft}, 
{"STARDATE", sDate},
{"CONDITION", eCondition}, 
{"QUADRANT", currQuad}, 
{"STAR BASES", "????"},
{"KLINGONS LEFT", "????"},
{"COMPUTER IS DOWN!        ", SpanFromLeft},
{mySeparator, SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, Background->Black, Spacings -> {2,Automatic}], White, FontFamily -> "Courier New", 13];)];

Column[{myGrid}, Background -> Black]

]

(* -------------------------------------------------   galaxyStatusReport   ------------------------------------------------- *)

galaxyStatusReport[theGalaxy_,computerUPQ_, totSBs_, totKlingons_, entComponentStatus_, starDate_, entCond_,currentQuad_, endSDate_] := Module[{scan,status},
scan = galaxyDisplay[theGalaxy,computerUPQ];
status = galaxyStatus[totSBs, totKlingons, entComponentStatus, starDate, entCond, currentQuad, endSDate,  computerUPQ];
Grid[{{status, scan}},Background->Black, Frame-> True]
]

supplyEnterprise[maxEnergy_] := Module[{maxPhotons, energy},
maxPhotons = 10;
energy = maxEnergy * 1.0;
{maxPhotons, energy}
]


setUpShipStatus[] := Module[{components, status},
components = {"WARP ENGINES", "S. R.SENSORS", "L. R. SENSORS", "PHASER CONTROL",
 "PHOTON TORPEDOS", "DAMAGE CONTROL", "SHIELD CONTROL", "COMPUTER"};
status = Table[0.0, {Length[components]}];
{components, status}]
damageControlReport[entStatus_] :=
Module[{myGrid, mySeparator},
mySeparator = "=================================";
If[entStatus[[2,6]] ≥ 0.0,
(myGrid = Style[Grid[{ {mySeparator, SpanFromLeft},
{"   DAMAGE CONTROL REPORT     ", SpanFromLeft},

{entStatus[[1,1]], formatDCelement[entStatus[[2,1]]]},
{entStatus[[1,2]], formatDCelement[entStatus[[2,2]]]},
{entStatus[[1,3]], formatDCelement[entStatus[[2,3]]]},
{entStatus[[1,4]], formatDCelement[entStatus[[2,4]]]},
{entStatus[[1,5]], formatDCelement[entStatus[[2,5]]]},
{entStatus[[1,6]], formatDCelement[entStatus[[2,6]]]},
{entStatus[[1,7]], formatDCelement[entStatus[[2,7]]]},
{entStatus[[1,8]], formatDCelement[entStatus[[2,8]]]},

{mySeparator, SpanFromLeft} },
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"}, Background->Black, Spacings -> {2,Automatic}], 13];)
,
(myGrid = Style[Grid[{{"-- DAMAGE CONTROL REPORT  -- ", SpanFromLeft},{"DAMAGE CONTROL IS DAMAGED   ", SpanFromLeft},
{mySeparator, SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"}, Background->Black, Spacings -> {2,Automatic}], 13];)];

myGrid

]

formatDCelement[statusNumber_] :=
Module[{retString = Style["GOOD", FontFamily->"Courier New", Bold, Green]},
If[statusNumber < 0.0, retString = Style[ToString[Row[{"FIX IN ",Ceiling[Abs[statusNumber]], " DAYS"}]],FontFamily->"Courier New", Bold, Red]];
retString
]
deviceStatus[entStatus_, deviceNum_] := 
If[deviceNum ≥ 1 && deviceNum ≤ 8,entStatus[[2,deviceNum]] ≥ 0.0, False]

enterpriseDockedQ[currentQuadrant_, entcoords_] := Module[{nearby,rowE = entcoords[[1]], colE = entcoords[[2]], startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
nearby = currentQuadrant[[startRow ;; endRow, startCol ;; endCol]];
MemberQ[Flatten[nearby], "B"]
]
enterpriseCondition[amIDockedQ_, nKlingons_, energyFraction_] := Module[{status = Style["GREEN", FontFamily->"Courier New", Bold, Green]},
If[energyFraction <.1, status = Style["YELLOW", FontFamily->"Courier New", Bold, Yellow]];
If[nKlingons > 0, status = Style["RED", FontFamily->"Courier New", Bold, Red]];
If[amIDockedQ, status = Style["DOCKED", FontFamily->"Courier New", Bold, Green]];
status
]
photonSound = ;
phaserSound = ;
redAlertSound = ;
console1Sound = ;
(* ================================================   shieldControl   ================================================ *)
SetAttributes[shieldControl,  HoldFirst];
shieldControl[shieldPCT_,eEnergy_, sEnergy_, shieldControlOKQ_] := DynamicModule[{tEnergy, newEnergy, newShields, currPCT},
tEnergy = sEnergy + eEnergy;

currPCT = If[(tEnergy ≥ 0.0),100.0 * sEnergy/(tEnergy + 0.00001), 0.0];

Panel[If[(shieldControlOKQ && (tEnergy > 0.0) ) , 
(
Column[{
Dynamic[Control[{{shieldPCT, currPCT},0, 100,ControlType -> Slider, ImageSize -> Medium}]],
Dynamic[Style[Row[{"Enterprise Energy : ",NumberForm[0.01 * (100 -shieldPCT) * tEnergy, {4,0}, NumberPadding -> {" ", ""}],
" \rShield Energy:      ", NumberForm[0.01 * shieldPCT* tEnergy, {4,0}, NumberPadding -> {" ", ""}]}],13, White, FontFamily -> "Courier New"]]}]


)
,
(
Column[{

Text[Style["Shield Control DOWN", 17,Bold, Red,FontFamily -> "Courier New"]]}]
)], Background->Black,ImageSize->{250, 80}, Alignment->{Left, Top}

]
];



(* ================================================   setShields   ================================================ *)

(* When we set shields, we also have to compute the enterprise condition *)
(* The BASIC game uses energy % of non-shield energy. I think it should be total energy, but I left it as the BASIC game had it. *)
SetAttributes[setShields,HoldAll];
setShields[theShieldPCT_,  entNRG_, shNRG_, eCond_, nKling_, amIDockedQ_, maxEE_] :=
Module[{theTotEnergy, tmpPct},


EmitSound[console1Sound];


theTotEnergy =entNRG + shNRG;
tmpPct = theShieldPCT;
entNRG = 0.01 * (100.0 - theShieldPCT) * theTotEnergy;
shNRG = 0.01 * theShieldPCT * theTotEnergy;
eCond = enterpriseCondition[amIDockedQ, nKling, entNRG/maxEE];
];

(* ================================================   pControl   ================================================ *)
SetAttributes[photonControl,HoldAll];
photonControl[heading_ , nTorpsLeft_, photonsOKQ_] := DynamicModule[{photonVec, okToFireQ},


photonVec = {Sin[heading * π/180.], Cos[heading * π/180.]};
okToFireQ = photonsOKQ && nTorpsLeft > 0;
If[okToFireQ,
Panel[Row[{LocatorPane[Dynamic[myTempVec],Graphics[{White,Disk[],LightGray,Thickness[0.11],Circle[{0,0}, 0.8],Thin, Black,
Table[Line[{{Sin[i π/4], Cos[i π/4]},{.1Sin[i π/4], .1Cos[i π/4]}}], {i, 0, 7}], Thick,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.9Sin[i π/4], .9Cos[i π/4]}}], {i, 0, 7}],Red,Disk[{0,0}, .1],
Arrowheads[Medium],Arrowheads[.15],Arrowheads[Medium], Arrow[{{0,0},Dynamic[Normalize[myTempVec]]}]},ImageSize -> 75, PlotRange -> 1.1],{{-1,-1},{1,1}},Appearance->None],


Column[{Style[ToString[Row[{" Torpedos Left: ",nTorpsLeft}]],FontFamily->"Courier New", Bold,If[nTorpsLeft > 1, White, Red], 13],

Dynamic[Style[" Torpedo Heading:\r  "<>ToString[NumberForm[heading =Round[Mod[-((180./π)Arg[myTempVec[[1]] + I myTempVec[[2]]]- 90), 360], 5],3, NumberPadding -> {"0", ""}]], White, 13, Bold, FontFamily -> "Courier New"]]
}]
}],ImageSize->{250, 100} ,  Background ->  Black]
,
Panel[If[photonsOKQ,
 Text[Style["No Photon Torpedos left!", Red, 14,Bold, FontFamily -> "Courier"]],
 Text[Style["Photon Control Is Down!", Red, 14,Bold, FontFamily -> "Courier"]]
],Alignment -> Center,ImageSize->{250, 100} ,  Background ->  Black]
]

]
(* ================================================   phaserControl   ================================================ *)
SetAttributes[phaserControl,HoldAll];
phaserControl[phasersOKQ_, phsrEnergy_,eEnergy_] := DynamicModule[{},


Panel[
If[(phasersOKQ ),
(
Column[{
Dynamic[Control[{{phsrEnergy, eEnergy/20.},0, eEnergy,ControlType -> Slider, ImageSize -> Medium}]],
Dynamic[Style[Row[{"Phaser Energy : ", NumberForm[phsrEnergy, {4,0}, NumberPadding -> {" ", ""}]}],12, White, FontFamily -> "Courier New"]]


}]),

Text[Style[" Phasers DOWN", 20,Bold, Red,FontFamily -> "Courier New"]]]

, ImageSize -> {250, 60},Background -> Black]]




(* ================================================   firePhoton   ================================================ *)

SetAttributes[firePhoton, HoldAll];
firePhoton[eEnergy_,photonDir_,log_, nQuadKlings_, klingsArray_,sDamageControl_, eShields_,dockStatus_, 
EntPos_, totKlingons_, theQuad_, theGalaxy_, curGQuad_, maxEntEnergy_, eCond_, nPhots_, curSDate_, finSDate_, isGameOverQ_] := Module[{vector, testPos, testCoords, okQ, kNum, myLog},

EmitSound[photonSound];
myLog = "Torpedo away: ";
nPhots--;
vector = { -Cos[photonDir * π/180], Sin[photonDir * π/180]};

testPos = EntPos + vector;
testCoords = Round[testPos];

okQ = True;
While[okQ,

If[testCoords[[1]] > 8 || testCoords[[1]] < 1 || testCoords[[2]] > 8 || testCoords[[2]] < 1,
(
(* The Photon torpedo left the quadrant *)
okQ = False;
myLog = myLog <> " Torpedo missed!\r";

),
(
myLog = myLog <> ToString[Row[{"{", testCoords[[1]],",", testCoords[[2]],"}->" }]];
If [theQuad[[testCoords[[1]], testCoords[[2]] ]] != "",
(
(* We hit something. Find out what *)
If[theQuad[[testCoords[[1]], testCoords[[2]] ]] == "B", (
myLog = myLog <> " Starbase shields Absorbed the hit. Aim better next time!\r";
okQ = False;
)];
If[theQuad[[testCoords[[1]], testCoords[[2]] ]] == "*", (
myLog = myLog <> " You can't destroy a star!\r";
okQ = False;
)];
If[IntegerQ[theQuad[[testCoords[[1]], testCoords[[2]] ]] ], (
myLog = myLog <> " Klingon destroyed!\r";
kNum =theQuad[[testCoords[[1]], testCoords[[2]] ]];
klingsArray[[kNum, 2]] = 0.0;
theQuad[[testCoords[[1]], testCoords[[2]] ]] = "";
nQuadKlings --;
totKlingons --;
decrementKlingonInGalQuadrant[theGalaxy, curGQuad];
okQ = False;
)];


),
(
(* We are on the path *)
testPos += vector;
testCoords = Round[testPos];

)];
)];
];
dockStatus = enterpriseDockedQ[theQuad, EntPos];

log = log <> myLog;

If[dockStatus,(myLog = myLog<> " Docked. Starbase shields protect the ship.\r";
{nPhots, eEnergy} =supplyEnterprise[maxEntEnergy];
eShields = 0.0;
sDamageControl[[2,1]] = 0.0;
sDamageControl[[2,2]] = 0.0;
sDamageControl[[2,3]] = 0.0;
sDamageControl[[2,4]] = 0.0;
sDamageControl[[2,5]] = 0.0;
sDamageControl[[2,6]] = 0.0;
sDamageControl[[2,7]] = 0.0;
sDamageControl[[2,8]] = 0.0;

)];

(* If there are any klingons in the quadrant, they can shoot back *)
If[nQuadKlings > 0, klingonAttack[nQuadKlings,klingsArray, sDamageControl, eEnergy, eShields,dockStatus, log, EntPos, curSDate, finSDate, totKlingons, isGameOverQ]];

eCond = enterpriseCondition[dockStatus, nQuadKlings, eEnergy/maxEntEnergy];
checkEndGame[curSDate, finSDate, eEnergy, eShields, totKlingons, log, sDamageControl, isGameOverQ];

];
(* ================================================   firePhasers   ================================================ *)

SetAttributes[firePhasers, HoldAll];
firePhasers[eEnergy_,phasNRG_,log_, nQuadKlings_, klingsArray_,sDamageControl_, eShields_,dockStatus_,EntPos_, totKlingons_, theQuad_, theGalaxy_, curGQuad_, maxEntEnergy_, eCond_, nPhots_, curSDate_, finSDate_, isGameOverQ_] := 
Module[{accuracy, kNum, actualNRG, dist, hitEnergy, startNKl, klDestroyed, klFleetNRG, myLog},

(* spend the energy up front, even if we did not use all of it. *)
eEnergy -= phasNRG;
myLog = "Firing Phasers : ";
startNKl =nQuadKlings;
(* a damaged computer reduces accuracy *)
accuracy = 1.0;
If[sDamageControl[[2,8]] < 0, (
accuracy -= 0.1 * RandomReal[] * Abs[Max[sDamageControl[[2,8]], 0.0]];
myLog = myLog <> " Damaged computer hampers accuracy\r";
)];
actualNRG = accuracy * phasNRG;

klDestroyed = 0;
(* We must allow for a dumb captain shooting phasers at nothing. *)
If[nQuadKlings > 0,
(
For[kNum = 1, kNum <= Length[klingsArray], kNum++,
If[klingsArray[[kNum, 2]] > 0.0,
(
	dist = Max[N[EuclideanDistance[klingsArray[[kNum, 1]], EntPos]], 1.0];
	hitEnergy =  Floor[(actualNRG/(Sqrt[dist] * nQuadKlings)) * (1.5 + RandomReal[{-0.5, 0.5}])];
	EmitSound[phaserSound];
	klingsArray[[kNum, 2]] -=  hitEnergy;
	If[klingsArray[[kNum, 2]] <= 0,
	(
		klDestroyed ++;
		nQuadKlings --;
		totKlingons--;
		theQuad[[klingsArray[[kNum, 1,1]],klingsArray[[kNum, 1,2]] ]] = "";
		decrementKlingonInGalQuadrant[theGalaxy, curGQuad];
	)];
)];
];

(* Just give a battle summary *)
klFleetNRG = 0.0;
For[kNum = 1, kNum <= Length[klingsArray], kNum++,
If[klingsArray[[kNum, 2]] >0.0, klFleetNRG += klingsArray[[kNum, 2]]];
];
If[nQuadKlings == 0, myLog = myLog <>"All Klingons Destroyed!\r" ];
If[nQuadKlings > 0, myLog = myLog <> ToString[Row[{klDestroyed," Klingons destroyed; ", nQuadKlings, " left with ", klFleetNRG," total energy.\r"}]] ];
)
,
myLog = myLog <>"Firing at empty space just wastes energy!";
];



dockStatus = enterpriseDockedQ[theQuad, EntPos];
If[dockStatus,(myLog = myLog<> " Docked. Starbase shields protect the ship.\r";
{nPhots, eEnergy} =supplyEnterprise[maxEntEnergy];
eShields = 0.0;
sDamageControl[[2,1]] = 0.0;
sDamageControl[[2,2]] = 0.0;
sDamageControl[[2,3]] = 0.0;
sDamageControl[[2,4]] = 0.0;
sDamageControl[[2,5]] = 0.0;
sDamageControl[[2,6]] = 0.0;
sDamageControl[[2,7]] = 0.0;
sDamageControl[[2,8]] = 0.0;

)];

log = log <> myLog;

(* If there are any klingons in the quadrant, they can shoot back *)
If[nQuadKlings > 0, klingonAttack[nQuadKlings,klingsArray, sDamageControl, eEnergy, eShields,dockStatus, log, EntPos, curSDate, finSDate, totKlingons, isGameOverQ]];

eCond = enterpriseCondition[dockStatus, nQuadKlings, eEnergy/maxEntEnergy];
checkEndGame[curSDate, finSDate, eEnergy, eShields, totKlingons, log, sDamageControl, isGameOverQ];
]; 


SetAttributes[decrementKlingonInGalQuadrant, HoldAll];
decrementKlingonInGalQuadrant[theGal_,currQuad_] := Module[{quadRep, nKlings, myDigits},
quadRep = theGal[[currQuad[[1]], currQuad[[2]]]];
myDigits = IntegerDigits[quadRep];
nKlings = myDigits[[-3]];
nKlings --;
If[nKlings <0, nKlings = 0];
myDigits[[-3]] = nKlings;
theGal[[currQuad[[1]], currQuad[[2]]]] = FromDigits[myDigits];
];

(* ================================================   klingonAttack   ================================================ *)
SetAttributes[klingonAttack,HoldAll];

klingonAttack[currQuadNKling_,currQuadKlingArray_, eDamConStatus_, eEnergy_, shEnergy_, amIDockedQ_, logMessage_, entCords_, cSDate_, eSDate_, tKlings_, isGameOverQ_] := Module[{doneQ = False, kNum, hitEnergy = 0.0, dist, ergLoss, damDev, damage, myMess, nKlAttacking, hitPoints},

myMess = "";
nKlAttacking = 0;
hitPoints = 0.0;

If[amIDockedQ, (doneQ = True;
myMess = myMess<> "Starbase shields protect ship from attack!\r"; )];
If[currQuadNKling ≤ 0, doneQ = True];

If[!doneQ,
EmitSound[redAlertSound];
myMess = "Klingon Attack: ";
(* We need to loop through the entire array, only acting if the energy is > 0.0 *)
For[kNum = 1, kNum <= Length[currQuadKlingArray], kNum++,
If[currQuadKlingArray[[kNum, 2]] >0.0,(
dist = Max[N[EuclideanDistance[currQuadKlingArray[[kNum, 1]], entCords]], 1.0];
hitEnergy = Floor[(currQuadKlingArray[[kNum, 2]]/dist) * 2.0 * RandomReal[]];
shEnergy -=  hitEnergy;
nKlAttacking ++;
hitPoints += hitEnergy;
(* We want to disable a device if the shield energy is negative. This is not in the original game. *)
If[shEnergy < 0,(
ergLoss = Abs[shEnergy];
shEnergy = 0.0;
eEnergy -= ergLoss;
damDev = RandomInteger[{1,8}];
damage = 1.0 + RandomReal[] * 2.0;
eDamConStatus[[2,damDev]] -= damage;
myMess = myMess <> ToString[Row[{eDamConStatus[[1,damDev]], " Damaged. ", NumberForm[damage,6], " units \r"}]];
)];
)];
]; 
If[nKlAttacking == 1,
myMess = myMess <>ToString[Row[{hitPoints, " unit hit from Klingon, Shield energy: ",shEnergy," \r"}]],
myMess = myMess <>ToString[Row[{hitPoints, " unit hits from ", nKlAttacking, " Klingons, Shield energy: ", shEnergy, " \r" }]]
];

];
logMessage = logMessage <> myMess;
checkEndGame[cSDate, eSDate, eEnergy, shEnergy, tKlings, logMessage, eDamConStatus, isGameOverQ];
];



(* ================================================   checkEndGame   ================================================ *)

SetAttributes[checkEndGame,HoldAll];
checkEndGame[curSDate_, endSDate_, eERGY_, eShields_, totKlings_, myLog_, eDamageControlStats_, gameOverQ_] := Module[{totNRG, allDots, endDots, endingQ, myMess},


allDots = "*******************************************************************\r";
endDots = "**\r";
endingQ = False;
gameOverQ = False;
myMess = "";
totNRG = eERGY + eShields;

If[totKlings ≤ 0, (myMess = allDots;
myMess = myMess <> endDots;
myMess = myMess <> ToString[Row[{"**     It is Stardate: ",curSDate, ". All Klingon vessels have been Destroyed!\r"}]];
myMess = myMess <> "**                Y O U    W I N !\r";
myMess = myMess <> endDots;
myMess = myMess <> allDots;
endingQ = True;

)];

If[curSDate > endSDate, (myMess = allDots;
myMess = myMess <> endDots;
myMess = myMess <> ToString[Row[{"**     It is Stardate: ", curSDate, ". ", totKlings, " Klingon vessels remain.\r"}]];
myMess = myMess <> "**                Y O U    L O S E ! \r";
myMess = myMess <> endDots;
myMess = myMess <> allDots;
endingQ = True;
)];

If[totNRG ≤0.0, (myMess = allDots;
myMess = myMess <> endDots;
myMess = myMess <> ToString[Row[{"**     It is Stardate: ", curSDate, ". ", totKlings, " Klingon vessels remain.\r"}]];
myMess = myMess <> "**     You are out of energy!\r";
myMess = myMess <> "**                Y O U    L O S E ! \r";
myMess = myMess <> endDots;
myMess = myMess <> allDots;
endingQ = True;
)];

If[endingQ, (
myMess = myMess <> "           Credits       :\r";
myMess = myMess <> "           Programmed by :  George V. Woodrow III in Wolfram Language \r";
myMess = myMess <> "           Based on      :  spacwr.bas by Mike Mayfield ca 1972 \r";
myMess = myMess <> "           Beta testers  :  Marjess Leighton \r\r\r";


)];

If[myMess ≠ "", myLog = myMess];

(* If we are ending,diasble ability to do stuff. Make sure displays are enabled *)
If[endingQ,
gameOverQ = True;
];

];


(* ================================================   setDriveType   ================================================ *)
SetAttributes[setDriveType, HoldAll];
setDriveType[theDriveType_, warpOKQ_] :=DynamicModule[{}, 
If[!warpOKQ,theDriveType = 0];
If[warpOKQ,
Style[RadioButtonBar[Dynamic[theDriveType], {1 ->"Warp  ",0 -> "Impulse"}, LabelStyle -> {White, Bold, 16,FontFamily -> "Courier New"}], ControlsRendering->"Generic"]
,Style["Warp Drive Down, Impulse only", Red, 12, Bold,FontFamily -> "Courier New"]]
]
(* ================================================   setDriveDirection   ================================================ *)
SetAttributes[setDriveDirection, HoldAll];
setDriveDirection[entDirection_] := DynamicModule[{warpVec},
warpVec = {Sin[entDirection * π/180.], Cos[entDirection * π/180.]};
Column[{
LocatorPane[Dynamic[warpVec],Graphics[{White,Disk[],LightGray,Thickness[0.11],Circle[{0,0}, 0.8],Thin, Black,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.1Sin[i π/4], .1Cos[i π/4]}}], {i, 0, 7}],
 Thick,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.9Sin[i π/4], .9Cos[i π/4]}}], {i, 0, 7}],Red,Disk[{0,0}, .1],Arrowheads[Medium],Arrowheads[.15],Arrowheads[Medium],
 Arrow[{{0,0},Dynamic[Normalize[warpVec]]}]},ImageSize -> 100, PlotRange -> 1.1],{{-1,-1},{1,1}},Appearance->None]
,
Dynamic[Style["Heading :" <> ToString[NumberForm[entDirection = Round[Mod[-((180./π)Arg[warpVec[[1]] + I warpVec[[2]]]- 90), 360], 5],
 3, NumberPadding -> {"0", ""}]], White, FontFamily -> "Courier New",12]]
}, Center]
]
(* ================================================   setDriveVelocity   ================================================ *)
SetAttributes[setDriveVelocity, HoldAll];
setDriveVelocity[theVelocity_, theDriveType_, eEnergy_] := DynamicModule[{},
If[theDriveType == 0, theVelocity = Min[theVelocity, 0.2]];
Column[{
Style["Factor", FontFamily -> "Courier New", 12, White]
,
Dynamic[Control[{theVelocity,0.01, If[theDriveType == 1,8.0, .2],ControlType -> VerticalSlider, ImageSize -> {20, 85}, Appearance -> "RightArrow"}]]
,
Dynamic[Style[ToString[NumberForm[Round[theVelocity, .01],{4, 2}, NumberPadding -> {" ", "0"}]], If[ 8.0 * theVelocity < eEnergy, White, Red], FontFamily -> "Courier New", 12]]}, Alignment -> Center]
]

(* ================================================   moveEnterprise   ================================================ *)
(*                                                                                                                     *)
(* ================================================   moveEnterprise   ================================================ *)

(* theQuad will be modified in place. *)
SetAttributes[moveEnterprise, HoldAll];
moveEnterprise[theQuad_,theQuadCoords_, wFactor_, theHeading_, startEntPos_, theGal_, nQuadKlings_, nSBs_, nStars_, sBcoords_, klingsArray_, dockStatus_, eCond_, eE_,maxEE_, eShields_,theStardate_,theLog_, sDamageControl_, nPhotons_,finSDate_, totKlingons_, isGameOverQ_]:= Module[{myQuad,warpVec, qVel, oldEntPos, testEntPos, testEntCoords,oldEntCoords, maxSteps,tQCoords,newQuadCoords, stepNum, okQ, mess, auxMess, inQuadQ, doneQ, incStarDateQ , energyUsed, usedWFactor, damDev,damage, dev, hitEdge},

mess = "";
auxMess = "";
newQuadCoords = {0,0};
myQuad =theQuad;
hitEdge = False;

(* EmitSound[console2Sound]; *)
Beep[];

(* If there are klingons in the quadrant, the can attack here *)
If[nQuadKlings > 0, klingonAttack[nQuadKlings,klingsArray, sDamageControl, eE, eShields,dockStatus, mess, startEntPos, theStardate, finSDate, totKlingons,isGameOverQ]];


(* moving uses energy. We want to be sure we have enough. If we don't and there is enough from the shields,
then use some shield energy. If we still don't have enough, move just one sector. *)

usedWFactor =wFactor;

energyUsed = (8.0*usedWFactor);
If[energyUsed > eE,
(
If[eShields >energyUsed, 
(eE += energyUsed;
 eShields -= energyUsed;
mess = mess <>" Using shield energy to move.\r";
)
,
(
mess = mess <>" Almost out of Energy. Minimal headway\r";
usedWFactor = 0.01;
)];
)];

maxSteps = Floor[8 * usedWFactor]+ 1;
stepNum = 0;

incStarDateQ = False;
(* Going faster than warp 1 increments the stardate, even if we don't leave the quadrant. *)
If[usedWFactor ≥ 1.0, incStarDateQ = True];
(* Similarly, we expend the same energy, even if we are blocked. *)
eE -= (8.0*usedWFactor);

warpVec = { -Cos[theHeading * π/180], Sin[theHeading * π/180]};

oldEntPos = startEntPos;
(* compute coords for new Quadrant, just in case we need it *)
newQuadCoords = theQuadCoords;
tQCoords = Round[theQuadCoords + Max[usedWFactor, .7]* warpVec];
If[tQCoords[[1]] < 1, (tQCoords[[1]] = 1; hitEdge = True)];
If[tQCoords[[1]] > 8, (tQCoords[[1]] = 8; hitEdge = True)];
If[tQCoords[[2]] < 1, (tQCoords[[2]] = 1; hitEdge = True)];
If[tQCoords[[2]] > 8, (tQCoords[[2]] = 8; hitEdge = True)];


mess = mess <>ToString[Row[{" Moving, heading ", theHeading, " , warp factor ", usedWFactor, " .\r"}] ];

(* Repair devices. Notify if a device is fixed. *)
For[dev = 1, dev ≤ 8, dev++,
If[sDamageControl[[2,dev]] < 0,(
sDamageControl[[2,dev]] ++;
If[sDamageControl[[2,dev]] ≥ 0.0,mess = mess <>ToString[Row[{" ",sDamageControl[[1,dev]], " repaired.\r"}] ] ]
)]
];


(* remove ent from starting position *)
myQuad[[oldEntPos[[1]], oldEntPos[[2]] ]] = "";
oldEntCoords = oldEntPos;
okQ = True;
inQuadQ = True;
doneQ = False;

While[okQ,

testEntPos = oldEntPos + warpVec;
testEntCoords = Round[testEntPos];

If[testEntCoords[[1]] > 8 || testEntCoords[[1]] < 1 || testEntCoords[[2]] > 8 || testEntCoords[[2]] < 1,
(
(* We want to leave the current Quadrant. Unless we are at the edge, we do *)
inQuadQ = False;
okQ = False;
incStarDateQ = True;

If[hitEdge,(auxMess = auxMess <> " Stopped by energy barrier at edge of the Galaxy!\r";
mess = mess <> " Stopped by energy barrier at edge of the Galaxy!\r";)];

(* If we try to leave the galaxy, we can't. This is the same as being blocked. *)
If[newQuadCoords == tQCoords, (myQuad[[oldEntCoords[[1]], oldEntCoords[[2]] ]] = "E";
testEntCoords = oldEntCoords;
inQuadQ = True;  (* we really didn't leave the galaxy *)
doneQ = True;)];
)
,
(
(* We are in the same quadrant. See if we are blocked by something.
If we are, then we back up to the previous location and stop *)
If [myQuad[[testEntCoords[[1]], testEntCoords[[2]] ]] != "",
(myQuad[[oldEntCoords[[1]], oldEntCoords[[2]] ]] = "E";
mess = mess <> ToString[Row[{" Enterprise blocked by object at {",testEntCoords[[1]], ", ", testEntCoords[[2]], "} due to bad navigation.\r"}] ];
testEntCoords = oldEntCoords;
okQ = False;
doneQ = True;
(* Not in BASIC Game: If we hit something, we have damage *)
damDev = RandomInteger[{2,8}];
damage = 0.5 + RandomReal[] * 1.0;
sDamageControl[[2,damDev]] -= damage;
mess = mess <>ToString[Row[{sDamageControl[[1,damDev]], " Damaged. ", NumberForm[damage,6], " units \r"}]];
)];
stepNum++;
oldEntPos = testEntPos;
oldEntCoords = testEntCoords;
If[stepNum >= maxSteps, okQ = False];
)
]
]; (* while *)
(* We got through the While statement with no problem. Put the Enterprise in the current location *)
If[inQuadQ && !doneQ, myQuad[[testEntCoords[[1]], testEntCoords[[2]] ]] = "E"];
(* If we are not in the same quadrant, we need to do stuff:
   1. update the quadrant co-ordinates
 2. Make a new quadrant. This affects other globals.
3. Increment Startdate
 4. Update message. *)
If[!inQuadQ,(newQuadCoords = tQCoords;
{myQuad, nQuadKlings, nSBs, nStars, testEntCoords, sBcoords, klingsArray} = createQuadrantA[theGal[[newQuadCoords[[1]], newQuadCoords[[2]]]]];

)];

(* To improve game play, we cause damage for moves. We restrict the damage to warp 4 and above. *)
If[wFactor > 4.0,

If[(RandomReal[] < 0.012 * (wFactor - 2.0)^2),
(
damDev = RandomInteger[{1,8}];
damage = 0.5 + RandomReal[] * 1.0;
sDamageControl[[2,damDev]] -= damage;
auxMess = auxMess <> ToString[Row[{sDamageControl[[1,damDev]], " Damaged due to high warp. ", NumberForm[damage,6], " units \r" }]];
mess = mess <> ToString[Row[{sDamageControl[[1,damDev]], " Damaged due to high warp. ", NumberForm[damage,6], " units \r" }]];
)]];


theQuad = myQuad;
startEntPos = testEntCoords;
theQuadCoords = newQuadCoords;
wFactor = 0.01;
(* theHeading = 0.0;  not needed *)
dockStatus = enterpriseDockedQ[theQuad, startEntPos];
 
eCond = enterpriseCondition[dockStatus, nQuadKlings, eE/maxEE];
If[mess ≠ "",
theLog = theLog <>mess;
mess = "";];

If[incStarDateQ,(
theStardate++; 

mess = ToString[Row[{"StarDate: ", theStardate, " -----  Quadrant: {", newQuadCoords[[1]], ", ", newQuadCoords[[2]], "} ------------ \r" }]];
If[auxMess ≠ "",
mess = mess <> auxMess;];

If[nQuadKlings >0 && eShields < 20.0, mess = mess <> " Klingons in Quadrant. Suggest raising shields.\r"];
)];
If[nQuadKlings > 0, EmitSound[redAlertSound]];

If[dockStatus,(mess = mess<> " Docked. Starbase shields protect the ship.\r";
{nPhotons, eE} =supplyEnterprise[maxEE];
eShields= 0.0;
sDamageControl[[2,1]] = 0.0;
sDamageControl[[2,2]] = 0.0;
sDamageControl[[2,3]] = 0.0;
sDamageControl[[2,4]] = 0.0;
sDamageControl[[2,5]] = 0.0;
sDamageControl[[2,6]] = 0.0;
sDamageControl[[2,7]] = 0.0;
sDamageControl[[2,8]] = 0.0;
)
];
If[mess ≠ "",
theLog = mess;
mess = "";];
checkEndGame[theStardate, finSDate, eE, eShields, totKlingons, theLog, sDamageControl, isGameOverQ];
];

initialLogMessageC[starDte_, endStarDte_, nKlings_, nSBs_ ] :=  "StarDate: "<>ToString[ starDte]<>"----- \r Your mission is to destroy "<>ToString[nKlings]<>" Klingons by Stardate : "<>ToString[endStarDte]<>".\r There are "<>ToString[nSBs]<>" Starbases. \r";