// Update state of live aliens func updateLive(dt float32) { x0, y0 := Zoo[0].Sx, Zoo[0].Sy for k := 1; k < len(Zoo); k++ { c := &Zoo[k] // Update S and v bounce(&c.Sx, &c.vx, xSize-1, dt) bounce(&c.Sy, &c.vy, ySize-1, dt) // Update Progress c.Progress += c.fallRate * dt // Update health if c.health > 0 { // Healthy alien if c.Progress >= 1 { // Alien reached full power! if !isPractice { gameState = GameLose } // Mark alien for culling c.health = deathThreshold continue } dx := c.Sx - x0 dy := c.Sy - y0 if dx*dx+dy*dy <= killRadius2 { const killTime = 0.1 c.health -= healthType(dt * (float32(initialHealth) / killTime)) if c.health <= 0 { c.health = -1 // Transition to death sequence sound.Play(sound.Twang, alienPitch[c.Id]) } c.Show = true } else { c.Show = showAlways } } else { // Dying alien c.health -= 1 c.Show = true } // Update amplitude if c.health > 0 { c.Amplitude = math32.Sqrt(c.Progress) } else { c.Amplitude -= dt * (1 / amplitudeDieTime) if c.Amplitude < 0 { // Alien croaked. Mark as dead c.health = deathThreshold if !isPractice { TallyKill() } } } } }
func tryBirth(dt float32) { j := len(Zoo) if nLiveMax > len(zooStorage)-1 { panic(fmt.Sprintf("tryBirth: nLiveMax=%v > len(zooStorage)-1=%v\n", nLiveMax, len(zooStorage)-1)) } if nLive := j - 1; nLive >= nLiveMax { // Already reached population limit return } if rand.Float32() > dt*birthRate { return } // Swap in random id avail := len(zooStorage) - len(Zoo) k := rand.Intn(avail) + j zooStorage[j].Id, zooStorage[k].Id = zooStorage[k].Id, zooStorage[j].Id Zoo = zooStorage[:j+1] // Initialize the alien Zoo[j].initAlien() sound.Play(sound.AntiTwang, alienPitch[Zoo[j].Id]) }
/* The "boot sequence" was created in the 1990's to create eye candy while the program was slowly computing lookup tables. By the mid-2000s machines were so fast that it has no practical purpose anymore. But to retain the original look of Frequon Invaders, it's done nonethless, with the teletype techno-babble. It like the flutes on concrete columnes. */ func advanceBootSequence(dt float32) { if bootSequenceIndex < 0 || bootSequenceIndex >= 10 { return } bootSequenceFrac += dt if bootSequenceFrac < bootSequencePeriod { return } bootSequenceFrac = 0 n := bootSequenceIndex bootSequenceIndex = n + 1 if 1 <= n && n <= 8 { teletype.PrintUpper(grammar.Generate(rune('0' + n))) teletype.PrintChar('\n') } if 0 < n && n <= 8 { sound.Play(sound.Wobble, float32(n+1)*0.25) } switch n { case 1: break case 2, 3, 4: dividerCount = n - 1 case 5: fallIsVisible = true case 6: radarIsVisible = true radarIsRunning = true case 7: scoreIsVisible = true case 9: // C++ original does following actions for n==8, but that hides the 8th techobabble. fourierIsVisible = true setZoom(zoomGrow) teletype.Reset() } }
// At certain scores, certain kinds of damages become possible. // Here are the thresholds: // // 0: 1 stationary alien // 1: half-speed alien // 2: full-speed alien // 4: 2 stationary aliens // 8: phase, real, or imaginary lost, 1 stationary alien // 12: 3 aliens // 16: lose 1 color bit // 20: 4 aliens // 24: lose 2 color bits // 32: compress x or y // // n is score func setDifficulty(n int) { const simpleScheme = coloring.AllBits if n == 0 { nLiveMax = 1 velocityMax = 0 compressX = 1 compressY = 1 scheme = simpleScheme return } if n >= 64 { gameState = GameWin return } // Set nLiveMax, which is the max number of aliens simultaneously running. var liveLimit int if n < 4 { liveLimit = 1 } else { liveLimit = (n-4)/7 + 2 } if nLiveMax < liveLimit { if rand.Intn(2) == 1 { nLiveMax++ // The game is now harder. Simplify other things that make the game not quite so hard. if n < 8 { velocityMax = 0 } else { velocityMax /= 2 } if scheme != simpleScheme { scheme = simpleScheme sound.Play(sound.Bell, 1.5) } return } } // See if velocityMax should be increased // FIXME - should be scaled to screen size var velocityLimit float32 if n <= 4 { velocityLimit = float32(n * 15) } else { velocityLimit = 60 } if velocityMax < velocityLimit { if rand.Intn(2) == 1 { velocityMax += velocityLimit * 0.5 if velocityMax > velocityLimit { velocityMax = velocityLimit } if nLiveMax > 1 { // Ease up on live limit nLiveMax-- } // The game is now harder. return } } // Now consider a change in the radar scheme. if n >= 8 { if rand.Intn(2) == 1 { s := scheme if s&coloring.CoordinateBits == coloring.CoordinateBits { // Break the radar by removing phase, real, or imaginary information. switch rand.Intn(3) { case 0: s &= ^coloring.PhaseBit case 1: s &= ^coloring.RealBit case 2: s &= ^coloring.ImagBit } // Play sound announcing damage. sound.Play(sound.Broken, 0.5) } else { // Fix the radar by restoring all coordinate information (but not color) s |= coloring.CoordinateBits sound.Play(sound.Bell, 1) } // Commit the new scheme scheme = s if s&coloring.CoordinateBits != coloring.CoordinateBits { // Game is harder. return } } } // Consider changing color failures. if n >= 16 { r := rand.Intn(6) if r&1 == 0 { var minOnes int if n >= 24 { minOnes = 1 } else { minOnes = 2 } s := scheme ^ coloring.RedBit<<uint(r%3) if bitCount(s&coloring.ColorBits) >= minOnes { scheme = s // Game is harder sound.Play(sound.Broken, 1) return } } } // Consider compression if n >= 32 { compressed := false switch rand.Intn(2) { case 0: if compressX > 1./16. { compressX *= 0.5 compressed = true } case 1: if compressY > 1./16. { compressY *= 0.5 compressed = true } } if compressed { // Ease up on lost colors // FIXME - consider restoring only one color bit scheme |= coloring.ColorBits if nLiveMax > 1 { // Ease up on live limit nLiveMax-- } sound.Play(sound.Broken, 0.75) return } } }