packages feed

haskell-in-space-0.1: Asteroids-haskelly.hs

module Main (main) where -- Spaceman_Spiff where

import Asteroids.Geometry

import Graphics.HGL.Run (runGraphics)
import Graphics.HGL.Window (Event(..), RedrawMode(..), Window(), closeWindow, getWindowTick, maybeGetWindowEvent, openWindowEx, setGraphic)
import Graphics.HGL.Units (Point())
import Graphics.HGL.Utils (overGraphics, withRGB)
import Graphics.HGL.Draw.Monad (Graphic())
import Graphics.HGL.Draw.Text (text, RGB(..))
import Graphics.HGL.Key (isCharKey, isDownKey, isEscapeKey, isLeftKey, isReturnKey, isRightKey, isTabKey, isUpKey, keyToChar, Key)

import Random (randomRIO, newStdGen, randomRs)
import  System.IO.Unsafe (unsafePerformIO)

-- to make ghc happy -- delete for hugs
fromInt :: Num a=> Int-> a
fromInt n = fromInteger (toInteger n)
toInt   :: Integral a=> a-> Int
toInt n = fromInteger(toInteger n)

------------------------------------ Edit Sektion --------------------------------------------

-- Fenstergröße: (Default: 700, 600)
winSize :: (Int, Int)
winSize = (900, 900)

-- Spezialtasten: False=Aus, True=Ein (Default: True)
specKeys :: Bool
specKeys = True

-- Hintergrundbild: False=Aus, True=Ein (Default: False)
backgr :: Bool
backgr = True

-- Head-Up Display HUD Konsole: False=Aus, True=Ein (Default: True)
hud :: Bool
hud = True

-- Weitere Informationen an oder aus: False=Aus, True=An (Default: False)
debuginfo :: Bool
debuginfo = False

-- Asteroidensplitter: True=An, False=Aus (Default True)
shrep :: Bool
shrep = True

-- Geschwindigkeit des Schiffs, Default: 10 (ändert auch Projektilgeschwindigkeit/-reichweite)
vMax :: Double
vMax = 15

-- Maximale Anzahl der Projektile, Default: 7
maxBul :: Int
maxBul = 10

-- Farbe des Raumschiffs: 0=Schwarz,1=Weiss,2=Standard,3=Seltsamgrün,4=Merkwürdiggelb (Default: 2)
shipCol :: Int
shipCol = 3

-- Raumschifftyp: 0=Standard, 1=UFO, 2=Turret, 3=Space Shuttle, 4=Millenium Falcon, 5=X-Wing,
--                 6=F-16 Falcon, 7=SR-71 Blackbird, 8=Fokker Dr I
{-# NOINLINE shipTyp #-}
shipTyp :: Int
shipTyp = unsafePerformIO (randomRIO (0,8))

-- Farbe der Projektile: 0=Blau, 1=Rot/Gelb, 2=Violett, 3=Grün  (Default: 0)
bulCol :: Int
bulCol = 3

-- Form der Projektile: 0=Line, 1=Circle  (Default: 1)
bulFig :: Int
bulFig = 1

-- Farbe der Asteroiden: 0=Braun, 1=Grau, 2=Dunkel, 3=Hell (Default: 0)
astCol :: Int
astCol = 3

-- Form der Asteroiden: 0=Rund, 1=Eckig, 2=Borg-Kuben (Default: 0)
astFig :: Int
astFig = 2

-- Initialgeschwindigkeit der Asteroiden (Default: 4)
astSpd :: Int
astSpd = 5

-- Waffen-Reichweite in Fensterbreiten (Default 0.7)
wpRange :: Double
wpRange = 0.7

-- Schiff bewegungslos (Turret-Funktion): True=Ja, False=Nein (Default: False)
static :: Bool
static = False

-- Godmode (Kollision Schiff/Asteroid aus): True=Ja, False=Nein (Default: False)
godmode :: Bool
godmode = False

-- Anzahl der Ladungen des Starburst-Streuwaffensystems (Default: 10)
bursts :: Int
bursts = 15

-------------------------------------- Edit Ende ---------------------------------------------


---------------------------------- Ressourcen Sektion ----------------------------------------


data State = State { ship :: Ship,      -- Schiff
             bullets   :: BulletList,   -- Liste aller Bullets
             shrepnels :: ShrepList,    -- Liste aller Spliter
             asteroids :: AstList,      -- Liste aller Asteroiden
             spaces    :: SpaceList,    -- Liste der Hintergrundgrafiken
             mission   :: Mission,      -- Zufallszahlen für die Texte
             stats     :: Stats,        -- Statistikwerte
             end       :: Bool,         -- Ende?
             rsCnt     :: Int           -- Neustartverzögerung
             }

data Ship = Ship {                      -- Schiff
            spos :: Point,              -- Position
            sshp    :: Shape,           -- Form
            svel    :: Point,           -- Geschwindigkeitsvektor
            ornt    :: Double,          -- Orientierung
            thrust  :: Double,          -- Schubwert der Längsrichtig
            hthrust :: Double,          -- Schubwert der Querrichtig
            hAcc    :: Double,          -- Drehwert
            shipDes :: Bool,            -- Schiff zerstört?
            sbur    :: Int}             -- Anzahl der Starbursts

type BulletList = [Bullet]
data Bullet = Bullet {                  -- Projektil
          bpos :: Point,                -- Position
              bshp  :: Shape,           -- Form
              bvel  :: Point,           -- Geschwindigkeitsvektor
              bcou  :: Double,          -- Zähler für das Verschwinden
              bort  :: Double,          -- Orientierung
              bcol  :: Int}             -- Farbe

type ShrepList = [Shrepnel]
data Shrepnel = Shrepnel {              -- Splitter
                gpos :: Point,          -- Position
                gshp :: Shape,          -- Form
                gvel :: Point,          -- Geschwindigkeitsvektor
                gcnt :: Double,         -- Zähler für das Verschwinden
                gcol :: Int}            -- Farbe

type AstList = [Asteroid]
data Asteroid = Asteroid {              -- Asteroid
            asize :: Int,               -- Größe
                apos :: Point,          -- Position
                ashp :: Shape,          -- Form
                avel :: Point,          -- Geschwindigkeitsvektor
                aort :: Double}         -- Orientierung

type SpaceList = [Space]
data Space = Space {                    -- Hintergrundobjekt
             upos :: Point,             -- Position
             ushp :: Shape,             -- Form
             utyp :: Int,               -- Typ
             ucol :: Int}               -- Farbe

data Mission = Mission {                -- Zufallswerte der Zufallstexte
               rndm1 :: Int,            -- Zufallszahl für Textliste 1
               rndm2 :: Int,            -- Zufallszahl für Textliste 2
               rndm3 :: Int,            -- Zufallszahl für Textliste 3
               rndm4 :: Int}            -- Zufallszahl für Textliste 4

data Stats = Stats {                    -- Statistik
             fails :: Int,              -- Fehlschüsse
             spl   :: Int,              -- Gesamtschüsse
             hits :: Int,               -- Treffer
             ratio :: Int}              -- Treffer/Fehlschuss-Verhältnis


aDelta :: Double                        -- Schubkonstante des Schiffs
aDelta = 1

hDelta :: Double                        -- Drehkonstante des Schiffs
hDelta = 0.3

bSpeed :: Double                        -- Höchstgeschwindigkeit des Bullets
bSpeed = (vMax*3)

bMaxFlight :: Double                    -- Maximalreichweite des Bullets
bMaxFlight = (fromInt(fst winSize)* wpRange )/ bSpeed

addWinMod :: (Int,Int) -> (Int,Int) -> (Int,Int)        -- sorgt für Umbruch des Space
addWinMod (a, b) (c, d)= ((a+ c) `mod` (fst winSize), (b+ d) `mod` (snd winSize))

------------- Liste der Schiffsformen
spaceShip :: [Figure]
spaceShip = [sfStdrd,sfSaucr,sfTurrt,sfShutl,sfMilln,sfXWing,sfFalcn,sfBBird,sfBaron,sfDestr]

sfStdrd :: Figure
sfStdrd = (Polygon [(15, 0), (-15, 10), (-20, 0), (-15, -10), (15, 0)])
sfSaucr :: Figure
sfSaucr = (Circle 21)
sfTurrt :: Figure
sfTurrt = (Polygon [(28,2),(7,2),(5,8),(-13,12),(-13,-12),(5,-8),(7,-2),(28,-2),(28,2)])
sfXWing :: Figure
sfXWing = (Polygon [(25,-1),(25,1),(6,3),(6,6),(3,6),(3,16),(12,17),(-1,17),
            (-4,6),(-9,6),(-9,4),(-7,4),(-7,-4),(-9,-4),(-9,-6),(-4,-6),(-1,-17),
            (12,-17),(3,-16),(3,-6),(6,-6),(6,-3),(25,-1)])
sfMilln :: Figure
sfMilln = (Polygon [(14,-3),(14,3), (20,3), (20,3), (8,8),(8,11),(12,11),(12,12),(11,13),
            (5,13),(4,10), (3,11),(-2,11),(-6,9),(-9,6),(-11,2),(-11,-2), (-9,-6),
            (-6,-9),(-2,-11),(3,-11),(8,-9),(20,-3),(20,-3),(14,-3)])
sfShutl :: Figure
sfShutl = (Polygon [(22,2),(4,5),(-3,13),(-5,13),(-7,3),(-11,2),(-11,-2),(-7,-3),(-5,-13),
            (-3,-13),(4,-5),(22,-2),(25,0),(22,2)])
sfFalcn :: Figure
sfFalcn = (Polygon [(24,0),(7,3), (0,13),(4,13),(4,14),(-5,14),(-5,13),(-3,13),(-3,2),
            (-7,2),(-11,6),(-13,6),(-13,-6),(-11,-6),(-7,-2),(-3,-2),(-3,-13),
            (-5,-13),(-5,-14),(4,-14),(4,-13),(0,-13),(7,-3),(24,0)])
sfBBird :: Figure
sfBBird = (Polygon [(23,0),(15,2),(4,2),(4,6),(8,6),(3,7),(-1,7),(-5,10),(-7,10),
            (-8,7),(-5,7),(-6,3),(-9,3),(-10,0),(-9,-3),(-6,-3),(-5,-7),(-8,-7),
            (-7,-10),(-5,-10),(-1,-7),(3,-7),(8,-6),(4,-6),(4,-2),(15,-2),(23,0)])
sfBaron :: Figure
sfBaron = (Polygon [ (6,2),(4,2),(4,16),(1,16),(-1,3),(-7,1),(-13,6),(-14,5),(-14,3),(-12,1),
            (-12,-1),(-14,-3),(-14,-5),(-13,-6),(-7,-1),(-1,-3),(-3,-2),(-3,2),(-1,3),
            (2,2),(2,-2),(-1,-3),(1,-16),(4,-16),(4,-2),(6,-2),(6,0),(10,0),(10,3),
            (10,-3),(10,0),(6,0),(6,2) ])
sfDestr :: Figure
sfDestr = (Circle 0)

------------- Liste der Asteroidenformen
assis :: [[Figure]]
assis = [stds,stda,stdb]

stda :: [Figure]
stda = [ (Polygon [(50,0),(35,35),(-20,35),(-40,0),(-40,-20),(0,-40),(30,-35),(50,0)]),
         (Polygon [(45,0),(0,15),(-15,15),(-20,0),(0,-30),(20,-25),(45,0)]),
         (Polygon [(20,0),(0,15),(-10,0),(-8,-8),(20,0)]) ]

stdb :: [Figure]
stdb = [ (Polygon [(0,50),(80,46),(80,-46),(0,-50),(-40,-35),(-40,35),(0,50)]),
         (Polygon [(30,5),(30,25),(-30,25),(-30,-25),(-10,-25),(0,0),(30,5)]),
         (Polygon [(20,5),(-23,8),(-20,-5),(20,-5),(20,5)]) ]

stds :: [Figure]
stds = [ (Circle 50),(Circle 30),(Circle 15)]

------------- Liste der Projektilformen
bulletFig :: [Figure]
bulletFig = [winBul, linBul]

winBul :: Figure
winBul = Polygon [(4,0),(-4,0),(4,0)]

linBul :: Figure
linBul = Circle 3

------------- Splitterform
shreps :: Figure
shreps = (Circle 2)

------------- Liste der Schiffsfarbpaletten
shipPl :: [[RGB]]
shipPl = [shBlk,shWhi,shStd,shGrn,shYlw]

shBlk :: [RGB]
shBlk = [(RGB 0 0 0), (RGB 0 0 0)]
shStd :: [RGB]
shStd = [(RGB 130 130 150), (RGB 160 130 150)]
shWhi :: [RGB]
shWhi = [(RGB 215 215 215), (RGB 255 205 205)]
shGrn :: [RGB]
shGrn = [(RGB 100 180 100), (RGB 160 200 100)]
shYlw :: [RGB]
shYlw = [(RGB 150 120 0), (RGB 200 150 0)]

------------- Liste der Asteroidenfarbpaletten
astPl :: [RGB]
astPl = [(RGB 90 70 70),(RGB 60 60 65),(RGB 30 30 30),(RGB 150 120 120)]

------------- Liste der Projektilfarbpaletten
firePl :: [[RGB]]
firePl = [elcPl,redPl,bluPl,grnPl]

redPl :: [RGB]
redPl = [(RGB 255 180 0),(RGB 255 140 0),(RGB 255 100 0),(RGB 255 50 0),(RGB 255 100 0),(RGB 255 140 0)]
bluPl :: [RGB]
bluPl = [(RGB 255 0 255),(RGB 200 0 255),(RGB 150 0 255),(RGB 100 0 255),(RGB 150 0 255),(RGB 200 0 255)]
grnPl :: [RGB]
grnPl = [(RGB 255 255 0),(RGB 165 255 0),(RGB 105 205 0),(RGB 0 155 0),(RGB 105 205 0),(RGB 165 255 0)]
elcPl :: [RGB]
elcPl = [(RGB 0 255 255),(RGB 0 200 255),(RGB 0 150 255),(RGB 0 100 255),(RGB 0 150 255),(RGB 0 200 255)]

------------- Liste der Splitterfarbpaletten
shrPl :: [RGB]
shrPl = sred

sred :: [RGB]
sred = [(RGB 255 255 50),(RGB 255 255 00),(RGB 255 215 0),(RGB 245 185 0),(RGB 225 155 0),(RGB 215 115 0),
         (RGB 195 75 15),(RGB 185 65 25),(RGB 155 55 35),(RGB 145 50 50),(RGB 130 70 70),(RGB 120 90 90),
         (RGB 70 50 50),(RGB 50 20 20)]

sgrn :: [RGB]
sgrn = [(RGB 255 255 115),(RGB 255 255 50),(RGB 255 255 0),(RGB 135 255 0),(RGB 70 215 0),(RGB 50 175 0),
         (RGB 15 165 15),(RGB 25 145 25),(RGB 35 125 35),(RGB 50 100 50),(RGB 70 100 70),(RGB 90 90 90),
         (RGB 50 50 50),(RGB 25 25 25)]

------------- Textbausteine für Zufallstexte
mtxt1 :: [String]
mtxt1 = ["Verloren im ", "Gestrandet im ", "Dem Tode nahe im ", "So gut wie erledigt im ", "Verirrt im ",
         "Bald vernichtet im ", "Voellig verminzt im ", "Nur noch 60 Sekunden im ", "Widerstand ist zwecklos im ",
         "Sonnige Gruesse aus dem ", "Die Macht sei mit dir im ", "Schwerer Ausnahmefehler im ", "Schauen Sie mal wieder rein im "]

mtxt2 :: [String]
mtxt2 = ["wilden ", "konvexen ", "dunklen ", "paradiesischen ", "preussischen ", "bayrischen ", "universitaeren ",
         "neu compilierten ", "fluiden ", "traumhaften ", "syntaktischen ", "rechtlich geschuetzen ", "fernen "]

mtxt3 :: [String]
mtxt3 = ["Delta","Schlaraffen","Tackatucka","Microsoft","Morchel","Zucker","Hugs","Linux","Lueth","Semaphoren", "MZH",
         "Mensa", "Todes", "Dijkstra"]

mtxt4 :: [String]
mtxt4 = ["-Quadrant", "-Nebel", "-Guertel", "-Gebiet", "-Areal", "-Territorium", "-Sektor",
         "land", "-System", "-Raum", "-Sonnensystem", "-Nexus", "-Konflux"]

------------- Rangbezeichnungen
ranks :: [String]
ranks = ["Space-Amoeba","Critter","Tribble", "ALF","Commander Keen","E.T.","Spaceball","Chewbacca","Alien",
        "Space-Marine","Captain Kirk","Jedi-Ritter","Spezies 8472","Meister Yoda","Astro-Held","Haskelly","Astrale Gottheit"]

------------- Liste der Hintergrundobjekte
spaceFig :: [Figure]
spaceFig = [(Circle 54), (Circle 48), (Circle 46), (Circle 44),(Circle 42), (Circle 38), (Circle 38), (Circle 1),
                (Circle 10), (Circle 5), (Polygon [(8,0),(-8,0),(0,0),(0,8),(0,-8),(0,0)]),
                (Polygon [(3,0),(-3,0),(0,0),(0,3),(0,-3),(0,0)]),
                (Polygon [(-350,100),(-350,50),(-200,20),(-110,0),(0,-20),(130,-40),(220,-60),(350,-100),(350,30),
                (190,10),(110,40),(-70,55),(-160,70),(-270,85),(-350,100)]),
                (Polygon [(-350,70),(-350,60),(-200,40),(-110,10),(0,-10),(130,-25),(220,-40),(350,-75),(350,-10),
                (190,0),(110,10),(-70,35),(-160,40),(-270,65),(-350,70)]),

                -- Kubus 1
                (Polygon [(6,56),(86,52),(86,-52),(6,-56),(-46,-41),(-46,41),(0,56)]),
                (Polygon [(4,54),(84,50),(84,-50),(4,-54),(-44,-39),(-44,39),(0,54)]),
                (Polygon [(2,52),(82,48),(82,-48),(2,-52),(-42,-37),(-42,37),(0,52)]),
                (Polygon [(0,50),(80,46),(80,-46),(0,-50),(-40,-35),(-40,35),(0,50)]),
                (Polygon [(2,48),(78,44),(78,-44),(2,-48),(2,48)]),

                -- Kubus 2
                (Polygon [(4,-40),(44,-30),(44,30),(4,40),(-44,30),(-44,-30),(4,-40)]),
                (Polygon [(2,-38),(42,-28),(42,28),(2,38),(-42,28),(-42,-28),(2,-38)]),
                (Polygon [(0,-36),(40,-26),(40,26),(0,36),(-40,26),(-40,-26),(0,-36)]),
                (Polygon [(2,-34),(38,-24),(38,24),(2,34),(2,-34)]),

                (Polygon [(30,0),(2,2),(0,30),(-2,2),(-30,0),(-2,-2),(0,-30),(2,-2),(30,0)]),
                (Polygon [(15,0),(2,2),(0,15),(-2,2),(-15,0),(-2,-2),(0,-15),(2,-2),(15,0)]),

                (Circle 25),(Circle 22),(Circle 18),(Circle 14),

                -- Star Destroyer
                (Polygon [(-90,-6),(30,-30),(90,-6),(-90,-6)]),
                (Polygon [(-90,0),(90,0),(30,15),(-90,0)]),
                (Polygon [(90,0),(-90,0),(-90,-6),(90,-6),(90,0)]),
                (Polygon [(-30,-10),(0,-30),(30,-30),(30,-50),(50,-50),(75,-10),(-30,-10)]),
                (Polygon [(30,-51),(11,-47),(11,-43),(30,-38),(49,-43),(49,-47),(30,-51)]),

                -- Turret-Plattform
                (Polygon [(27,0),(13,-22),(-13,-22),(-27,0),(-13,22),(13,22),(27,0)]),
                (Polygon [(30,0),(15,-25),(-15,-25),(-30,0),(-15,25),(15,25),(30,0)])

                ]

------------- Liste der Hintergrundfarbpaletten
spacePl :: [RGB]
spacePl = [(RGB 0 0 0), (RGB 255 255 255), (RGB 180 180 180), (RGB 100 100 225), (RGB 225 0 225), (RGB 50 150 0),
            (RGB 30 0 0), (RGB 50 0 0), (RGB 70 0 0), (RGB 90 0 0), (RGB 150 0 0), -- 6
            (RGB 0 30 0), (RGB 0 50 0), (RGB 0 70 0), (RGB 0 90 0), (RGB 0 150 0), -- 11
            (RGB 0 0 30), (RGB 0 0 50), (RGB 0 0 70), (RGB 0 0 180), (RGB 0 0 225), --16
            (RGB 30 0 30), (RGB 50 0 50), (RGB 70 0 70), (RGB 90 0 90), (RGB 150 0 150), --21
            (RGB 30 30 40), (RGB 40 40 50), (RGB 70 70 80), (RGB 90 90 100), (RGB 110 110 120), --26
            (RGB 30 30 30), (RGB 40 40 40), (RGB 70 70 70), (RGB 90 90 900), (RGB 110 110 110),  --30
            (RGB 30 40 30),(RGB 50 60 50),(RGB 70 70 80), (RGB 5 30 0), (RGB 10 40 0)] -- 36

------------- Liste des Sternenfelds
stars :: [Space]
stars = [setSpace (Space{upos= (650,90),utyp=7,ucol=1}),setSpace (Space{upos= (160,150),utyp=7,ucol=2}),
         setSpace (Space{upos= (620,50),utyp=7,ucol=1}),setSpace (Space{upos= (20,250),utyp=7,ucol=2}),
         setSpace (Space{upos= (420,350),utyp=7,ucol=1}),setSpace (Space{upos= (490,450),utyp=7,ucol=2}),
         setSpace (Space{upos= (30,550),utyp=7,ucol=2}),setSpace (Space{upos= (340,250),utyp=7,ucol=1}),
         setSpace (Space{upos= (220,580),utyp=7,ucol=2}),setSpace (Space{upos= (590,350),utyp=7,ucol=1})]

------------- Liste der zusammengesetzten Hintergrundgrafiken
spacePic :: [[Space]]
spacePic = [([]),

        -- Two Star Destroyers, Stars
       ([setSpace (Space{upos= (220,250),utyp=29,ucol=27}), setSpace (Space{upos= (220,250),utyp=30,ucol=26}),
         setSpace (Space{upos= (220,250),utyp=33,ucol=26}), setSpace (Space{upos= (220,250),utyp=32,ucol=27}),

         setSpace (Space{upos= (420,150),utyp=29,ucol=27}), setSpace (Space{upos= (420,150),utyp=30,ucol=26}),
         setSpace (Space{upos= (420,150),utyp=33,ucol=26}), setSpace (Space{upos= (420,150),utyp=32,ucol=27})]
         ++stars),

        -- Shadow Planet Blue, Bright Star I Blue, Stars
       ([setSpace (Space{upos= (217,147),utyp=6,ucol=0}), setSpace (Space{upos= (220,150),utyp=5,ucol=19}),
         setSpace (Space{upos= (220,150),utyp=4,ucol=18}), setSpace (Space{upos= (220,150),utyp=2,ucol=17}),
         setSpace (Space{upos= (220,150),utyp=0,ucol=16}),

         setSpace (Space{upos= (550,480),utyp=11,ucol=1}),setSpace (Space{upos= (550,480),utyp=10,ucol=3}),
         setSpace (Space{upos= (550,480),utyp=9,ucol=20}),setSpace (Space{upos= (550,480),utyp=8,ucol=18})]
         ++stars),

        -- Shadow Planet Star Violet, Bright Star Violet, Stars
       ([setSpace (Space{upos= (323,453),utyp=27,ucol=0}), setSpace (Space{upos= (305,444),utyp=11,ucol=1}),
         setSpace (Space{upos= (305,444),utyp=10,ucol=4}), setSpace (Space{upos= (305,444),utyp=24,ucol=24}),
         setSpace (Space{upos= (320,450),utyp=27,ucol=25}), setSpace (Space{upos= (320,450),utyp=26,ucol=24}),
         setSpace (Space{upos= (305,444),utyp=23,ucol=23}), setSpace (Space{upos= (320,450),utyp=25,ucol=22}),

         setSpace (Space{upos= (550,180),utyp=7,ucol=1}),setSpace (Space{upos= (550,180),utyp=11,ucol=4}),
         setSpace (Space{upos= (550,180),utyp=10,ucol=24})]++stars),

        -- Two Borg Cubes, Nebula, Stars
       ([setSpace (Space{upos= (550,250),utyp=18,ucol=0}), setSpace (Space{upos= (550,250),utyp=17,ucol=32}),
         setSpace (Space{upos= (550,250),utyp=16,ucol=14}), setSpace (Space{upos= (550,250),utyp=15,ucol=12}),

         setSpace (Space{upos= (450,210),utyp=22,ucol=0}), setSpace (Space{upos= (450,210),utyp=21,ucol=32}),
         setSpace (Space{upos= (450,210),utyp=20,ucol=12}), setSpace (Space{upos= (450,210),utyp=19,ucol=11}),

         setSpace (Space{upos= (350,400),utyp=13,ucol=40}), setSpace (Space{upos= (350,400),utyp=12,ucol=39})]
         ++stars),

        -- Green Shadow Planet, Bright Star Green, Stars
       ([setSpace (Space{upos= (510,140),utyp=2,ucol=0}), setSpace (Space{upos= (520,150),utyp=5,ucol=5}),
         setSpace (Space{upos= (520,150),utyp=4,ucol=14}), setSpace (Space{upos= (520,150),utyp=2,ucol=12}),
         setSpace (Space{upos= (520,150),utyp=0,ucol=11}),

         setSpace (Space{upos= (100,430),utyp=10,ucol=5}), setSpace (Space{upos= (100,430),utyp=24,ucol=12}),
         setSpace (Space{upos= (100,430),utyp=23,ucol=11})] ++ stars),

        -- Red Shadow Planet, Stars
       ([setSpace (Space{upos= (210,440),utyp=2,ucol=0}), setSpace (Space{upos= (220,450),utyp=5,ucol=10}),
         setSpace (Space{upos= (220,450),utyp=4,ucol=9}), setSpace (Space{upos= (220,450),utyp=2,ucol=8}),
         setSpace (Space{upos= (220,450),utyp=0,ucol=7})] ++ stars),

        -- Violet Star
       ([setSpace (Space{upos= (650,180),utyp=7,ucol=1}),setSpace (Space{upos= (650,180),utyp=11,ucol=4}),
         setSpace (Space{upos= (650,180),utyp=10,ucol=24})] ++ stars),

        -- Four Stars Blue, Stars
       ([setSpace (Space{upos= (350,150),utyp=7,ucol=1}),setSpace (Space{upos= (350,150),utyp=11,ucol=20}),
         setSpace (Space{upos= (350,150),utyp=10,ucol=18}),

         setSpace (Space{upos= (350,450),utyp=7,ucol=1}),setSpace (Space{upos= (350,450),utyp=11,ucol=20}),
         setSpace (Space{upos= (350,450),utyp=10,ucol=18}),

         setSpace (Space{upos= (200,300),utyp=7,ucol=1}),setSpace (Space{upos= (200,300),utyp=11,ucol=20}),
         setSpace (Space{upos= (200,300),utyp=10,ucol=18}),

         setSpace (Space{upos= (500,300),utyp=7,ucol=1}),setSpace (Space{upos= (500,300),utyp=11,ucol=20}),
         setSpace (Space{upos= (500,300),utyp=10,ucol=18})]++stars)

        ]

turret :: [Space]                       -- Zusatzhintergrundgrafik für Turret-Mode
turret = if static then [setSpace (Space{upos= (350,300),utyp=28,ucol=38}),
                         setSpace (Space{upos= (350,300),utyp=34,ucol=37}),
                         setSpace (Space{upos= (350,300),utyp=35,ucol=36})]
         else []

------------------------------------- Ressourcen Ende ----------------------------------------


------------------------------------ Programm Sektion ----------------------------------------

------------- Initialzustand
initialState :: [Int] ->[Int]-> [Int]-> State
initialState  r u m =
  State {
        ship= setShip   Ship{spos= (fst winSize `div` 2, snd winSize `div` 2),
          svel= (0,0), ornt= pi/2, thrust= 0, hAcc= 0, sbur = bursts, hthrust = 0, shipDes = False},
        bullets= [],
        shrepnels = [],
        stats = Stats { fails = 0, spl = 0, hits = 0, ratio=0 },
        mission = Mission{rndm1 = (m!!4), rndm2 = (m!!3), rndm3 = (m!!2), rndm4 = (m!!1)},
        spaces = if backgr then ((spacePic!!(u!!1))++turret) else ((spacePic!!0)++turret),
        asteroids = [
          setAst (Asteroid{ asize=0, apos=(0,0), avel = (r!!4,r!!1), aort = pi/3}),
          setAst (Asteroid{ asize=0, apos=(0,0), avel = (r!!6,r!!2), aort = pi/6}),
          setAst (Asteroid{ asize=0, apos=(0,0), avel = (r!!5,r!!8), aort = pi/4})],
        end = False,
        rsCnt = 20}

------------- Bewegung des Schiffs
moveShip :: Ship-> Ship
moveShip(Ship {spos= spos0, svel= svel0, sbur = sbur0,
               hAcc= hAcc, thrust= t, ornt= o, hthrust = ht, shipDes = shipx}) =
  setShip
    Ship{spos= addWinMod spos0 svel1, sbur = sbur0,
         svel= if l> vMax then smult (vMax/l) svel1
                 else svel1, shipDes = shipx,
         thrust= t, ornt= o+ hAcc, hAcc= hAcc, hthrust = ht} where
           svel1= add (add (polar t o) (polar ht (o+pi/2))) svel0
           l= len svel1

------------- Bewegung der Asteroiden
moveAsteroid :: Asteroid-> Asteroid
moveAsteroid(Asteroid {apos = apos0, avel = avel0, aort = ao, asize=asize0}) =
  setAst Asteroid{apos = addWinMod apos0 avel0, avel = avel0, aort = ao + 0.05, asize=asize0}

moveAsteroids :: AstList-> AstList
moveAsteroids [] = []
moveAsteroids al = map moveAsteroid al

------------- Bewegung der Projektile und zeitbedingte Beseitigung
moveBullet :: Bullet-> Bullet
moveBullet (Bullet { bpos=bpos0, bvel=bvel0, bcou=bcou0, bort = bort0,bcol = bcol0 }) =
  setBullet Bullet{ bpos= addWinMod bpos0 bvel0, bvel = bvel0, bcou =(bcou0-1), bort = bort0, bcol = (toInt(bcol0+1))}

moveBullets :: BulletList-> BulletList
moveBullets [] = []
moveBullets bl = map moveBullet (kickLast(bl)) where

  kickLast :: BulletList-> BulletList
  kickLast bl = concat (map deleteB bl) where

    deleteB :: Bullet-> BulletList
    deleteB b
      | (bcou b) <= 0 = []
      | otherwise = [b]

------------- Bewegung der Splitter und zeitbedingte Beseitigung
moveShrep :: Shrepnel-> Shrepnel
moveShrep (Shrepnel {gpos=gpos0, gvel=gvel0, gcnt=gcnt0, gcol=gcol0}) =
  setShrep Shrepnel{gpos=addWinMod gpos0 gvel0, gvel=gvel0, gcnt=(gcnt0-1),
        gcol=if (gcol0 < 11) then (gcol0+1) else if ((gcnt0 <5) && (gcol0 <13)) then (gcol0+1) else gcol0}

moveShreps :: ShrepList-> ShrepList
moveShreps [] = []
moveShreps gl = map moveShrep (remShrep(gl)) where

  remShrep :: ShrepList-> ShrepList
  remShrep gl = concat (map deleteG gl) where

    deleteG :: Shrepnel-> ShrepList
    deleteG g
      | (gcnt g) <= 0 = []
      | otherwise = [g]

------------- setzt Schiffs-Shape
setShip :: Ship-> Ship
setShip sh = if (shipDes sh) then sh{sshp= shape (sfDestr)}
               else sh{sshp= shape (Translate (spos sh) (Rotate (ornt sh) (spaceShip!!shipTyp)))}

------------- setzt Asteroiden-Shape
setAst :: Asteroid-> Asteroid
setAst a = if (astFig == 0) then (a{ashp = shape(Translate (apos a) (assis!!(astFig `mod` (length assis))!!(asize a)))})
           else (a{ashp = shape(Translate (apos a) (Rotate (aort a) ((assis!!(astFig `mod` (length assis)))!!(asize a))))})

------------- setzt Bullet-Shape
setBullet :: Bullet-> Bullet
setBullet b = if (bulFig == 0) then b{ bshp = shape (Translate (bpos b) (Rotate (bort b) (bulletFig!!bulFig))) }
          else b{ bshp = shape (Translate (bpos b) (bulletFig!!bulFig)) }

------------- setzt Splitter-Shape
setShrep :: Shrepnel-> Shrepnel
setShrep g = g{gshp=shape (Translate (gpos g) shreps)}

------------- setzt Hintergrund-Shape
setSpace :: Space-> Space
setSpace u = u{ushp= shape (Translate (upos u) (spaceFig!!(utyp u)))}

------------- zeichnet den gesamten State
drawState :: State-> Graphic
drawState s = overGraphics [
        withRGB (RGB 205 155 0) (text (10,5) (show ( (mtxt1!!(rndm3 (mission s)))++
        (mtxt2!!(rndm1 (mission s))) ++ (mtxt3!!(rndm2 (mission s))) ++ (mtxt4!!(rndm4 (mission s))) ))),
        drawHUD s, drawInfo s, drawFail s, (drawShip (ship s)), (drawBullets (bullets s)),(drawShreps (shrepnels s)),
        (drawAsteroids (asteroids s)),(drawSpaces (spaces s)) ]

------------- zeichnet die statistische Informationen
drawHUD :: State-> Graphic
drawHUD s = if hud then overGraphics [(text (10,20) ("Hits/Fails:  ")),
        (text (120,20) (show (fromInt(hits (stats s)))++"/"++show (fromInt(fails (stats s))))),
        (text (10,35) ("Hit-Ratio:   ")), (text (120,35) (show (fromInt(ratio (stats s)))++"%")),
        (text (10,50) ("Rang:        ")), (text (120,50) (show (rank (hits (stats s))))),
        (text (10,65) ("Starbursts:  ")), (text (120,65) (show (sbur (ship s))))]
        else overGraphics []
          where
            rank :: Int-> String
            rank k = if (k < 100) then ranks!!(k `div` 10)
              else if (k >= 100) && (k < 200) then ranks!!(((k-100) `div` 25)+10)
              else if (k > 500) then ranks!!16
              else if (k > 300) then ranks!!15
              else ranks!!14

------------- zeichnet den "Game Over"-Bildschirm
drawFail :: State-> Graphic
drawFail s = if (shipDes (ship s)) then overGraphics [
        withRGB (RGB 255 0 0) (text (305,290) ("GAME OVER!")),
        withRGB (RGB 255 0 0) (text (289,305) ("Neustart in ")),
        withRGB (RGB 255 0 0) (text (397,305) (show (rsCnt s)))]
        else overGraphics []

------------- zeichnet Informationsmaske
drawInfo :: State-> Graphic
drawInfo s = if debuginfo then overGraphics [
        (text (260,20) ("Asteroiden:  ")), (text (370,20) (show (length (asteroids s)))),
        (text (260,35) ("Splitter:    ")), (text (370,35) (show (length (shrepnels s)))),
        (text (260,50) ("Bullets:     ")), (text (370,50) (show (length (bullets s)))),
        (text (260,65) ("Speedvect:   ")), (text (370,65) (show (svel (ship s))))]
        else overGraphics []

------------- zeichnet das Schiff
drawShip :: Ship-> Graphic
drawShip s = withRGB (if thrust s> 0 then shipPl!!(shipCol `mod`(length shipPl))!!1 else
                        shipPl!!(shipCol `mod`(length shipPl))!!0) (drawShape (sshp s))

------------- zeichnen die Asteroiden
drawAsteroid :: Asteroid-> Graphic
drawAsteroid a = withRGB (astPl!!(astCol `mod` (length astPl))) (drawShape (ashp a))

drawAsteroids :: AstList-> Graphic
drawAsteroids al = overGraphics (map drawAsteroid al)

------------- zeichnen die Projektile
drawBullet :: Bullet-> Graphic
drawBullet b = withRGB ((firePl!!(bulCol `mod` (length firePl)))!!((bcol b) `mod` 6)) (drawShape (bshp b))

drawBullets :: BulletList-> Graphic
drawBullets bl = overGraphics (map drawBullet bl)

------------- zeichnen die Splitter
drawShrep :: Shrepnel-> Graphic
drawShrep g = withRGB (shrPl!!(toInt(gcol g))) (drawShape (gshp g))

drawShreps :: ShrepList-> Graphic
drawShreps gl = overGraphics (map drawShrep gl)

------------- zeichnen die Hintergrundgrafiken
drawSpace :: Space-> Graphic
drawSpace u = withRGB (spacePl!!(ucol u)) (drawShape (ushp u))

drawSpaces :: SpaceList-> Graphic
drawSpaces ul = overGraphics (map drawSpace ul)

------------- überprüft den State und erzeugt neue Objekte
checkState :: [Int]-> State-> State
checkState r s  =
    s {ship = (checkShip (ship s) (asteroids s)), bullets = concat (map (checkBullet (asteroids s)) (bullets s)),
    shrepnels = if ((not (length (bullets s) == 0) && shrep) || ((shipDes (ship s)) && shrep)) then ((shrepnels s) ++
        concat (map (crShreps r (ship s) (bullets s)) (asteroids s))) else (shrepnels s),
    asteroids = if (length (asteroids s) == 0) then (newAst r) else if (length (bullets s) == 0) then (asteroids s) else
    concat (map (checkAst r (bullets s)) (asteroids s)),
    stats = if hud then refreshSt (stats s) (bullets s) else (stats s)
                    }

------------- überprüft Bullet-Kollision mit Asteroiden (beides Listen)
checkBullet :: AstList -> Bullet-> BulletList
checkBullet al b = if (collBull b al) then [] else [b]

------------- überprüft Asteroiden-Kollision mit Bullets (beides Listen)
checkAst :: [Int] -> BulletList -> Asteroid-> AstList
checkAst r bl a = if (collAst a bl) then (crAsteroids r a) else [a]

------------- erzeugt aus getroffenen Asteroiden neue Asteroiden
crAsteroids :: [Int]-> Asteroid-> AstList
crAsteroids r a
        | asize a == 2 = []
        | r!!1 < (-2) = [setAst Asteroid{apos= add (apos a) (10,15),avel=add (avel a) ((r!!1), (r!!5)),asize=(asize a+1),aort=pi/4},
                            setAst Asteroid{apos=add (apos a) (-15,-15),avel=add (avel a) ((r!!2), (r!!4)),asize=(asize a+1),aort=pi/(-3)}]
        | r!!1 < 1 = [setAst Asteroid{apos=add (apos a) (15,15),avel=add (avel a) ((r!!3), (r!!6)),asize=(asize a+1),aort=pi/6},
                         setAst Asteroid{apos=add (apos a) (5,-15),avel=add (avel a) ((r!!5), (r!!4)),asize=(asize a+1),aort=pi/(-7)},
                         setAst Asteroid{apos=add (apos a) (-15,10),avel=add (avel a) ((r!!7), (r!!1)),asize= if (asize a) == 0 then (asize a+2) else (asize a+1),aort=pi/2},
                         setAst Asteroid{apos=add (apos a) (-10,-15),avel=add (avel a) ((r!!2), (r!!2)),asize= if (asize a) == 0 then (asize a+2) else (asize a+1),aort=pi/(-2)}]
        | otherwise = [setAst Asteroid{apos=add (apos a) (15,10),avel=add (avel a) ((r!!4), (r!!5)),asize=(asize a+1),aort=pi/(-6)},
                            setAst Asteroid{apos=add (apos a) (10,-15),avel=add (avel a) ((r!!2), (r!!3)),asize=(asize a+1),aort=pi/7},
                            setAst Asteroid{apos=add (apos a) (-15,15),avel=add (avel a) ((r!!7), (r!!6)),asize= if (asize a) == 0 then (asize a+2) else (asize a+1),aort=pi/2}]

------------- überprüft Asteroiden-Kollision mit Bullets und erzeugt Splitter
crShreps :: [Int]-> Ship-> BulletList-> Asteroid->  ShrepList
crShreps r sh bl a = if (collAst a bl) then [setShrep   Shrepnel{gpos = (apos a), gvel =((r!!5),(r!!7)),gcnt=15,gcol=0},
                        setShrep Shrepnel{gpos = (apos a), gvel =((r!!2),(r!!4)),gcnt=35,gcol=0},
                        setShrep Shrepnel{gpos = (apos a), gvel =((r!!6),(r!!3)),gcnt=55,gcol=0}]
                     else if (shipDes sh) then [setShrep   Shrepnel{gpos = (spos sh), gvel =((r!!5),(r!!7)),gcnt=15,gcol=0},
                        setShrep Shrepnel{gpos = (spos sh), gvel =((r!!6),(r!!3)),gcnt=55,gcol=0}]
                     else []

------------- überprüft Schiffskolision mit Asteroiden
collShip :: Ship-> AstList-> Bool
collShip sh [] = False
collShip sh al  = or (map (collides (sshp sh)) al)
  where collides sp a = intersect sp (ashp a)

------------- überprüft, ob ein Asteroid mit Bullet-Liste kollidiert
collAst :: Asteroid-> BulletList-> Bool
collAst a bl  = or (map (collides (ashp a)) bl)
  where collides sp b = intersect sp (bshp b)

------------- überprüft, ob ein Bullet mit Asteroiden-Liste kollidiert
collBull :: Bullet-> AstList-> Bool
collBull b [] = False
collBull b al  = or (map (collides (bshp b)) al)
  where collides sp a = intersect sp (ashp a)

------------- berechnet die Statistiken neu
refreshSt :: Stats-> BulletList-> Stats
refreshSt h bl = h { fails = (fails h) + (length [ e | e <- bl, ((bcou e) < 0) ]),
                hits = ((spl h) - (fails h) - (length bl)), ratio = if (spl h)>0 then ((hits h)*100 `div` (spl h)) else 0}

------------- erzeugt neue Asteroiden nach Säuberung des Space
newAst :: [Int]-> AstList
newAst r = [setAst (Asteroid{apos=(0,0), avel=(r!!5,r!!2), asize= 0, aort=pi/3 }),
            setAst (Asteroid{apos=(0,0), avel=(r!!3,r!!4), asize= 0, aort=pi/(-4)}),
            setAst (Asteroid{apos=(0,0), avel=(r!!1,r!!7), asize= 0, aort=pi/2}),
            setAst (Asteroid{apos=(0,0), avel=(r!!6,r!!8), asize= 0, aort=pi})]

------------- überprüft, ob das Schiff getroffen ist, und "zerstört" es
checkShip :: Ship-> AstList-> Ship
checkShip sh a = if (collShip sh a) then sh{shipDes= True, sshp= shape (sfDestr)}
                else sh

------------- wiederholt den gesamten Programmablauf
loop :: Window-> State-> IO ()
loop w s =
  do setGraphic w (drawState s)
     getWindowTick w
     evs<- getEvs
     s<- nextState evs s
     g<- newStdGen

     if (end s) then (closeWindow w)
       else if ((shipDes (ship s)) && not godmode && (rsCnt s) > 0)
         then loop w s{rsCnt= (rsCnt s)-1}
       else if ((shipDes (ship s)) && not godmode)
         then loop w (initialState (randomRs ((-astSpd),astSpd)  g) (randomRs (1,8) g) (randomRs (0,12) g))
       else loop w s
     where

       nextState :: [Event]-> State-> IO State
       nextState evs s =
         do g <- newStdGen
            return (
              checkState (randomRs (-(astSpd+(astSpd `div` 2)), (astSpd +(astSpd `div` 2))) g) s1{
                ship= moveShip (ship s1),
                bullets = moveBullets (bullets s1),
                shrepnels = moveShreps (shrepnels s1),
                asteroids = moveAsteroids(asteroids s1)

              }) where
            s1= foldl (flip procEv) s evs

       getEvs :: IO [Event]
       getEvs = do x<- maybeGetWindowEvent w
                   case x of
                     Nothing -> return []
                     Just e  -> do rest <- getEvs
                                   return (e : rest)

------------- Überprüfung der Tasteneingaben
       procEv :: Event-> State-> State
       procEv (Key {keysym= k, isDown=down})
        | isLeftKey k && down       = sethAcc hDelta
        | isLeftKey k && not down   = sethAcc 0
        | isRightKey k && down      = sethAcc (- hDelta)
        | isRightKey k && not down  = sethAcc 0
        | isUpKey k && down         = setThrust aDelta
        | isUpKey k && not down     = setThrust 0
        | isKey ' ' k && down       = fire
        | isEscapeKey k && down     = setEnd
        | isDownKey k && down && specKeys       = setThrust (- aDelta)
        | isDownKey k && not down && specKeys   = setThrust 0
        | isKey 'w' k && down && specKeys       = warp
        | isKey 'c' k && down && specKeys       = burst
        | isTabKey k && down && specKeys        = quickTurn
        | isReturnKey k && down && specKeys     = setSpeed 0
        | isReturnKey k && not down && specKeys = setSpeed 0
        | isKey 'y' k && down && specKeys       = setStrafe aDelta
        | isKey 'y' k && not down && specKeys   = setStrafe 0
        | isKey 'x' k && down && specKeys       = setStrafe (- aDelta)
        | isKey 'x' k && not down && specKeys   = setStrafe 0
       procEv _ = id

       isKey :: Char-> Key-> Bool
       isKey c k = (isCharKey k) && (keyToChar k == c)

------------- setzt Schiffbeschleunigung
       sethAcc :: Double->State-> State
       sethAcc a s = s{ship= (ship s){hAcc= a}}

------------- setzt Schiffsschub
       setThrust :: Double-> State-> State
       setThrust a s = if (not static ) then s{ship= (ship s){thrust= a}} else s

------------- setzt Schiffsgeschwindigkeit
       setSpeed :: Double-> State-> State
       setSpeed a s = s{ship= (ship s){svel = (0,0)}}

------------- setzt Seitwärtsschub des Schiffs
       setStrafe :: Double-> State-> State
       setStrafe a s = if (not static) then s{ship= (ship s){hthrust= a}} else s

------------- setzt Schiff auf Bildschirmmitte
       warp :: State-> State
       warp s = s{ship=(ship s){svel = (0,0), spos = (fst winSize `div` 2, snd winSize `div` 2), ornt = pi/2}}

------------- wendet das Schiff auf der Stelle
       quickTurn :: State-> State
       quickTurn s = s{ship= (ship s) {ornt = (ornt (ship s))+(pi), svel = (0,0) }}

------------- beendet Spiel
       setEnd :: State-> State
       setEnd s = s{end=True}

------------- feuert ein Projektil ab
       fire :: State-> State
       fire s =
         if (length(bullets s) < maxBul && (not (shipDes (ship s)))) then
           s{ bullets = ( setBullet Bullet{bpos= spos (ship s),
             bort = (ornt (ship s)), bvel= polar bSpeed (ornt (ship s)),
             bcou=bMaxFlight, bcol = 0}):(bullets s),
             stats = (stats s) { spl = ((spl (stats s)) +1)}
           }
         else s

------------- feuert Starburst-Ladung ab
       burst :: State-> State
       burst s = if (length(bullets s) < 3 ) && ((sbur (ship s)) >0 && (not (shipDes (ship s)))) then
         s{ bullets = (bullets s) ++
           [setBullet (Bullet{bpos= add (1,0) (spos (ship s)), bort = ((ornt (ship s))+(pi/12)), bcou=(bMaxFlight * 0.8),
                bvel= polar bSpeed ((ornt (ship s))+(pi/12)),  bcol = 0}),
            setBullet (Bullet{bpos= add (1,0) (spos (ship s)), bort = ((ornt (ship s))+(pi/6)), bcou=(bMaxFlight * 0.8),
                bvel= polar bSpeed ((ornt (ship s))+(pi/6)),  bcol = 0}),
            setBullet (Bullet{bpos= add (1,0) (spos (ship s)), bort = ((ornt (ship s))+(pi/360)), bcou=(bMaxFlight * 0.8),
                bvel= polar bSpeed ((ornt (ship s))),  bcol = 0}),
            setBullet (Bullet{bpos= add (1,0) (spos (ship s)), bort = ((ornt (ship s))-(pi/12)), bcou=(bMaxFlight * 0.8),
                bvel= polar bSpeed ((ornt (ship s))-(pi/12)),  bcol = 0}),
            setBullet (Bullet{bpos= add (1,0) (spos (ship s)), bort = ((ornt (ship s))-(pi/6)), bcou=(bMaxFlight * 0.8),
                bvel= polar bSpeed ((ornt (ship s))-(pi/6)),  bcol = 0})],
              stats = (stats s) { spl = ((spl (stats s)) +5)}, ship = (ship s) {sbur = ((sbur (ship s))-1)} }
                 else s

------------- erzeugt Fenster, startet die Programmausführung und schließt Fenster später
main :: IO ()
main = runGraphics $
  do w<- openWindowEx "Spaceman Spiff in War Stars: The Haskell Menace"
                      Nothing winSize DoubleBuffered
                      (Just 30)
     g<- newStdGen
     loop w (initialState (randomRs ((-astSpd),astSpd)  g) (randomRs (1,8) g) (randomRs (0,12) g))
     closeWindow w

-------------------------------------- Programm Ende -----------------------------------------