packages feed

Monadius-0.99: src/Monadius.hs

module Monadius (
  Monadius(..),
  initialMonadius,
  getVariables,
  GameVariables(..),
  shotButton,
  missileButton,
  powerUpButton,
  upButton,
  downButton,
  leftButton,
  rightButton,
  selfDestructButton
  ) where

import Data.Array ((!), Array(), array)
import Data.Complex
import Data.List
import Data.Maybe
import Graphics.UI.GLUT hiding (position)

import Game
import Util

instance Game Monadius where
  update = updateMonadius
  render = renderMonadius
  isGameover = isMonadiusOver

newtype Monadius = Monadius (GameVariables,[GameObject])

getVariables :: Monadius -> GameVariables
getVariables (Monadius (vs,_))=vs

data GameVariables = GameVariables {
  totalScore :: Int,hiScore :: Int ,flagGameover :: Bool,
  nextTag :: Int, gameClock :: Int,baseGameLevel :: Int,playTitle :: Maybe String
  } deriving Eq

data GameObject = -- objects that are actually rendered and moved.
  VicViper{ -- player's fighter.
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hp :: Int,
    trail :: [Complex GLdouble],
    speed :: GLdouble,
    powerUpPointer :: Int,
    powerUpLevels :: Array Int Int,
    reloadTime :: Int,weaponEnergy :: Int,
    ageAfterDeath :: Int
    } |
  Option{ -- trailing support device.
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,
    optionTag :: Int,
    reloadTime :: Int,weaponEnergy :: Int} |
  StandardMissile{
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hp :: Int,mode :: Int,
    velocity :: Complex GLdouble,parentTag :: Int,probe :: GameObject } | -- missile that fly along the terrain
  Probe{ -- this lets missile to fly along the terrain
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hp :: Int
  } |
  StandardRailgun{
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hitDispLand :: Shape,hp :: Int,
    velocity :: Complex GLdouble,parentTag :: Int } | -- normal & double shot
  StandardLaser{
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hitDispLand :: Shape,hp :: Int,
    velocity :: Complex GLdouble,parentTag :: Int,age :: Int } | -- long blue straight laser
  Shield{
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hitDispLand :: Shape,hp :: Int,
    settled :: Bool,size :: GLdouble,placement :: Complex GLdouble,
    angle :: GLdouble,omega :: GLdouble} |  -- solid state of Reek power that protects enemy atacks
  PowerUpCapsule{
    tag :: Maybe Int,position :: Complex GLdouble,hitDisp :: Shape,hp :: Int,age :: Int} |
  PowerUpGauge{
    tag :: Maybe Int,position :: Complex GLdouble} |

  DiamondBomb{
    tag :: Maybe Int,position :: Complex GLdouble,velocity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int} |  -- Bacterian's most popular warhead
  TurnGear{
    tag :: Maybe Int,position :: Complex GLdouble,velocity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int,mode :: Int,
    managerTag :: Int} |  -- one of small Bacterian lifeforms, often seen in a squad.
  SquadManager{
    tag :: Maybe Int,position :: Complex GLdouble,interval :: Int,age :: Int,
    bonusScore :: Int,currentScore :: Int,
    members :: [GameObject],items :: [GameObject]
  } |
  -- 1. generates objects contained in <members> with <interval>, one at each time.
  -- 2. sticks to one of the still-alive troop members.
  -- 3. counts up <currentScore> every time when one of the squad members are destroyed by lack of hp.
  -- 4. doesn't count up <currentScore> if a squad member are destroyed by scrolling out.
  -- 5. dies when all squad members were destroyed. at this time,
  --        releases <items> if <currentScore> >= <bonusScore>, or
  --        doesn't ,if not.
  Jumper{
    tag :: Maybe Int,position :: Complex GLdouble,velocity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int,hasItem :: Bool,gravity :: Complex GLdouble,
    touchedLand :: Bool,jumpCounter :: Int
  } | -- dangerous multi way mine dispenser.

  Grashia{
    tag :: Maybe Int,position :: Complex GLdouble,velocity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int,hasItem :: Bool,gravity :: Complex GLdouble,
    gunVector :: Complex GLdouble,mode :: Int
  } | -- fixed antiaircraft cannon.

  Ducker{
    tag :: Maybe Int,position :: Complex GLdouble,velocity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int,hasItem :: Bool,gVelocity :: Complex GLdouble,
    charge :: Int,vgun :: Complex GLdouble,touchedLand :: Bool
  } | -- 2-feet mobile land to air attack device.

  Flyer{
    tag :: Maybe Int,position :: Complex GLdouble,velocity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int,hasItem :: Bool,mode :: Int
  } | -- Baterian's standard interceptor.

  ScrambleHatch{
    tag :: Maybe Int,position :: Complex GLdouble,gateAngle :: GLdouble,gravity :: Complex GLdouble,
    hitDisp :: Shape,hp :: Int,age :: Int,launchProgram :: [[GameObject]]
  } | -- Where Baterian larvae spend last process of maturation.

  LandScapeBlock{
    tag :: Maybe Int, position :: Complex GLdouble,hitDisp :: Shape,velocity :: Complex GLdouble
  } | -- landscape that just look like, and hit like its hitDisp.

  Particle{
    tag :: Maybe Int, position :: Complex GLdouble, velocity :: Complex GLdouble,
    size :: GLdouble,particleColor :: Color3 GLdouble,age :: Int,decayTime :: GLdouble,expireAge :: Int
  } | -- multi purpose particles that vanishes after expireAge.

  Star{
    tag :: Maybe Int, position :: Complex GLdouble,particleColor :: Color3 GLdouble
  } | -- background decoration
  SabbathicAgent{
    tag :: Maybe Int, fever :: Int
  } | -- generates many flyers for additional fun if there are none of them
  DebugMessage {tag :: Maybe Int,debugMessage :: String} |
  ScoreFragment{tag :: Maybe Int,score :: Int}

data HitClass = BacterianShot |
                BacterianBody |
                LaserAbsorber |
                MetalionShot |
                MetalionBody |
                ItemReceiver |
                PowerUp |
                LandScape
                deriving(Eq)

data WeaponType = NormalShot | Missile | GLdoubleShot | Laser
                  deriving (Eq)
-- WeaponType NormalShot | Missile | GLdoubleShot | Laser ... represents function of weapon that player selected, while
-- GameObject StandardRailgun | StandardLaser ... represents the object that is actually shot and rendered.
-- for example;
-- shooting NormalShot :: WeaponType and GLdoubleShot :: WeaponType both result in StandardRailgun :: GameObject creation, and
-- shooting Laser :: WeaponType creates StandardLaser :: GameObject when player is operating VicViper, or RippleLaser :: GameObject when LordBritish ... etc.

data ScrollBehavior = Enclosed{doesScroll :: Bool} | NoRollOut{doesScroll :: Bool}
 | RollOutAuto{doesScroll :: Bool, range :: GLdouble}  | RollOutFold{doesScroll :: Bool}

-----------------------------
--
--   initialization
--
------------------------------
initialMonadius :: GameVariables -> Monadius
initialMonadius initVs = Monadius (initGameVariables,initGameObjects)
    where
      initGameVariables = initVs
      initGameObjects =
          stars ++ [freshVicViper,freshPowerUpGauge]
      stars = take 26 $ map (\(t,i) -> Star{tag=Nothing,position = (fix 320 t:+fix 201 t),particleColor=colors!!i}) $ zip (map (\x -> square x + x + 41) [2346,19091..]) [1..]
      fix :: Int -> Int -> GLdouble
      fix limit value = intToGLdouble $ (value `mod` (2*limit) - limit)
      colors = [Color3 1 1 1,Color3 1 1 0,Color3 1 0 0, Color3 0 1 1] ++ colors
    --  ++ map (\x -> freshOption{optionTag = x}) [1..4]  -- full option inchiki

{-
Default settings of game objects and constants
-}
downButton,leftButton,missileButton,powerUpButton,rightButton,selfDestructButton,shotButton,upButton :: Key
downButton = SpecialKey KeyDown
leftButton = SpecialKey KeyLeft
missileButton = Char 'x'
powerUpButton = Char 'c'
rightButton = SpecialKey KeyRight
selfDestructButton = Char 'g'
shotButton = Char 'z'
upButton = SpecialKey KeyUp

-- Cuteness to add later
-- konamiCommand = [upButton,upButton,downButton,downButton,leftButton,rightButton,leftButton,rightButton,missileButton,shotButton]

gaugeOfMissile,gaugeOfGLdouble,gaugeOfLaser,gaugeOfShield :: Int
gaugeOfMissile = 1
gaugeOfGLdouble  = 2
gaugeOfLaser   = 3
gaugeOfShield  = 5

stageClearTime :: Int
stageClearTime = 7800

-- these lists are game rank modifiers.
bacterianShotSpeedList, duckerShotWay, jumperShotFactor, grashiaShotSpeedFactor :: [GLdouble]
bacterianShotSpeedList = [8,4,6,8] ++ cycle [12,8]
duckerShotCount = [2,1,1,3] ++ repeat 2
duckerShotWay = [1,1,2,1] ++ cycle [2,2]
jumperShotFactor = [0.5,0.5,0.5,0.5] ++ cycle [0.8,0.5]
grashiaShotSpeedFactor = [1,1,1,1] ++ cycle [1,0.6]

flyerHitBack, particleHitBack, powerUpCapsuleHitBack, scrambleHatchHitBack, treasure, turnGearHitBack :: [Bool]
flyerHitBack = [False,False,False] ++ repeat True
particleHitBack = True:repeat False
powerUpCapsuleHitBack = [False,False,False,False] ++ cycle [False,True]
scrambleHatchHitBack = [False,False,False,False] ++ cycle [False,True]
treasure = [False,False,False,False] ++ cycle [False,True]
turnGearHitBack = [False,False,False] ++ repeat True

duckerShotCount, flyerShotInterval, grashiaShotHalt, grashiaShotInterval, inceptorShotInterval, jumperShotWay, landRollShotInterval, scrambleHatchLaunchLimitAge :: [Int]
flyerShotInterval = [30,infinite,30,15] ++ cycle [15,15]
grashiaShotHalt = [50,100,50,50] ++ cycle [0,0]
grashiaShotInterval = [30,60,30,30] ++ cycle [15,5]
inceptorShotInterval = [45,infinite,60,45] ++ cycle [45,45]
jumperShotWay = [16,4,8,16] ++ cycle [24,32]
landRollShotInterval = [60,120,60,60] ++ cycle [30,60]
scrambleHatchLaunchLimitAge = [400,200,400,400] ++ cycle [600,400]

shotSpeed,laserSpeed,laserBreadth :: GLdouble
shotSpeed = 25
laserSpeed = 60
laserBreadth = 20

landScapeSensitive :: GameObject -> Bool
landScapeSensitive StandardRailgun{} = True -- these objects has hitDispLand
landScapeSensitive StandardLaser{} = True   -- in addition to hitDisp
landScapeSensitive Shield{} = True
landScapeSensitive _ = False

vicViperSize :: GLdouble
vicViperSize = 6

shieldPlacementMargin,shieldHitMargin :: GLdouble
shieldPlacementMargin = 5
shieldHitMargin = 10

shieldMaxHp, hatchHP :: Int
shieldMaxHp = 16
hatchHP = 15

diamondBombSize,smallBacterianSize,hatchHeight :: GLdouble
diamondBombSize = 6
hatchHeight = 35
smallBacterianSize = 16

freshDiamondBomb, freshFlyer, freshInterceptor, freshOption, freshPowerUpCapsule, freshPowerUpGauge, freshShield, freshStalk, freshStandardLaser, freshStandardMissile, freshStandardRailgun, freshTurnGear, freshTurnGearSquad, freshVicViper :: GameObject
freshDiamondBomb = DiamondBomb{tag=Nothing,position=0:+0,velocity=0:+0,hp=1,hitDisp=Circular (0:+0) diamondBombSize,age=0}
freshFlyer = Flyer{tag=Nothing,position=0:+0,velocity=(-3):+0,hitDisp=Circular 0 smallBacterianSize,hp=1,age=0,hasItem=False,mode=0}
freshInterceptor = freshFlyer{mode=1,velocity = 0:+0}
freshOption = Option{tag = Nothing, position=0:+0, hitDisp = Circular (0:+0) 0,optionTag = 0,reloadTime=0,weaponEnergy=100}
freshPowerUpCapsule = PowerUpCapsule{tag = Nothing, hitDisp = Circular (0:+0) 30,position = 0:+0,hp=1,age=0}
freshPowerUpGauge = PowerUpGauge{tag=Nothing, position = (-300):+(-240)}
freshShield = Shield{tag=Nothing,position=380:+0,hitDisp=Circular (0:+0) 0,hitDispLand=Circular (0:+0) 0,hp=shieldMaxHp,settled=False,size=0,placement=0:+0,angle=0,omega=0}
freshStalk = freshFlyer{mode=10,velocity = (-2):+0}
freshStandardLaser = StandardLaser{tag=Nothing,position=0:+0,hitDisp=Rectangular (laserSpeed/(-2):+(-laserBreadth)) (laserSpeed/2:+laserBreadth),hitDispLand = Rectangular (laserSpeed/(-2):+(-vicViperSize)) (laserSpeed/2:+vicViperSize),velocity=laserSpeed:+0,hp=1,parentTag=0,age=0}
freshStandardMissile = StandardMissile{tag=Nothing,position=0:+0,hitDisp=Circular 0 7,velocity=0:+0,hp=1,parentTag=0,probe=Probe{tag=Nothing,position=0:+0,hitDisp=Circular (0:+(-5)) 12,hp=1},mode=0}
freshStandardRailgun = StandardRailgun{tag=Nothing,position=0:+0,hitDisp=Circular 0 12,hitDispLand = Circular (0:+0) vicViperSize,velocity=shotSpeed:+0,hp=1,parentTag=0}
freshTurnGear = TurnGear{tag=Nothing,position=0:+0,velocity=0:+0,hp=1,hitDisp=Circular (0:+0) smallBacterianSize,age=0,managerTag=0,mode=0}
freshTurnGearSquad = SquadManager{tag=Nothing,position=0:+0, interval=10, age=0,
  bonusScore=squadSize, currentScore=0, members = replicate squadSize freshTurnGear,items=[freshPowerUpCapsule]} where
  squadSize=6
freshVicViper = VicViper{tag = Nothing, position = 0:+0, hitDisp = Circular (0:+0) vicViperSize,hp=1, trail = repeat $ 0:+0,
      speed = 1,powerUpPointer=(-1),powerUpLevels=array (0,5) [(x,0)|x<-[0..5]],reloadTime=0,weaponEnergy=100,
      ageAfterDeath = 0}

freshDucker :: GLdouble -> GameObject
freshDucker vg = Ducker{tag = Nothing, position = 0:+0, velocity= 0:+0, hitDisp = Circular (0:+0) smallBacterianSize, hp = 1,
  age = 0, hasItem = False, gVelocity = 0:+(8*vg),charge = 0, vgun = 0:+0,touchedLand=False}
freshScrambleHatch :: GLdouble -> GameObject
freshScrambleHatch sign = ScrambleHatch{tag=Nothing,position=0:+0,hitDisp=regulate $ Rectangular ((-45):+0) (45:+(hatchHeight*(-sign))),gravity=(0:+sign),hp=hatchHP,age=0,
  launchProgram = cycle $ replicate 40 [] ++ (concat.replicate 6) ([[freshInterceptor{velocity = 0:+(-6)*sign}]]++replicate 9 []),gateAngle=0
  }

freshVolcano :: GLdouble -> GameObject
freshVolcano grvty = LandScapeBlock{tag=Nothing,position=0:+0,velocity=0:+0,hitDisp=
  Shapes $ map (regulate.(\i -> Rectangular ((120 - 33*i + 2*i*i):+ sign*30*i) ((33*i - 2*i*i - 120) :+ sign*30*(i+1)) ) ) [0..4]}
  where sign = (-grvty)

freshTable :: GLdouble -> GameObject
freshTable grvty =  LandScapeBlock{tag=Nothing,position=0:+0,velocity=0:+0,hitDisp= Shapes $ map (regulate.(\i -> Rectangular ((-2**(i+3)+shiftSinePi i):+sign*30*i) ((2**(i+3)+shiftSinePi i) :+sign*30*(i+1)))) [0..4]
} where
    sign = (- grvty)

    shiftSinePi :: (Floating a) => a -> a
    shiftSinePi a = 5 * sin (a*0.5*pi)

freshGrashia, freshJumper, freshLandRoll :: GLdouble -> GameObject
freshGrashia sign= Grashia{tag=Nothing,position=0:+0,velocity=0:+0,
  hitDisp=Circular 0 smallBacterianSize,hp=1,age=0,hasItem=False,gravity=(0:+sign),gunVector=0:+0,mode=0}
freshJumper sign=Jumper{tag=Nothing,position=0:+0,velocity=0:+0,
                    hitDisp=Circular 0 smallBacterianSize,hp=1,age=0,hasItem=False,gravity=(0:+0.36*sign),touchedLand=False,jumpCounter=0}
freshLandRoll sign = (freshGrashia sign){mode=1}

freshLandScapeGround, freshSabbathicAgent :: GameObject
freshLandScapeGround = LandScapeBlock{tag=Nothing,position=0:+0,velocity=0:+0,hitDisp=Rectangular ((-158):+(-20)) (158:+20)}
freshSabbathicAgent = SabbathicAgent{tag=Nothing,fever=1}

freshScore :: Int -> GameObject
freshScore point = ScoreFragment{tag=Nothing,score = point}

-----------------------------
--
--  game progress
--
-----------------------------

updateMonadius :: [Key] -> Monadius -> Monadius
updateMonadius realKeys (Monadius (variables,objects))
 = Monadius (newVariables,newObjects) where
  gameVariables = variables
  gameObjects   = objects
  gameLevel = baseGameLevel gameVariables
  bacterianShotSpeed = bacterianShotSpeedList!!gameLevel


  keys = if hp vicViper<=0 then [] else realKeys
   -- almost all operation dies when vicViper dies. use realKeys to fetch unaffected keystates.

  (newNextTag,newObjects) = issueTag (nextTag variables) $
                            (loadObjects ++) $
                            filterJust.map scroll $
                            concatMap updateGameObject $
                            gameObjectsAfterCollision
  gameObjectsAfterCollision = collide objects
  -- * collision must be done BEFORE updateGameObject(moving), for
  --   players would like to see the moment of collision.
  -- * loading new objects after collision and moving is nice idea, since
  --   you then have new objects appear at exact place you wanted them to.
  -- * however, some operation would like to refer the result of the collision
  --   before it is actually taken effect in updateGameObject.
  --   such routine should use gameObjectsAfterCollision.


  newVariables = variables{
    nextTag = newNextTag,
    flagGameover = flagGameover variables ||  ageAfterDeath vicViper > 240,
    gameClock = (\c -> if hp vicViper<=0 then c else if goNextStage then 0 else c+1) $ gameClock variables,
    baseGameLevel = (\l -> if goNextStage then l+1 else l) $ baseGameLevel variables,
    totalScore = newScore,
    hiScore = max (hiScore variables) newScore
  }
   where
    goNextStage = gameClock variables > stageClearTime
    newScore = totalScore variables +  (sum) (map (\obj -> case obj of
      ScoreFragment{score = p} -> p
      _ -> 0) objects  :: [Int])

  updateGameObject :: GameObject -> [GameObject]
  -- update each of the objects and returns list of resulting objects.
  -- the list usually includes the modified object itself,
  --   may include several generated objects such as bullets and explosions,
  --   or include nothing if the object has vanished.

  updateGameObject vic@VicViper{} = newShields ++ makeMetalionShots vic{
    position=position vic + (vmag*(speed vic):+0) * (vx:+vy) ,
    trail=(if isMoving then ((position vic-(10:+0)):) else id) $ trail vic,
    powerUpLevels =
      (modifyArray gaugeOfShield (const (if (shieldCount > 0) then 1 else 0))) $
      (if doesPowerUp then (
      modifyArray (powerUpPointer vic)
        (\x -> if x<powerUpLimits!!powerUpPointer vic then x+1 else 0) . -- overpowering-up results in initial powerup level.
      (if powerUpPointer vic==gaugeOfGLdouble then modifyArray gaugeOfLaser (const 0) else id) . -- laser and double are
      (if powerUpPointer vic==gaugeOfLaser then modifyArray gaugeOfGLdouble (const 0) else id) ) --  exclusive equippment.
        else id) (powerUpLevels vic),
    powerUpPointer = if doesPowerUp then (-1) else powerUpPointer vic,
    speed = speeds !! (powerUpLevels vic!0),
    reloadTime = max 0 $ reloadTime vic - 1,
    ageAfterDeath = if hp vic>0 then 0
                    else ageAfterDeath vic+1,
    hitDisp = if treasure!!gameLevel then Circular 0 0 else Circular (0:+0) vicViperSize,
    hp = if selfDestructButton `elem` keys then 0 else hp vic
  } where
    vx = (if (rightButton `elem` keys) then 1    else 0) +
         (if (leftButton  `elem` keys) then (-1) else 0)
    vy = (if (upButton    `elem` keys) then 1    else 0) +
         (if (downButton  `elem` keys) then (-1) else 0)
    vmag = if vx*vx+vy*vy>1.1 then sqrt(0.5) else 1
    isMoving = any (\b ->  elem b keys) [rightButton,leftButton,upButton,downButton]
    doesPowerUp = (powerUpButton `elem` keys) && (powerUpPointer vic >=0) &&
      (powerUpPointer vic ==0 ||
       powerUpLevels vic!powerUpPointer vic<powerUpLimits!!powerUpPointer vic)
    speeds = [2,4,6,8,11,14] ++ speeds
    shieldCount :: Int
    shieldCount = sum $ map (\o -> case o of
      Shield{} -> 1
      _ -> 0) gameObjects
    newShields = if (doesPowerUp && powerUpPointer vic==gaugeOfShield) then
      [freshShield{position=350:+260   ,placement=40:+shieldPlacementMargin,   angle=30,omega=10   },
       freshShield{position=350:+(-260),placement=40:+(-shieldPlacementMargin),angle=0 ,omega=(-10)}]
      else []

  updateGameObject option@Option{} = makeMetalionShots option{
    position = trail vicViper !! (10*optionTag option),
    reloadTime = max 0 $ reloadTime option - 1
  }

  updateGameObject miso@StandardMissile{} = [
                                       miso{position=newpos,
                                            mode = newmode,
                                            velocity=v,
                                            probe = (probe miso){position=newpos,hp=1}
                                           }
                                      ]  where
      newmode = if hp(probe miso) <= 0 then 1 else
        if mode miso == 0 then 0 else 2
      v = case newmode of
        0 -> 3.5:+(-7)
        1 -> 8:+0
        2 -> 0:+(-8)
        _ -> 0
      newpos = position miso + v

  updateGameObject shot@StandardRailgun{} = if hp shot <=0 then [] else
    [shot{position=position shot + velocity shot}]

  updateGameObject laser@StandardLaser{} = if hp laser <=0 then [] else
    [laser{position=(\(x:+_) -> x:+parentY) $ position laser + velocity laser,age=age laser+1}]  where
      myParent = head $ filter (\o -> tag o==Just (parentTag laser)) gameObjects
      _:+parentY = position myParent

  updateGameObject shield@Shield{} = if(hp shield<=0) then [] else [
    (if settled shield then
      shield{
        position=target,size=shieldPlacementMargin+intToGLdouble (hp shield),
        hitDisp = Circular (0:+0) (size shield+shieldHitMargin),
        hitDispLand = Circular (0:+0) (size shield)
      }
    else
      shield{hp=shieldMaxHp,size=5+intToGLdouble (hp shield),position=newPosition,settled=chaseFactor>0.6})
    {angle=angle shield + omega shield}
    ] where
      newPosition = position shield + v
      v =  difference * (chaseFactor:+0)
      chaseFactor = (10/magnitude difference)
      difference = target-position shield
      target = position vicViper+(realPart (placement shield) :+ additionalPlacementY)
      additionalPlacementY = signum (imagPart$placement shield)*size shield

  updateGameObject pow@PowerUpCapsule{} = if(hp pow<=0) then [freshScore 800] else [
    pow{age=age pow + 1}
    ] ++ if powerUpCapsuleHitBack!!gameLevel && age pow ==1 then
     map (\theta -> freshDiamondBomb{position=position pow,velocity=mkPolar (bacterianShotSpeed*0.5) theta}) $
       take 8 $ iterate (+(2*pi/8)) (pi/8)  else []


  updateGameObject bullet@DiamondBomb{} = if hp bullet<=0 then [] else
    [bullet{position=position bullet + velocity bullet,age=age bullet+1}]
  updateGameObject self@TurnGear{position=pos@(x:+y),mode=m} = if hp self<=0 then
     [freshScore 50] ++ freshExplosions pos ++ if turnGearHitBack!!gameLevel then [scatteredNeraiDan pos (bacterianShotSpeed:+0)] else []
   else [
    self{
      position = position self + velocity self,
      age = age self + 1,
      mode = newmode,
      velocity = newv
    }] where
      newv = case m of
        0 -> ((-4):+0)
        1 -> if (y - (imagPart.position) vicViper) > 0 then (3:+(-5)) else (3:+(5))
        _ -> if isEasy then 2:+0 else 6:+0
      newmode = if m==0 && x < (if not isEasy then -280 else 0) && (realPart.position) vicViper> (-270) then 1 else
                    if m==1 && abs (y - (imagPart.position) vicViper) < 20 then 2 else m

  updateGameObject me@SquadManager{position=pos,interval=intv,members=membs,age=clock,tag=Just myTag} =
    if mySquadIsWipedOut then(
      if currentScore me >= bonusScore me then map (\o -> o{position=pos}) (items me) else []
     )else me{
      age = age me + 1,
      currentScore = currentScore me + todaysDeaths,
      position = if clock <= releaseTimeOfLastMember then pos else warFront
    }:dispatchedObjects where
      dispatchedObjects = if (clock `div` intv < length membs && clock `mod` intv == 0) then
        [(membs!!(clock `div` intv)){position=pos,managerTag=myTag}] else []
      todaysDeaths = sum $ map (\o -> if hp o <=0 then 1 else 0) $ mySquad
      mySquadIsWipedOut = clock > releaseTimeOfLastMember && length mySquad <= 0
      warFront = position $ head $ mySquad
      releaseTimeOfLastMember = intv * (length membs-1)
      mySquad = filter (\o -> case o of
        TurnGear{managerTag=hisManagerTag} -> hisManagerTag == myTag  -- bad, absolutely bad code
        _                                  -> False) gameObjectsAfterCollision

  updateGameObject this@Flyer{position=pos@(x:+_),age=myAge,mode=m,velocity =v} =
    if gameClock variables > stageClearTime - 100 then freshExplosions pos else
    if hp this <=0 then ([freshScore (if mode this == 10 then 30 else 110)] ++ freshExplosions pos++(if hasItem this then [freshPowerUpCapsule{position=pos}] else if flyerHitBack!!gameLevel then [scatteredNeraiDan pos (bacterianShotSpeed:+0)] else []))
     else
    [this{
      age=myAge+1,
      position = pos + v ,
      velocity = newV,
      mode = newMode
    }]++myShots where
      newV = case m of
        00 -> (realPart v):+sin(intToGLdouble myAge / 5)
        01 -> v --if magnitude v <= 0.01 then if imagPart (position vicViper-pos)>0 then 0:+10 else 0:+(-10) else v
        02 -> (-4):+0
        10 -> if (not isEasy) || myAge < 10 then stokeV else v
        _ -> v
      stokeV = angleAccuracy 16 $ (* ((min (speed vicViper*0.75) (intToGLdouble$round$magnitude v)):+0) ) $ unitVector $ position vicViper-pos
      newMode = case m of
        01 -> if myAge > 20 && (position vicViper - pos) `innerProduct` v < 0 then 02 else 01
        _  -> m
      myShots = if (myAge+13*(fromJust $tag this)) `mod` myInterval == 0 && (x <= (-80) || x <= realPart(position vicViper))
        then [jikiNeraiDan pos (bacterianShotSpeed:+0)] else []
      myInterval = if m==00 || m==03 then flyerShotInterval!!gameLevel else inceptorShotInterval!!gameLevel

  updateGameObject me@Ducker{position=pos@(_:+_),velocity = v,age=myAge,gVelocity= vgrav,touchedLand = touched} =
    if hp me <=0 then ([freshScore 130] ++ freshExplosions pos ++ if hasItem me then [freshPowerUpCapsule{position=pos}] else[]) else
    [me{
      age=myAge+1,
      position = pos + v,
      charge = if charge me <=0 && aimRate > 0.9 && aimRate < 1.1 then (duckerShotCount!!gameLevel)*7+3
        else ((\x -> if x>0 then x-1 else x)  $  charge me),
      vgun = unitVector $ aimX:+aimY,
      velocity = if charge me >0 then 0:+0 else
        if magnitude v <= 0.01 then (
          if realPart(position vicViper - pos)>0 then 3:+0 else (-3):+0
        ) else if touched then
          ((realPart v):+(-imagPart vgrav))
        else (realPart v:+(imagPart vgrav)),
      touchedLand = False
    }]++myShots where
      aimX:+aimY = position vicViper - pos
      aimRate = (-(signum$realPart v))*aimX / (abs(aimY) +0.1)
      myShots = if charge me `mod` 7 /= 6 then [] else
        map (\w -> freshDiamondBomb{position=pos,velocity=w}) vs
      vs = map (\vy -> (vgun me)*(bacterianShotSpeed:+(1.5*(vy)))) [-duckerShotWay!!gameLevel+1 , -duckerShotWay!!gameLevel+3 .. duckerShotWay!!gameLevel-0.9]

  updateGameObject me@Jumper{position=pos@(_:+_),velocity = v,age=_,gravity= g,touchedLand = touched} =
    if hp me <=0 then ([freshScore 300] ++ freshExplosions pos ++if hasItem me then [freshPowerUpCapsule{position=pos}] else[]) else
    [
      me{
        position = pos + v,
        velocity = if touched then (signum(realPart $ position vicViper-pos)*abs(realPart v):+imagPart(jumpSize*g)) else v + g,
        jumpCounter = (if touched && v`innerProduct` g >0 then (+1) else id) $
          (if doesShot then (+1) else id) $ jumpCounter me,
        touchedLand = False
      }
    ] ++shots where
    jumpSize = if jumpCounter me `mod` 4 == 2 then (-30) else (-20)
    doesShot = jumpCounter me `mod` 4 == 2 && v`innerProduct` g >0
    shots = if doesShot then
      map (\theta -> freshDiamondBomb{position=pos,velocity=mkPolar (bacterianShotSpeed*jumperShotFactor!!gameLevel) theta}) $
        take way $ iterate (+(2*pi/intToGLdouble way)) 0
      else []
    way=jumperShotWay!!gameLevel

  updateGameObject me@ScrambleHatch{position = pos,age=a} =
    if hp me <=0 then [freshScore 3000] ++ freshMiddleExplosions pos ++
      if scrambleHatchHitBack!!gameLevel then hatchHitBacks else []
     else
    [me{
      age = a + 1,
      gateAngle = max 0$ min pi$ (if length currentLaunches>0 then (+1) else (+(-0.05))) $ gateAngle me
    }] ++ currentLaunches where
      currentLaunches = if a <= scrambleHatchLaunchLimitAge!!gameLevel then
          (map (\obj -> obj{position = pos}) $ launchProgram me!!a)
        else []
      hatchHitBacks =
        (map (\theta -> freshDiamondBomb{position=pos-16*gravity me,velocity=mkPolar (bacterianShotSpeed*0.5) theta}) $ take way $ iterate (+2*pi/intToGLdouble way) 0 )++
        (map (\theta -> freshDiamondBomb{position=pos-16*gravity me,velocity=mkPolar (bacterianShotSpeed*0.4) theta}) $ take way $ iterate (+2*pi/intToGLdouble way) (pi/intToGLdouble way) )
      way = 16


  updateGameObject me@Grashia{position = pos} =
    if hp me <=0 then ([freshScore 150] ++ freshExplosions pos++ if hasItem me then [freshPowerUpCapsule{position=pos}] else[]) else
    [
      me{
        age=age me+1,
        gunVector = unitVector $ position vicViper - pos,
        position = position me + ((-3)*sin(intToGLdouble (age me*mode me)/8):+0)
      } --V no shotto wo osoku
    ] ++ if age me `mod` myInterval == 0 && age me `mod` 200 > grashiaShotHalt!!gameLevel then
      [jikiNeraiDanAc (pos+gunVector me*(16:+0)) (grashiaShotSpeedFactor!!gameLevel*bacterianShotSpeed:+0) 64] else [] where
      myInterval = if mode me == 0 then grashiaShotInterval!!gameLevel else landRollShotInterval!!gameLevel

  updateGameObject me@Particle{position = pos} =
    if age me > expireAge me then (if particleHitBack!!gameLevel then [freshScore 10,scatteredNeraiDan pos (bacterianShotSpeed:+0)] else []) else
    [me{
      age = age me + 1,
      position = position me + (decay:+0) * velocity me
    }] where
      decay =  exp $  -  intToGLdouble (age me) / decayTime me

  updateGameObject me@LandScapeBlock{position = pos,velocity = v} = [me{position = pos+v}]

  updateGameObject DebugMessage{} = []

  updateGameObject ScoreFragment{} = []

  updateGameObject me@SabbathicAgent{fever = f} = if gameClock variables>stageClearTime-180  then [] else [
    me{
      fever = if launch then f+1 else f
    }]
    ++ if launch then map (\pos -> freshStalk{position = pos,velocity=(-4):+0,hasItem = (realPart pos>0 && (round $ imagPart pos :: Int) `mod` (3*round margin)==0)}) $
    concat $ map (\t -> [(340:+t),((-340):+t),(t:+(260)),(t:+(-260))]) $[(-margin*df),(((negate margin) * df) + (margin * 2))..(margin*df+1)] else [] where
      launch = (<=0) $ length $ filter (\obj -> case obj of
        Flyer{} -> True
        _ -> False) objects
      df = intToGLdouble f - 1
      margin :: GLdouble
      margin = 20

  updateGameObject x = [x]

  makeMetalionShots :: GameObject -> [GameObject]
  {- this generates proper playerside bullets
  according to the current power up state of vicviper.
  both options and vicviper is updated using this. -}

  makeMetalionShots obj = obj{reloadTime=reloadTime obj+penalty1+penalty2,
                              weaponEnergy = max 0 $ min 100 $ weaponEnergy obj + if doesLaser then (-10) else 50 }
                                      :(shots ++ missiles) where
    (shots,penalty1) = if doesNormal then ([freshStandardRailgun{position=position obj,parentTag=myTag}] ,2)
      else if doesGLdouble then ([freshStandardRailgun{position=position obj,parentTag=myTag},freshStandardRailgun{position=position obj,parentTag=myTag,velocity=mkPolar 1 (pi/4)*velocity freshStandardRailgun}] ,2)
      else if doesLaser then ([freshStandardLaser{position=position obj+(shotSpeed/2:+0),parentTag=myTag}] ,1)
      else ([],0)
    penalty2 = if weaponEnergy obj <= 0 then 8 else 0
    missiles = if doesMissile then [freshStandardMissile{position=position obj}] else []
    doesShot = (isJust $tag obj) && (reloadTime obj <=0) && (shotButton `elem` keys)
    doesNormal = doesShot && elem NormalShot types && (shotCount<2)
    doesGLdouble = doesShot && elem GLdoubleShot types && (shotCount<1)
    doesLaser = doesShot && elem Laser types
    doesMissile = (isJust $tag obj) && elem Missile types && (missileButton `elem` keys) && (missileCount<=0)
    myTag = fromJust $ tag obj
    shotCount = length $ filter (\o -> case o of
                            StandardRailgun{} -> parentTag o==myTag
                            _             -> False) gameObjects
    missileCount = length $ filter (\o -> case o of
                                           StandardMissile{} -> True
                                           _                 -> False) gameObjects

    types = weaponTypes vicViper

  jikiNeraiDan :: Complex GLdouble -> Complex GLdouble -> GameObject
  -- an enemy bullet starting at position sourcePos and with relative velocity initVelocity.
  -- bullet goes straight to vicviper if initVelocity is a positive real number.
  jikiNeraiDanAc sourcePos initVelocity accuracy = freshDiamondBomb{
    position = sourcePos,
    velocity = (*initVelocity) $ (angleAccuracy accuracy) $ unitVector $ position vicViper - sourcePos
  }
  jikiNeraiDan sourcePos initVelocity = jikiNeraiDanAc sourcePos initVelocity 32

  scatteredNeraiDan :: Complex GLdouble -> Complex GLdouble -> GameObject
  -- a rather scattered jikiNeraiDan.
  scatteredNeraiDan sourcePos initVelocity = freshDiamondBomb{
    position = sourcePos,
    velocity = scatter $ (*initVelocity) $ (angleAccuracy 32) $ unitVector $ position vicViper - sourcePos
  } where
    scatter z = let (r,theta)=polar z in
      mkPolar r (theta+pi/8*((^(3::Int)).sin)((intToGLdouble $ gameClock variables) + magnitude sourcePos))

  freshExplosionParticle pos vel a = Particle{tag=Nothing,position=pos,velocity=vel,size=8,particleColor=Color3 1 0.5 0,age=a,decayTime=6,expireAge=20}

  freshExplosions pos = take 5 expls where
    expls :: [GameObject]
    expls = makeExp randoms
    randoms = [square $ sin(9801*sqrt t*(intToGLdouble$gameClock variables) + magnitude pos)|t<-[1..]]
    makeExp (a:b:c:xs) = (freshExplosionParticle pos (mkPolar (3*a) (2*pi*b)) (round $ -5*c)):makeExp xs
    makeExp _ = []

  freshMiddleExplosions pos = take 16 expls where
    expls :: [GameObject]
    expls = makeExp randoms 0
    randoms = [square $ sin(8086*sqrt t*(intToGLdouble$gameClock variables) + magnitude pos)|t<-[1..]]
    makeExp (a:b:xs) i = (freshExplosionParticle (pos+mkPolar 5 (pi/8*i)) (mkPolar (6+3*a) (pi/8*i)) (round $ -5*b)){size=16}:makeExp xs (i+1)
    makeExp _ _ = []

  -- issue tag so that each objcet has unique tag,
  -- and every object will continue to hold the same tag.
  issueTag :: Int -> [GameObject] -> (Int,[GameObject])
  issueTag nt [] = (nt,[])
  issueTag nt (x:xs) = (newNextTag',taggedX:taggedXs)
      where
        (nextTagForXs,taggedX) = if(isNothing $ tag x) then (nt+1,x{tag = Just nt}) else (nt,x)
        (newNextTag',taggedXs)  = issueTag nextTagForXs xs

  collide :: [GameObject] -> [GameObject]
  -- collide a list of GameObjects and return the result.
  -- it is important NOT to delete any object at the collision -- collide, show then delete
  collide = map personalCollide
    where
    -- each object has its own hitClasses and weakPoints.
    -- collision is not symmetric: A may crushed by B while B doesn't feel A.
    -- object X is hit by only objectsWhoseHitClassIsMyWeakPoint X.
    personalCollide :: GameObject -> GameObject
    personalCollide obj = foldr check obj $ objectsWhoseHitClassIsMyWeakPoint obj

    objectsWhoseHitClassIsMyWeakPoint :: GameObject -> [GameObject]
    objectsWhoseHitClassIsMyWeakPoint me =
      filter (\him -> not $ null $ (weakPoint me) `intersect` (hitClass him)) gameObjects

    hitClass :: GameObject -> [HitClass]
    hitClass VicViper{} = [MetalionBody,ItemReceiver]
    hitClass StandardMissile{} = [MetalionShot]
    hitClass StandardRailgun{} = [MetalionShot]
    hitClass StandardLaser{} = [MetalionShot]
    hitClass Shield{} = [MetalionBody]
    hitClass PowerUpCapsule{} = [PowerUp]

    hitClass DiamondBomb{} = [BacterianShot]
    hitClass TurnGear{} = [BacterianBody]
    hitClass Flyer{} = [BacterianBody]
    hitClass Ducker{} = [BacterianBody]
    hitClass Jumper{} = [BacterianBody]
    hitClass Grashia{} = [BacterianBody]
    hitClass ScrambleHatch{} = [BacterianBody,LaserAbsorber]

    hitClass LandScapeBlock{} = [LandScape]
    hitClass _ = []

    weakPoint :: GameObject -> [HitClass]
    weakPoint VicViper{} = [PowerUp,BacterianBody,BacterianShot,LandScape]
    weakPoint StandardMissile{} = [BacterianBody,LandScape]
    weakPoint Probe{} = [LandScape]
    weakPoint StandardRailgun{} = [BacterianBody,LandScape]
    weakPoint StandardLaser{} = [LaserAbsorber,LandScape]
    weakPoint Shield{} = [BacterianBody,BacterianShot,LandScape]
    weakPoint PowerUpCapsule{} = [ItemReceiver]

    weakPoint DiamondBomb{} = [MetalionBody,LandScape]
    weakPoint TurnGear{} = [MetalionBody,MetalionShot]
    weakPoint Flyer{} = [MetalionBody,MetalionShot]
    weakPoint Ducker{} = [MetalionBody,MetalionShot,LandScape]
    weakPoint Jumper{} = [MetalionBody,MetalionShot,LandScape]
    weakPoint Grashia{} = [MetalionBody,MetalionShot]
    weakPoint ScrambleHatch{} = [MetalionBody,MetalionShot]

    weakPoint _ = []

    -- after matching hitClass-weakPoint, you must check the shape of the pair of object
    -- to see if source really hits the target.
    check :: GameObject -> GameObject -> GameObject
    check source target = case (source, target) of
      (LandScapeBlock{},StandardMissile{}) -> if(hit source target) then (affect source target2) else target2 where
        target2 = target{probe = if(hit source p) then (affect source p) else p}
        p = probe target
      _                   -> if(hit source target) then (affect source target) else target

    -- if a is really hitting b, a affects b (usually, decreases hitpoint of b).
    -- note that landScapeSensitive objects have special hitDispLand other than hitDisp.
    -- this allows some weapons to go through narrow land features, and yet
    -- wipe out wider area of enemies.
    hit :: GameObject -> GameObject -> Bool
    hit a b = case (a,b) of
      (LandScapeBlock{},c) -> if landScapeSensitive c then (position a +> hitDisp a) >?< (position c +> hitDispLand c)
         else (position a +> hitDisp a) >?< (position b +> hitDisp b)
      _ -> (position a +> hitDisp a) >?< (position b +> hitDisp b)

    affect :: GameObject -> GameObject -> GameObject
    affect VicViper{} obj = case obj of
      pow@PowerUpCapsule{} -> pow{hp = hp pow-1}
      x                    -> x
    affect PowerUpCapsule{} obj = case obj of
      viper@VicViper{} -> viper{powerUpPointer = (\x -> if x >=5 then 0 else x+1)$powerUpPointer viper}
      _                -> error "Power capsule should not have been able to affect anything but the player craft."
    affect StandardMissile{} obj = obj{hp = hp obj-(hatchHP`div`2 + 2)} -- 2 missiles can destroy a hatch
    affect StandardRailgun{} obj = obj{hp = hp obj-(hatchHP`div`4 + 1)} -- 4 shots can also destroy a hatch
    affect StandardLaser{} obj = obj{hp = hp obj-1}
    affect Shield{} obj = obj{hp = hp obj-1}


    affect DiamondBomb{} obj = obj{hp = hp obj-1}
    affect TurnGear{} obj = obj{hp = hp obj-1}
    affect Flyer{} obj = obj{hp = hp obj-1}
    affect Ducker{} obj= obj{hp = hp obj-1}
    affect Jumper{} obj= obj{hp = hp obj-1}
    affect Grashia{} obj= obj{hp = hp obj-1}
    affect ScrambleHatch{} obj= obj{hp = hp obj-1}

    affect LandScapeBlock{} obj = case obj of
--      miso@StandardMissile{velocity=v} -> miso{velocity = (1:+0)*abs v}
      duck@Ducker{} -> duck{touchedLand=True}
      that@Jumper{} -> that{touchedLand=True}
      _ -> obj{hp = hp obj-1}

    affect _ t = t

  scroll :: GameObject -> Maybe GameObject
  -- make an object scroll.
  -- if the object is to vanish out of the screen, it becomes Nothing.
  scroll obj = let
      (x:+y) = position obj
      scrollBehavior :: GameObject -> ScrollBehavior
      scrollBehavior VicViper{} = Enclosed False
      scrollBehavior Option {}  = NoRollOut False
      -- We use the more verbose way of setting records here to guarantee
      -- 'range' is needed so -Wall doesn't get fooled.
      scrollBehavior StandardRailgun{} = RollOutAuto {doesScroll = True, range = shotSpeed}
      scrollBehavior StandardLaser{} = RollOutAuto True laserSpeed
      scrollBehavior PowerUpGauge{} = NoRollOut False
      scrollBehavior PowerUpCapsule{} = RollOutAuto True 40
      scrollBehavior Shield{} = NoRollOut False

      scrollBehavior DiamondBomb{} = RollOutAuto False 10
      scrollBehavior TurnGear{} = RollOutAuto False 20
      scrollBehavior SquadManager{} = NoRollOut False
      scrollBehavior ScrambleHatch{} = RollOutAuto True 60

      scrollBehavior LandScapeBlock{} = RollOutAuto True 160

      scrollBehavior Star{} = RollOutFold True

      scrollBehavior DebugMessage{} = NoRollOut False
      scrollBehavior ScoreFragment{} = NoRollOut False
      scrollBehavior SabbathicAgent{} = NoRollOut False

      scrollBehavior _          = RollOutAuto True 40

      scrollSpeed = if hp vicViper <= 0 then 0 else if gameClock variables <=6400 then 1 else 2
      rolledObj = if doesScroll $ scrollBehavior obj then obj{position=(x-scrollSpeed):+y} else obj
    in case scrollBehavior obj of
      Enclosed _ -> Just rolledObj{position = (max (-300) $ min 280 x):+(max (-230) $ min 230 y)}
      NoRollOut _ -> Just rolledObj
      RollOutAuto _ r -> if any (>r) [x-320,(-320)-x,y-240,(-240)-y] then Nothing
                        else Just rolledObj
      RollOutFold _ -> Just rolledObj{position = (if x< -320 then x+640 else x):+y} where
        (_:+_) = position rolledObj

  loadObjects :: [GameObject]
  -- a list of objects that are to newly loaded at this frame.

  loadObjects = if hp vicViper<=0 then [] else (case clock of
      -- stage layout.
      -- just like old BASIC code.
      150 -> [freshTurnGearSquad{position=340:+(180)}]
      300 -> [freshTurnGearSquad{position=340:+(-180)}]
      400 -> [freshTurnGearSquad{position=340:+(180)}]
      500 -> [freshTurnGearSquad{position=340:+(-180)}]
      633 -> map (\y -> freshStalk{position = 340:+y,hasItem=False }) [-120,120] ++ [freshStalk{position = 340:+0,hasItem=isEasy}]
      666 -> map (\y -> freshStalk{position = 340:+y,hasItem=isEasy}) [-130,130] ++ [freshStalk{position = 340:+0,hasItem=False}]
      700 -> map (\y -> freshStalk{position = 340:+y,hasItem=False }) [-140,140] ++ [freshStalk{position = 340:+0,hasItem=True}]
      733 -> map (\y -> freshStalk{position = 340:+y,hasItem=isEasy}) [-150,150] ++ [freshStalk{position = 340:+0,hasItem=False}]
      900 -> [freshTurnGearSquad{position=340:+(-180)}]
      1000 -> [freshTurnGearSquad{position=340:+(180)}]
      1050 -> map (\y -> freshStalk{position = 340:+y}) [-135,0,135]
      1250 -> map (\y -> freshFlyer{position = 340:+y}) [-150,-100]
      1300 -> map (\y -> freshFlyer{position = 340:+y,hasItem=True}) [100,150]
      1100 -> [freshTurnGearSquad{position=340:+(-180)},freshTurnGearSquad{position=340:+(180)}]
      1400 -> [(freshGrashia (-1)){position = 340:+(-185)},(freshGrashia 1){position = 340:+(185)}]
      1450 -> [(freshGrashia (-1)){position = 340:+(-185),hasItem=True},(freshGrashia 1){position = 340:+(185)}]
      1550 -> [(freshScrambleHatch (-1)){position = 360:+(-200)},(freshScrambleHatch (1)){position = 360:+(200)}]
      1700 -> [(freshVolcano (-1)){position=479:+(-200)}]
      1900 -> map (\(g,x) ->  (freshDucker g){position=x:+g*100}) $ [(1,340),(-1,340)] ++ if not isEasy then [(1,-340),(-1,-340)] else []
      1940 -> [(freshLandRoll (1)){position = 340:+(185)}]
      1965 -> [(freshLandRoll (1)){position = 340:+(185)}]
      1990 -> [(freshLandRoll (1)){position = 340:+(185)}]
      2000 -> [(freshGrashia (-1)){position = 340:+(-185)},(freshDucker (-1)){position=(-340):+(-185)}]
      2033 -> [(freshGrashia (-1)){position = 340:+(-185)},(freshDucker 1){position=(-340):+(185)}]
      2100 -> [(freshScrambleHatch (-1)){position = 360:+(-200)}]
      2200 -> [(freshVolcano 1){position=479:+(200)}]
      2250 -> map (\y -> freshStalk{position = 340:+y}) [-150,0,150]
      2339 -> [(freshGrashia (1)){position = 340:+35},(freshGrashia (-1)){position = 340:+(-185)}]

      2433 -> map (\y -> freshFlyer{position = 340:+y}) [-150,0]
      2466 -> map (\y -> freshFlyer{position = 340:+y}) [-150,0]
      2499 -> map (\y -> freshFlyer{position = 340:+y}) [-150,0]

      2620 -> [(freshDucker 1){position=(-340):+(200)}]
      2640 -> [(freshDucker 1){position=(-340):+(200),hasItem=True}]
      2800 -> map (\(g,x) ->  (freshJumper g){position=x:+g*100,velocity=((-3)*signum x):+0}) $ [(1,340),(-1,340)] ++ if not isEasy then [(1,-340),(-1,-340)] else []
      2999 -> [(freshVolcano 2){position=479:+(20),velocity=(0:+(-0.5))},(freshVolcano (-2)){position=479:+(-20),velocity=(0:+(0.5))}]

      3200 -> concat $ map (\x -> [freshLandScapeGround{position=(479-x):+220},freshLandScapeGround{position=(479-x):+(-220)}]) [320,640]

      3210 -> [freshFlyer{position = 340:+150},freshFlyer{position = 340:+100,hasItem=True}]
      3290 -> [freshFlyer{position = 340:+(-150)},freshFlyer{position = 340:+(-100),hasItem=True}]
      3350 -> map (\g -> (freshLandRoll (g)){position = 340:+(g*185)}) [1,-1] ++ if isRevival then [] else [(freshJumper (1)){position = (-340):+150,velocity=3:+0}]
      3400 -> map (\g -> (freshLandRoll (g)){position = 340:+(g*185)}) [1,-1] ++ if isRevival then [] else [(freshJumper (-1)){position = (-340):+(-150),velocity=3:+0}]
      3450 -> map (\g -> (freshLandRoll (g)){position = 340:+(g*185)}) [1,-1]
      3500 -> [(freshVolcano (-1)){position=479:+(-200)}]
      3579 -> [(freshGrashia (-1)){position = 340:+(-100)}]
      3639 -> [(freshGrashia (-1)){position = 340:+(-40)}]
      3699 -> [(freshGrashia (-1)){position = 340:+(-100)}]
      3501 -> [(freshScrambleHatch (1)){position = 360:+(200)}]
      3600 -> [(freshScrambleHatch (1)){position = 360:+(200)}]
      3582 -> [(freshDucker (-1)){position=(340):+(-200)}]
      3612 -> [(freshDucker (-1)){position=(340):+(-200)}]
      3642 -> [(freshDucker (-1)){position=(340):+(-200)}]
      3672 -> [(freshDucker (-1)){position=(340):+(-200)}]
      3702 -> [(freshDucker (-1)){position=(340):+(-200),hasItem=True}]
      3703 -> [(freshLandRoll (1)){position=(340):+(185),hasItem=True}]
      3820 -> map (\y -> freshFlyer{position = 340:+y}) [-100,100]
      3840 -> map (\y -> freshFlyer{position = 340:+y}) [-110,110]
      3860 -> map (\y -> freshFlyer{position = 340:+y}) [-120,120]
      3880 -> map (\y -> freshFlyer{position = 340:+y,hasItem = isEasy}) [-130,130]
      3900 -> [freshTurnGearSquad{position=340:+0}]
      4000 -> [(freshTable 1){position=450:+200}]
      4033 -> [(freshGrashia (1)){position = 340:+(185)}]
      4066 -> [(freshGrashia (1)){position = 340:+(185)}]
      4060 -> [(freshGrashia (1)){position = 340:+(40)}]
      4110 -> [(freshGrashia (1)){position = 340:+(40)}]
      4160 -> [(freshGrashia (1)){position = 340:+(40)}]
      4166 -> [(freshGrashia (1)){position = 340:+(185),hasItem=True}]
      4200 -> [(freshGrashia (1)){position = 340:+(185),hasItem=True}]
      4233 -> [(freshGrashia (1)){position = 340:+(185),hasItem=False}]
      4266 -> [(freshGrashia (1)){position = 340:+(185),hasItem=False}]
      4150 -> [freshLandScapeGround{position=479:+(-180)}]
      4203 -> [(freshJumper (-1)){position = 340:+(-180)}]
      4273 -> [(freshJumper (-1)){position = 340:+(-180)}]
      4343 -> [(freshJumper (-1)){position = 340:+(-180)}]
      4490 -> [(freshTable (-1)){position=450:+(-200)}]
      4500 -> [(freshDucker (-1)){position=340:+0}]
      4520 -> [(freshDucker (-1)){position=340:+0}]
      4540 -> [(freshDucker (-1)){position=340:+0}]
      4560 -> [(freshGrashia (-1)){position = 340:+(-185)}]
      4580 -> [(freshScrambleHatch (-1)){position = 360:+(-50)}]
      4603 -> if isRevival then [] else [(freshDucker 1){position = (-340):+0},(freshJumper (1)){position = (-340):+150,velocity=3:+0}]
      4663 -> [(freshDucker 1){position = (-340):+0}]++if isEasy then [] else [(freshJumper (1)){position = (-340):+150,velocity=3:+0}]
      4723 -> if isRevival then [] else [(freshDucker 1){position = (-340):+0},(freshJumper (1)){position = (-340):+150,velocity=3:+0}]
      4783 -> [(freshDucker 1){position = (-340):+0}]++if isEasy then [] else [(freshJumper (1)){position = (-340):+150,velocity=3:+0}]
      4680 -> [(freshScrambleHatch (-1)){position = 360:+(-200)}]
      4900 -> map (\y -> freshFlyer{position = 340:+y}) [-100,100]
      4930 -> map (\y -> freshFlyer{position = 340:+y}) [-66,66]
      4960 -> map (\y -> freshFlyer{position = 340:+y}) [-33,33]
      4990 -> map (\y -> freshFlyer{position = 340:+y,hasItem=True}) [0]
      5041 -> [(freshDucker (-1)){position = 340:+(-180)}]
      5061 -> [(freshDucker (-1)){position = 340:+(-180)}]
      5081 -> [(freshDucker (-1)){position = 340:+(-180)}]
      5101 -> if isRevival then [] else ([(freshDucker (-1)){position = 340:+(-180)}] ++ if isEasy then [] else [(freshDucker (1)){position = (-340):+(180)}])
      5121 -> if isRevival then [] else ([(freshDucker (-1)){position = 340:+(-180)}] ++ if isEasy then [] else [(freshDucker (1)){position = (-340):+(180)}])
      5141 -> if isRevival then [] else ([(freshDucker (-1)){position = 340:+(-180)}] ++ if isEasy then [] else [(freshDucker (1)){position = (-340):+(180)}])
      5261 -> [freshTurnGearSquad{position=340:+(-150)}]
      5364 -> [(freshScrambleHatch (-1)){position = 360:+(-200)}]
      5151 -> [(freshDucker (-1)){position = (-340):+(0)}]
      5181 -> [(freshDucker (-1)){position = (-340):+(0)}]
      5211 -> [(freshDucker (-1)){position = (-340):+(0)}]
      5241 -> [(freshDucker (-1)){position = (-340):+(0)}]
      5321 -> [(freshDucker (-1)){position = 340:+150}] ++ if isEasy then [] else [(freshJumper (1)){position = (-340):+(180),velocity = 3:+0}]
      5361 -> [(freshDucker (-1)){position = 340:+150}]
      5401 -> [(freshDucker (-1)){position = 340:+150}]
      5441 -> [(freshDucker (-1)){position = 340:+150}] ++ if isEasy then [] else map (\y -> freshStalk{position = 340:+y}) [-140,-70,0]
      5461 -> [(freshDucker (-1)){position = 340:+150}]
      5451 -> [(freshGrashia (-1)){position = 340:+160,hasItem=True}]

      5060 -> [(freshVolcano (-1)){position = 480:+(-100)}]
      5200 -> [(freshGrashia (-1)){position = 340:+70}] ++ [(freshDucker (-1)){position = 340:+70}]
      5235 -> [(freshGrashia (-1)){position = 340:+40}] ++ [(freshDucker (-1)){position = 340:+40}]
      5258 -> [(freshGrashia (-1)){position = 340:+10}] ++ [(freshDucker (-1)){position = 340:+10}]
      5285 -> [(freshGrashia (-1)){position = 340:+(-20)}] ++ [(freshDucker (-1)){position = 340:+(-20)}]
      5316 -> [(freshGrashia (-1)){position = 340:+(-50)}] ++ [(freshDucker (-1)){position = 340:+(-50)}]

      5310 -> [(freshVolcano (1)){position = 480:+(150)}]
      5450 -> [(freshGrashia (1)){position = 340:+(-20)}]
      5485 -> [(freshGrashia (1)){position = 340:+10}]
      5508 -> [(freshGrashia (1)){position = 340:+40}]
      5535 -> [(freshGrashia (1)){position = 340:+70}]
      5566 -> [(freshGrashia (1)){position = 340:+100}]

      5811 -> [(freshDucker (-1)){position = (-340):+0}]
      5841 -> [(freshDucker (-1)){position = (-340):+0}]
      5871 -> [(freshDucker (-1)){position = (-340):+0}]
      5901 -> [(freshDucker (-1)){position = (-340):+0}]
      6001 -> if isEasy then [(freshDucker (-1)){position = (-340):+150}] else []
      6031 -> if isEasy then [(freshDucker (-1)){position = (-340):+150}] else []
      6061 -> if isEasy then [(freshDucker (-1)){position = (-340):+150}] else []
      6091 -> if isEasy then [(freshDucker (-1)){position = (-340):+150}] else []

      5800 -> [(freshScrambleHatch (-1)){position = 360:+(-200)},(freshScrambleHatch (1)){position = 360:+(200)}]
      5950 -> [(freshScrambleHatch (-1)){position = 360:+(-200)},(freshScrambleHatch (1)){position = 360:+(200)}]
      6100 -> [(freshScrambleHatch (-1)){position = 360:+(-200)},(freshScrambleHatch (1)){position = 360:+(200)}]
      6116 -> [freshSabbathicAgent]

      _ -> []) ++
    (if(optionCount < powerUpLevels vicViper!4) then
      [freshOption{position=position vicViper, optionTag = optionCount+1}]
      else []) ++
    (if (clock `mod` 320 == 0 && clock>=1280 && clock <= 6400) then
      [freshLandScapeGround{position=479:+220},freshLandScapeGround{position=479:+(-220)}]
      else [])

    where
      clock = gameClock variables
      optionCount = length $ filter (\o -> case o of
        Option{} -> True
        _  -> False) gameObjects
      isRevival = optionCount <= 0
  isEasy = gameLevel <= 1

  vicViper = fromJust $ find (\obj -> case obj of
                            VicViper{} -> True
                            _          -> False) objects

-- things needed both for progress and rendering
powerUpLimits :: [Int]
powerUpLimits = [5,1,1,1,4,1]
weaponTypes :: GameObject -> [WeaponType]
weaponTypes viper@VicViper{} =
  [if powerUpLevels viper!gaugeOfGLdouble>0 then GLdoubleShot else
   if powerUpLevels viper!gaugeOfLaser>0 then Laser else
   NormalShot] ++
   if powerUpLevels viper!gaugeOfMissile>0 then [Missile] else []

weaponTypes _ = []

-------------------------
--
--  drawing
--
-------------------------
renderMonadius :: Monadius -> IO ()
renderMonadius (Monadius (variables,objects)) = do
  putDebugStrLn $ show $ length objects
  mapM_ renderGameObject objects
  preservingMatrix $ do
    translate (Vector3 (-300) (220) (0 :: GLdouble))
    renderWithShade (Color3 1 1 (1 :: GLdouble)) (Color3 0 0 (1 :: GLdouble)) $ do
      scale (0.2 :: GLdouble) 0.2 0.2
      renderString MonoRoman scoreStr
  preservingMatrix $ do
    translate (Vector3 (0) (220) (0 :: GLdouble))
    renderWithShade (Color3 1 1 (1 :: GLdouble)) (Color3 0 0 (1 :: GLdouble)) $ do
      scale (0.2 :: GLdouble) 0.2 0.2
      renderString MonoRoman scoreStr2
  where
  scoreStr = "1P "  ++ ((padding '0' 8).show.totalScore) variables
  scoreStr2 = if isNothing $ playTitle variables then "HI "++((padding '0' 8).show.hiScore) variables else (fromJust $ playTitle variables)

  gameclock = gameClock variables

  -- returns an IO monad that can render the object.
  renderGameObject :: GameObject -> IO ()
  renderGameObject gauge@PowerUpGauge{} = preservingMatrix $ do
    let x:+y = position gauge
    translate (Vector3 x y 0)
    color (Color3 (1.0 :: GLdouble) 1.0 1.0)
    mapM_ (\(i,j) -> (if(i==activeGauge)then renderActive else renderNormal) j (isLimit i) i) $
      zip [0..5] [0,90..450] where
      w=80
      h=20
      renderNormal x l i = preservingMatrix $ do
        color (Color3 0.7 0.8 (0.8 :: GLdouble))
        preservingMatrix $ do
          translate (Vector3 x 0 (0 :: GLdouble))
          renderPrimitive LineLoop $ ugoVertices2D 0 1 [(0,0),(w,0),(w,h),(0,h)]
          if l then renderPrimitive Lines $ ugoVertices2D 0 1 [(0,0),(w,h),(w,0),(0,h)] else return()
        preservingMatrix $ do
          ugoTranslate x 0 0 3
          translate (Vector3 (w/2) 0 (0 :: GLdouble))
          rotate (3 * sin(intToGLdouble gameclock/10)) (Vector3 0 0 (1 :: GLdouble))
          translate (Vector3 (-w/2) 0 (0 :: GLdouble))
          renderPowerUpName i

      renderActive x l i = preservingMatrix $ do
        color (Color3 1 1 (0 :: GLdouble))
        preservingMatrix $ do
          translate (Vector3 x 0 0)
          renderPrimitive LineLoop $ ugoVertices2DFreq 0 5 2 [(0,0),(w,0),(w,h),(0,h)]
          if l then renderPrimitive Lines $ ugoVertices2DFreq 0 5 2 [(0,0),(w,h),(w,0),(0,h)] else return()
        preservingMatrix $ do
          ugoTranslateFreq x 0 0 5 2
          translate (Vector3 (w/2) 0 (0 :: GLdouble))
          rotate (10 * sin(intToGLdouble gameclock/5)) (Vector3 0 0 (1 :: GLdouble))
          scale 1.2 1.2 (0 :: GLdouble)
          translate (Vector3 (-w/2) 0 (0 :: GLdouble))
          renderPowerUpName i
      activeGauge = powerUpPointer vicViper
      isLimit i = powerUpLevels vicViper!i>=powerUpLimits!!i
      renderPowerUpName i = do
        translate (Vector3 6 3.5 (0 :: GLdouble))
        scale (0.15 :: GLdouble) 0.13 0.15
        renderString Roman $ ["SPEED","MISSILE","DOUBLE","LASER","OPTION","  ?"]!!i

  renderGameObject vic@VicViper{position = x:+y} = if hp vic<=0 then preservingMatrix $ do
      translate (Vector3 x y 0)
      scale pishaMagnitudeX pishaMagnitudeY 0
      renderWithShade (Color3 (1.0 :: GLdouble) 0 0) (Color3 (1.0 :: GLdouble) 0.6 0.4) $ do
        renderPrimitive LineLoop $ ugoVertices2DFreq 0 1 1
          [(0,12),(8,8),(10,4),(20,0),(10,-4),(8,-8),(0,-12),(-8,-8),(-10,-4),(-20,0),(-10,4),(-8,8)]
    else preservingMatrix $ do
      translate (Vector3 x y 0)
      renderWithShade (Color3 (1.0 :: GLdouble) 1.0 1.0) (Color3 (0.4 :: GLdouble) 0.4 0.6) $ do
        renderPrimitive LineStrip $ ugoVertices2D 0 2
          [((-14),(-1)),((-12),5),((-20),13),(-14,13),(2,5),(8,1),(32,1),(32,(-1)),(24,(-3)),(16,(-3))]
        renderPrimitive LineStrip $ ugoVertices2D 0 2
          [((-10),(-1)),(14,(-1)),(18,(-5)),(4,(-9)),((-2),(-9))]
        renderPrimitive LineLoop $ ugoVertices2D 0 2
          [((-18),3),((-16),3),((-16),(-3)),((-18),(-3))]
      renderWithShade (Color3 (0.92 :: GLdouble) 0.79 0.62) (Color3 (0.75 :: GLdouble) 0.38 0.19) $ do
        renderPrimitive LineStrip $ ugoVertices2D 0 2
          [(4,3),(6,5),(14,5),(22,1)] --cockpit
      renderWithShade (Color3 (0.6 :: GLdouble) 0.8 1.0) (Color3 0.19 0.38 (0.75 :: GLdouble)) $ do
        renderPrimitive LineLoop $ ugoVertices2D 0 2
          [((-14),(-1)),((-10),(-1)),((-2),(-9)),((-4),(-9)),((-10),(-7)),((-14),(-3))] -- identification blue coting
      renderWithShade (Color3 (0 :: GLdouble) 0 0.8) (Color3 (0.0 :: GLdouble) 0.0 0.4) $ do
        renderPrimitive LineLoop $ ugoVertices2D 0 4
          [((-36),1),((-28),5),((-24),5),((-20),1),((-20),(-1)),((-24),(-5)),((-28),(-5)),((-36),(-1))] -- backfire
    where
      pishaMagnitudeX :: GLdouble
      pishaMagnitudeY :: GLdouble
      pishaMagnitudeX = max 0 $ (8*) $ (\z -> z*(1-z)) $ (/20) $ intToGLdouble $ ageAfterDeath vic
      pishaMagnitudeY = max 0 $ (5*) $ (\z -> z*(1-z)) $ (/15) $ intToGLdouble $ ageAfterDeath vic

  renderGameObject Option{position = x:+y} = preservingMatrix $ do
    translate (Vector3 x y 0)
    renderWithShade (Color3 (0.8 :: GLdouble) 0 0) (Color3 (0.4 :: GLdouble) 0 0) $
      renderPrimitive LineLoop $ ugoVertices2D 0 2
        [(5,9),(9,7),(13,3),(13,(-3)),(9,(-7)),(5,(-9)),
         ((-5),(-9)),((-9),(-7)),((-13),(-3)),((-13),3),((-9),7),((-5),9)]
    renderWithShade (Color3 (1.0 :: GLdouble) 0.45 0) (Color3 (0.4 :: GLdouble) 0.2 0) $
      renderPrimitive LineStrip $ ugoVertices2D 0 1
        [((-12.0),(3.4)),(0.8,8.7),((-8.1),(-0.9)),(4.0,5.8),(4.3,5.6),
          ((-4.4),(-6.8)),((-4.1),(-6.9)),(8.3,0.8),(9.0,0.6),(2.0,(-7.2))]

  renderGameObject StandardMissile{position=x:+y,velocity=v} = preservingMatrix $ do
    let dir = (phase v) :: GLdouble
    translate (Vector3 x y 0)
    rotate (dir / pi * 180) (Vector3 0 0 (1 :: GLdouble))
    color (Color3 (1.0 :: GLdouble) 0.9 0.5)
    renderPrimitive LineLoop $ ugoVertices2D 0 1 [(0,0),(-7,2),(-7,-2)]
    renderPrimitive LineStrip $ ugoVertexFreq (-11) 0 0 1 1 >> ugoVertexFreq (-17) 0 0 7 1

  renderGameObject StandardRailgun{position=x:+y,velocity=v} =
    preservingMatrix $ do
      let (_,phse)=polar v
      translate (Vector3 x y 0)
      rotate (phse / pi * 180) (Vector3 0 0 (1 :: GLdouble))
      color (Color3 (1.0 :: GLdouble) 0.9 0.5)
      renderPrimitive Lines $ ugoVertices2D 0 1 [(0,0),((-5),0),((-9),0),((-11),0)]

  renderGameObject laser@StandardLaser{position=x:+y,velocity=v} =
    if age laser < 1 then return ()
    else preservingMatrix $ do
      let (_,phs)=polar v
      translate (Vector3 x y 0)
      rotate (phs / pi * 180) (Vector3 0 0 (1 :: GLdouble))
      color (Color3 (0.7 :: GLdouble) 0.9 1.0)
      renderPrimitive Lines $ ugoVertices2D 0 0 [(12,0),(-laserSpeed,0)]

  renderGameObject Shield{position=x:+y, size = r,angle = theta} = preservingMatrix $ do
    translate (Vector3 x y 0)
    rotate theta (Vector3 0 0 (1 :: GLdouble))
    renderWithShade (Color3 (0.375 :: GLdouble) 0.75 0.9375) (Color3 (0.86 :: GLdouble) 0.86 0.86) $ do
      scale r r 0
      renderTriangle
      rotate 60 (Vector3 0 0 (1 :: GLdouble))
      renderTriangle where
        renderTriangle = do
          renderPrimitive LineLoop $ ugoVertices2DFreq 0 0.1 1 $ map (\t -> (cos t,sin t)) [0,pi*2/3,pi*4/3]

  renderGameObject powerUpCapsule@PowerUpCapsule{} = preservingMatrix $ do
    let x:+y = position powerUpCapsule
    translate (Vector3 x y 0)
    renderWithShade (Color3 (0.9 :: GLdouble) 0.9 0.9) (Color3 (0.4 :: GLdouble) 0.4 0.4) $ do
      futa >> neji >> toge
      rotate (180) (Vector3 1 0 (0 :: GLdouble)) >> toge
      rotate (180) (Vector3 0 1 (0 :: GLdouble)) >> futa >> neji >> toge
      rotate (180) (Vector3 1 0 (0 :: GLdouble)) >> toge
    renderWithShade (Color3 (1.0 :: GLdouble) 0.0 0.0) (Color3 (0.3 :: GLdouble) 0.3 0.0) $ do
      nakami
      where
        futa = renderPrimitive LineStrip $ ugoVertices2D 0 1 [((-10),6),((-6),10),(6,10),(10,6)]
        neji = (renderPrimitive LineStrip $ ugoVertices2D 0 1 [(12,4),(12,(-4))]) >>
               (renderPrimitive LineStrip $ ugoVertices2D 0 1 [(16,2),(16,(-2))])
        toge = renderPrimitive LineStrip $ ugoVertices2D 0 1 [(10,8),(16,14)]
        nakami = rotate 145 (Vector3 0.2 0.2 (1 :: GLdouble)) >> scale 9 6 (1 :: GLdouble) >>
          (renderPrimitive LineStrip $ ugoVertices2D 0 0.2 $ map (\n ->  (cos$n*pi/8,sin$n*pi/8)) [1,15,3,13,5,11,7,9])

  renderGameObject DiamondBomb{position = (x:+y),age=clock} = preservingMatrix $ do
    translate (Vector3 x y 0)
    rotate (90*intToGLdouble(clock`mod`4)) (Vector3 0 0 (1 :: GLdouble))
    color (Color3 (1 :: GLdouble) 1 1)
    renderPrimitive LineLoop $ vertices2D 0 $ [a,b,c]
    color (Color3 (0.5 :: GLdouble) 0.5 0.5)
    renderPrimitive Lines $ vertices2D 0 $ [a,d,a,e]
    renderPrimitive LineStrip $ vertices2D 0 $ [c,d,e,b]
    where
      [a,b,c,d,e] = [(0,0),(r,0),(0,r),(-r,0),(0,-r)]
      r = diamondBombSize
      --    c
      --   /|\
      --  d-a-b
      --   \|/
      --    e
  renderGameObject TurnGear{position=x:+y,age=clock} = preservingMatrix $ do
    translate (Vector3 x y 0)
    color (Color3 1.0 0.7 1.0 :: Color3 GLdouble)
    rotate (5 * intToGLdouble clock) (Vector3 0 0 1 :: Vector3 GLdouble)
    renderWing
    rotate 120 (Vector3 0 0 1 :: Vector3 GLdouble)
    renderWing
    rotate 120 (Vector3 0 0 1 :: Vector3 GLdouble)
    renderWing
    where
      renderWing = renderPrimitive LineLoop $ ugoVertices2D 0 2 $ map ((\(t:+u) -> (t,u)) . (\(r,t) -> mkPolar r (pi*t)) )
        [(3,0), (3,2/3), (smallBacterianSize,1/3), (smallBacterianSize,0), (smallBacterianSize+3,-1/3)]

  renderGameObject Flyer{position=x:+y,age=_,velocity = v,hasItem=item}  = preservingMatrix $ do
    translate (Vector3 x y 0)
    color (if item then (Color3 1.0 0.2 0.2 :: Color3 GLdouble) else (Color3 0.3 1.0 0.7 :: Color3 GLdouble))
    rotate (phase v / pi * 180) (Vector3 0 0 (1 :: GLdouble))
    renderPrimitive LineLoop $ ugoVertices2D 0 2 $ [(-2,0),(-6,4),(-10,0),(-6,-4)]
    renderPrimitive LineLoop $ ugoVertices2D 0 2 $ [(2,4),(16,4),(4,16),(-10,16)]
    renderPrimitive LineLoop $ ugoVertices2D 0 2 $ [(2,-4),(16,-4),(4,-16),(-10,-16)]

  renderGameObject Ducker{position = (x:+y),hitDisp=hd,hasItem=item,velocity = v,gVelocity = g,age = a} = preservingMatrix $ do
    translate (Vector3 x y 0)
    if signum (imagPart g) > 0 then scale 1 (-1) (1 :: GLdouble) else return ()
    if signum (realPart v) < 0 then scale (-1) 1 (1 :: GLdouble) else return ()
    --after this, ducker is on the lower ground, looking right
    color (if item then (Color3 1.0 0.2 0.2 :: Color3 GLdouble) else (Color3 0.3 1.0 0.7 :: Color3 GLdouble))
    renderShape (0:+0) hd
    renderPrimitive LineStrip $ vertices2D 0 [(0,0),(kx,ky),(fx,fy)]
    where
      fx:+fy=foot $ intToGLdouble a/2
      kx:+ky=knee $ intToGLdouble a/2
      foot theta = (16*cos(-theta)):+(-16+8*sin(-theta))
      knee theta = foot theta * (0.5 :+ (- sqrt(square(legLen/magnitude(foot theta)) - 0.25)))
      legLen = 16

  renderGameObject Jumper{position = (x:+y),hitDisp=hd,hasItem=item,gravity = g,velocity=v} = preservingMatrix $ do
    translate (Vector3 x y 0)
    color (if item then (Color3 1.0 0.2 0.2 :: Color3 GLdouble) else (Color3 0.3 1.0 0.7 :: Color3 GLdouble))
    renderShape (0:+0) hd
    if gsign >0 then rotate 180 (Vector3 (1 :: GLdouble) 0 0) else return() -- after this you can assume that the object is not upside down
    renderPrimitive LineStrip $ ugoVertices2D 0 2 $ [(15,-5),(25,-5+absvy*leg),(25,-25+absvy*leg)]
    renderPrimitive LineStrip $ ugoVertices2D 0 2 $ [(-15,-5),(-25,-5+absvy*leg),(-25,-25+absvy*leg)]
    where
      gsign = signum $ imagPart g
      absvy = imagPart v * gsign -- if falling (+) ascending (-)
      leg = 1.5

  renderGameObject Grashia{position = (x:+y),hitDisp=hd,hasItem=item,gunVector = nv,gravity = g,mode=m} = preservingMatrix $ do
    color (if item then (Color3 1.0 0.2 0.2 :: Color3 GLdouble) else (Color3 0.3 1.0 0.7 :: Color3 GLdouble))
    translate (Vector3 x y 0)
    renderShape (0:+0) hd
    renderPrimitive LineLoop $ ugoVertices2D 0 2 $ map (\r -> (nvx*r,nvy*r)) [16,32]
    if m == 1 then do
      renderShape 0 $ Circular (16:+12*gsign) 4
      renderShape 0 $ Circular ((-16):+12*gsign) 4
     else return ()
    where
      nvx:+nvy = nv
      gsign = signum $ imagPart g

  renderGameObject me@ScrambleHatch{position = (x:+y),hitDisp=_,gravity= g,gateAngle = angl} = preservingMatrix $ do
    translate (Vector3 x y 0)
    color (Color3 (1.2*(1-hpRate)) 0.5 (1.6*hpRate)  :: Color3 GLdouble)
    if gsign >0 then rotate 180 (Vector3 (1 :: GLdouble) 0 0) else return() -- after this you can assume that the object is not upside down
    renderPrimitive LineLoop $ ugoVertices2DFreq 0 (angl*2) 1 $ [(-45,1),(-45,hatchHeight),(45,hatchHeight),(45,1)]
    preservingMatrix $ do
      translate (Vector3 45 hatchHeight (0 :: GLdouble))
      rotate (-angl/pi*180) (Vector3 0 0 (1 :: GLdouble))
      renderPrimitive LineLoop $ ugoVertices2DFreq 0 (angl*1) 2 $ [(0,0),(-45,0),(-45,10)]
    preservingMatrix $ do
      translate (Vector3 (-45) hatchHeight (0 :: GLdouble))
      rotate (angl/pi*180) (Vector3 0 0 (1 :: GLdouble))
      renderPrimitive LineLoop $ ugoVertices2DFreq 0 (angl*1) 2 $ [(0,0),(45,0),(45,10)]
    where
      gsign = signum $ imagPart g
      hpRate = (intToGLdouble $ hp me)/(intToGLdouble hatchHP)

  renderGameObject LandScapeBlock{position=pos,hitDisp=hd} = preservingMatrix $ do
    color (Color3 0.6 0.2 0 :: Color3 GLdouble)
    renderShape pos hd
    if treasure!!(baseGameLevel variables) then do
      color (Color3 0.7 0.23 0 :: Color3 GLdouble)
      translate (Vector3 0 0 (60 :: GLdouble))
      renderShape pos hd
      color (Color3 0.5 0.17 0 :: Color3 GLdouble)
      translate (Vector3 0 0 (-120 :: GLdouble))
      renderShape pos hd
     else return()

  renderGameObject me@Particle{position = x:+y,particleColor=Color3 mr mg mb} = preservingMatrix $ do
    if age me>=0 then do
      translate (Vector3 x y 0)
      color (Color3 r g b)
      renderShape (0:+0) $ Circular (0:+0) (size me*extent)
      else return ()
    where
      extent = 0.5 +  intCut (intToGLdouble(age me) / decayTime me)
      decay =  exp $  intCut $ -intToGLdouble (age me) / decayTime me
      whiteout = exp $ intCut $  -2*intToGLdouble (age me) / decayTime me
      r = mr * decay + whiteout
      g = mg * decay + whiteout
      b = mb * decay + whiteout
      intCut :: GLdouble -> GLdouble
      intCut = intToGLdouble.round

  renderGameObject Star{position = x:+y,particleColor=c} = preservingMatrix $ do
    color c
    renderPrimitive LineStrip $ ugoVertices2D 0 2 [(0.1+x,0+y),(-0.1+x,0+y)]


  renderGameObject DebugMessage{debugMessage=str} =
    putDebugStrLn str

  renderGameObject _ = return ()
  vicViper = fromJust $ find (\obj -> case obj of
                            VicViper{} -> True
                            _          -> False) objects

  renderShape :: Complex GLdouble -> Shape -> IO ()
  renderShape (x:+y) s = case s of
    Rectangular{bottomLeft = (l:+b), topRight = (r:+t)}  ->
      renderPrimitive LineLoop $ vertices2D 0 [(x+l,y+b),(x+l,y+t),(x+r,y+t),(x+r,y+b)]
    Circular{center=cx:+cy, radius = r} -> preservingMatrix $ do
      translate (Vector3 (cx+x) (cy+y) 0)
      rotate (intToGLdouble gameclock*(45+pi)) (Vector3 0 0 (1 :: GLdouble))
      scale r r 1
      renderPrimitive LineLoop $ vertices2D 0 $ map (\t -> (cos(2/7*t*pi),sin(2/7*t*pi))) [0..6]
    Shapes{children=cs} -> mapM_ (renderShape (x:+y)) cs


  renderWithShade :: ColorComponent a=>Color3 a -> Color3 a -> IO () -> IO ()
  renderWithShade colorA colorB rndrr = do
    color colorB
    preservingMatrix $ do
      translate $ Vector3 1 (-1) (-1 :: GLdouble)
      rndrr
    color colorA
    preservingMatrix rndrr

--   ugoVertex :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
--   ugoVertex x y z r = ugoVertexFreq x y z r standardUgoInterval

  ugoVertexFreq :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> Int -> IO ()
  -- renders a vertex at somewhere near (x y z),
  -- but the point wiggles around in ugoRange when each interval comes.
  ugoVertexFreq x y z ugoRange intrvl = vertex $ Vertex3 (x+dr*cos theta) (y+dr*sin theta) z where
    flipper :: GLdouble
    flipper = fromIntegral $ (gameclock `div` intrvl) `mod` 1024
    dr = ugoRange * vibrator(phi)
    theta = (x + sqrt(2)*y + sqrt(3)*z + 573) * 400 * flipper
    phi   = (x + sqrt(3)*y + sqrt(7)*z + 106) * 150 * flipper
    vibrator a = 0.5 * (1 + sin a)

  ugoTranslate x y z ugoRange = ugoTranslateFreq x y z ugoRange standardUgoInterval
  ugoTranslateFreq x y z ugoRange intvl = translate (Vector3 (x+dr*cos theta) (y+dr*sin theta) z) where
    flipper :: GLdouble
    flipper = fromIntegral $ (gameclock `div` intvl) `mod` 1024
    dr = ugoRange * vibrator(phi)
    theta = (x + sqrt(2)*y + sqrt(3)*z + 573) * 400 * flipper
    phi   = (x + sqrt(3)*y + sqrt(7)*z + 106) * 150 * flipper
    vibrator a = 0.5 * (1 + sin a)

  ugoVertices2D z r xys = ugoVertices2DFreq z r standardUgoInterval xys
  ugoVertices2DFreq z r intrvl xys = mapM_ (\(x,y) -> ugoVertexFreq x y z r intrvl) xys

  vertices2D :: GLdouble -> [(GLdouble,GLdouble)] -> IO ()
  vertices2D z xys = mapM_ (\(x,y) -> vertex $ Vertex3 x y z) xys

standardUgoInterval :: Int
standardUgoInterval = 7

isMonadiusOver :: Monadius -> Bool
isMonadiusOver (Monadius (vars,_)) = flagGameover vars