├── go.mod ├── .gitignore ├── rebuild.sh ├── go.sum ├── f-terrain.go ├── layers.go ├── s-pad.yml ├── spritedata.go ├── f-pad.go ├── s-bullet.yml ├── event.go ├── levels.go ├── maker.go ├── s-blastwave.yml ├── runes.go ├── s-complete.yml ├── main.go ├── s-ship.yml ├── f-gantry.go ├── s-tinyexplosion.yml ├── s-hfence.yml ├── s-bullet.go ├── l-level1.yml ├── s-smexplosion.yml ├── s-exhaust.yml ├── s-pad.go ├── f-explosion.go ├── alarm.go ├── f-alien1.go ├── f-smexplosion.go ├── s-blastwave.go ├── s-press.yml ├── s-complete.go ├── l-level1.go ├── s-press.go ├── README.md ├── s-hfence.go ├── s-exhaust.go ├── s-tinyexplosion.go ├── f-bullet.go ├── s-ship.go ├── s-smexplosion.go ├── spritereg.go ├── leveldata.go ├── s-alien1.go ├── s-alien1.yml ├── collider.go ├── s-thrust.go ├── spritemgr.go ├── s-gantry.go ├── properties.go ├── s-explosion.go ├── s-gantry.yml ├── s-gameover.go ├── f-hfence.go ├── s-thrust.yml ├── mkassets.go ├── game.go ├── s-explosion.yml ├── s-gameover.yml ├── level.go ├── f-ship.go ├── story.txt ├── sprite.go ├── LICENSE └── s-cavern1.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gdamore/proxima5 2 | 3 | go 1.19 4 | 5 | require github.com/gdamore/tcell v1.4.0 6 | 7 | require ( 8 | github.com/gdamore/encoding v1.0.0 // indirect 9 | github.com/lucasb-eyer/go-colorful v1.0.3 // indirect 10 | github.com/mattn/go-runewidth v0.0.7 // indirect 11 | golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 // indirect 12 | golang.org/x/text v0.3.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #proxima5 binaries 2 | mkassets 3 | proxima5 4 | 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | 21 | build/ 22 | .vscode/ -------------------------------------------------------------------------------- /rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dn=$(dirname $0) 4 | cd $dn 5 | 6 | echo "Checking for Go" 7 | go version > /dev/null 8 | 9 | if [ $? -ne 0 ]; then 10 | echo "Go is required to build this project, and the go" 11 | echo "executable must be on your PATH." 12 | echo 13 | echo "Get Go from http://golang.org" 14 | exit 1 15 | fi 16 | 17 | echo "Getting dependencies" 18 | # Make sure our dependencies are present 19 | go get github.com/gdamore/tcell 20 | go get gopkg.in/yaml.v2 21 | 22 | echo "Building asset creator" 23 | # Build the asset constructor first 24 | go build mkassets.go spritedata.go leveldata.go properties.go || exit 1 25 | 26 | echo "Generating assets" 27 | ./mkassets -type level l-*.yml || exit 1 28 | ./mkassets -type sprite s-*.yml || exit 1 29 | 30 | echo "Building game" 31 | # Now build the program 32 | go build . 33 | 34 | echo "Finished." 35 | echo "Execute ${dn}/proxima5 to play. Have fun!" 36 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= 2 | github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= 3 | github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU= 4 | github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0= 5 | github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= 6 | github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 7 | github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= 8 | github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 9 | golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10= 10 | golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 11 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 12 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 13 | -------------------------------------------------------------------------------- /f-terrain.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | func makeTerrain(level *Level, props GameObjectProps) error { 18 | 19 | frame := props.PropString("frame", "TERRAIN") 20 | sname := props.PropString("sprite", "") 21 | if sname == "" { 22 | return nil 23 | } 24 | sprite := GetSprite(sname) 25 | sprite.SetLayer(LayerTerrain) 26 | sprite.SetFrame(frame) 27 | level.AddSprite(sprite) 28 | return nil 29 | } 30 | 31 | func init() { 32 | RegisterGameObjectMaker("terrain", makeTerrain) 33 | } 34 | -------------------------------------------------------------------------------- /layers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | const ( 18 | LayerBackdrop = 0 // Any backdrop (will we have?) 19 | LayerThrust = 1 // Ship thrust (set low to hide) 20 | LayerTerrain = 2 // Foreground terrain 21 | LayerHazard = 3 // Hazards that the player can hit 22 | LayerShot = 4 // Player's shots 23 | LayerPad = 5 // Launch pad 24 | LayerPlayer = 6 // The player's ship 25 | LayerExplosion = 7 // Player explosion 26 | LayerWipe = 8 // Blastwave 27 | LayerDialog = 9 // On top of everything 28 | ) 29 | -------------------------------------------------------------------------------- /s-pad.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: Pad 17 | width: 7 18 | height: 1 19 | originx: 3 20 | originy: 0 21 | layer: 5 22 | glyphs: 23 | d: 24 | background: black 25 | foreground: brown 26 | display: "▗" 27 | 28 | b: 29 | background: black 30 | foreground: brown 31 | display: "▖" 32 | 33 | P: 34 | background: black 35 | foreground: gray 36 | display: "░" 37 | 38 | frames: 39 | - names: [ PAD ] 40 | data: 41 | - "dPPPPPb" 42 | 43 | - names: [ TARMAC ] 44 | data: 45 | - "PPPPPPP" 46 | -------------------------------------------------------------------------------- /spritedata.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | type SpriteGlyph struct { 18 | Display string // What is displayed, can be Unicode 19 | Foreground string // See colors.go for color names 20 | Background string 21 | } 22 | 23 | type SpriteFrameData struct { 24 | Names []string // names for frame 25 | Next string // Next Frame number, or "" for none 26 | Time int // time in msec to display frame 27 | Data []string // lines of frame data (ASCII) 28 | } 29 | 30 | type SpriteData struct { 31 | Name string 32 | Width int 33 | Height int 34 | OriginX int 35 | OriginY int 36 | Layer int 37 | Glyphs map[string]SpriteGlyph 38 | Palette map[string]string 39 | Frames []SpriteFrameData 40 | } 41 | -------------------------------------------------------------------------------- /f-pad.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | func makePad(level *Level, props GameObjectProps) error { 18 | 19 | x := props.PropInt("x", 0) 20 | y := props.PropInt("y", 0) 21 | 22 | frame := props.PropString("frame", "PAD") 23 | sname := props.PropString("sprite", "Pad") 24 | sprite := GetSprite(sname) 25 | 26 | // sprite starts at 0, 0, so we can get its width 27 | // we use this to resize "TARMAC" frames. 28 | _, _, w, _ := sprite.Bounds() 29 | w++ 30 | if nw := props.PropInt("width", w); nw != w { 31 | sprite.Resize(nw, 1) 32 | } 33 | sprite.SetLayer(LayerPad) 34 | sprite.SetFrame(frame) 35 | sprite.SetPosition(x, y) 36 | level.AddSprite(sprite) 37 | return nil 38 | } 39 | 40 | func init() { 41 | RegisterGameObjectMaker("pad", makePad) 42 | } 43 | -------------------------------------------------------------------------------- /s-bullet.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | # 17 | # Bullet sprite. Bullet sprites are required to have a H frame, 18 | # which is set for bullets with horizontal motion, and a V frame, 19 | # which is used for bullets without horizontal (therefore vertical?) 20 | # motion. Additional frames may be used for animation. 21 | # 22 | name: Bullet 23 | width: 1 24 | height: 1 25 | layer: 4 26 | glyphs: 27 | V: 28 | background: black 29 | foreground: bullet 30 | display: "•" 31 | 32 | H: 33 | background: black 34 | foreground: bullet 35 | display: "•" 36 | palette: 37 | bullet: fuchsia 38 | 39 | frames: 40 | - names: [ "V" ] 41 | next: "" 42 | time: 0 43 | data: 44 | - "V" 45 | 46 | - names: [ "H" ] 47 | next: "" 48 | time: 0 49 | data: 50 | - "H" 51 | -------------------------------------------------------------------------------- /event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/gdamore/tcell" 21 | ) 22 | 23 | // EventHandler is anything that handles events. 24 | type EventHandler interface { 25 | HandleEvent(tcell.Event) bool 26 | } 27 | 28 | type EventGravity struct { 29 | when time.Time 30 | accel float64 31 | } 32 | 33 | func (ev *EventGravity) When() time.Time { 34 | return ev.when 35 | } 36 | 37 | func (ev *EventGravity) Accel() float64 { 38 | return ev.accel 39 | } 40 | 41 | type EventGame struct { 42 | when time.Time 43 | } 44 | 45 | func (ev *EventGame) When() time.Time { 46 | return ev.when 47 | } 48 | 49 | type EventPlayerDeath struct { 50 | EventGame 51 | } 52 | 53 | type EventLevelComplete struct { 54 | EventGame 55 | } 56 | 57 | type EventGameOver struct { 58 | EventGame 59 | } 60 | 61 | type EventLevelStart struct { 62 | EventGame 63 | } 64 | 65 | type EventTimesUp struct { 66 | EventGame 67 | } 68 | -------------------------------------------------------------------------------- /levels.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "bytes" 19 | "compress/gzip" 20 | "encoding/gob" 21 | "sync" 22 | ) 23 | 24 | var levelsD map[string]*LevelData 25 | var levelsL sync.Mutex 26 | 27 | func GetLevel(name string) *Level { 28 | levelsL.Lock() 29 | defer levelsL.Unlock() 30 | if data, ok := levelsD[name]; ok { 31 | return NewLevelFromData(data) 32 | } 33 | return nil 34 | } 35 | 36 | func addLevel(data *LevelData) { 37 | levelsL.Lock() 38 | defer levelsL.Unlock() 39 | if levelsD == nil { 40 | levelsD = make(map[string]*LevelData) 41 | } 42 | levelsD[data.Name] = data 43 | } 44 | 45 | func RegisterLevelGobZ(b []byte) { 46 | r, e := gzip.NewReader(bytes.NewBuffer(b)) 47 | if e != nil { 48 | panic("Malformed GZIP level data: " + e.Error()) 49 | } 50 | dec := gob.NewDecoder(r) 51 | data := &LevelData{} 52 | if e := dec.Decode(data); e != nil { 53 | panic("Malformed GOB level data: " + e.Error()) 54 | } 55 | addLevel(data) 56 | } 57 | -------------------------------------------------------------------------------- /maker.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "errors" 19 | "log" 20 | "sync" 21 | ) 22 | 23 | // ObjectMaker is a function that creates game objects as described 24 | // by the properties. There may actually be multiple objects or composite 25 | // objects taht result. The ObjectMaker will register any created objects 26 | // with the Level. 27 | type GameObjectMaker func(l *Level, props GameObjectProps) error 28 | 29 | var makerLk sync.Mutex 30 | var objectMakers map[string]GameObjectMaker 31 | 32 | func RegisterGameObjectMaker(name string, maker GameObjectMaker) { 33 | 34 | makerLk.Lock() 35 | if objectMakers == nil { 36 | objectMakers = make(map[string]GameObjectMaker) 37 | } 38 | objectMakers[name] = maker 39 | makerLk.Unlock() 40 | } 41 | 42 | func MakeGameObject(l *Level, name string, props GameObjectProps) error { 43 | makerLk.Lock() 44 | m, ok := objectMakers[name] 45 | makerLk.Unlock() 46 | if !ok || m == nil { 47 | log.Printf("No object maker for %s", name) 48 | return errors.New("Do not know how to make named object") 49 | } 50 | return m(l, props) 51 | } 52 | -------------------------------------------------------------------------------- /s-blastwave.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: Blastwave 17 | width: 1 18 | height: 1 19 | layer: 8 20 | glyphs: 21 | 1: 22 | background: color1 23 | foreground: color1 24 | display: " " 25 | 26 | 2: 27 | background: color2 28 | foreground: color2 29 | display: " " 30 | 31 | 3: 32 | background: color3 33 | foreground: color3 34 | display: " " 35 | 36 | 4: 37 | background: color2 38 | foreground: color2 39 | display: " " 40 | 41 | palette: 42 | color1: yellow 43 | color2: gold 44 | color3: orange 45 | color4: red 46 | 47 | frames: 48 | - names: [ F1 ] 49 | next: F2 50 | time: 50 51 | data: [ "1" ] 52 | 53 | - names: [ F2 ] 54 | next: F3 55 | time: 50 56 | data: [ "2" ] 57 | 58 | - names: [ F3 ] 59 | next: F4 60 | time: 700 61 | data: [ "3" ] 62 | 63 | - names: [ F4 ] 64 | next: F3 65 | time: 700 66 | data: [ "4" ] 67 | 68 | -------------------------------------------------------------------------------- /runes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/gdamore/tcell" 19 | ) 20 | 21 | // RegisterFallbacks registers the UTF-8 runes we use in this game with 22 | // reasonable (best effort) ASCII fallbacks. 23 | func RegisterFallbacks(screen tcell.Screen) { 24 | // Register a few fallback runes - best we can do for ASCII 25 | screen.RegisterRuneFallback('Ξ', "M") 26 | screen.RegisterRuneFallback('Ѧ', "A") 27 | screen.RegisterRuneFallback('Ж', "X") 28 | screen.RegisterRuneFallback('▖', "\\") 29 | screen.RegisterRuneFallback('▗', "/") 30 | screen.RegisterRuneFallback('▟', "/") 31 | screen.RegisterRuneFallback('▛', "/") 32 | screen.RegisterRuneFallback('▙', "\\") 33 | screen.RegisterRuneFallback('▜', "\\") 34 | screen.RegisterRuneFallback('˄', "^") 35 | screen.RegisterRuneFallback('˃', ">") 36 | screen.RegisterRuneFallback('˂', "<") 37 | screen.RegisterRuneFallback('˅', "v") 38 | screen.RegisterRuneFallback('♥', "A") 39 | screen.RegisterRuneFallback('─', "-") 40 | screen.RegisterRuneFallback('━', "-") 41 | screen.RegisterRuneFallback('═', "=") 42 | screen.RegisterRuneFallback('⁐', "O") 43 | screen.RegisterRuneFallback('•', "*") 44 | 45 | } 46 | -------------------------------------------------------------------------------- /s-complete.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: LevelComplete 17 | width: 33 18 | height: 1 19 | originx: 16 20 | originy: 1 21 | layer: 9 22 | glyphs: 23 | "L": 24 | background: color1 25 | foreground: color2 26 | display: "L" 27 | 28 | "V": 29 | background: color1 30 | foreground: color2 31 | display: "V" 32 | 33 | "E": 34 | background: color1 35 | foreground: color2 36 | display: "E" 37 | 38 | "C": 39 | background: color1 40 | foreground: color2 41 | display: "C" 42 | 43 | "O": 44 | background: color1 45 | foreground: color2 46 | display: "O" 47 | 48 | "P": 49 | background: color1 50 | foreground: color2 51 | display: "P" 52 | 53 | "M": 54 | background: color1 55 | foreground: color2 56 | display: "M" 57 | 58 | "T": 59 | background: color1 60 | foreground: color2 61 | display: "T" 62 | 63 | "_": 64 | background: color1 65 | foreground: color2 66 | display: " " 67 | 68 | palette: 69 | color1: green 70 | color2: white 71 | 72 | frames: 73 | - names: [ F0 ] 74 | data: [ "___L_E_V_E_L___C_O_M_P_L_E_T_E___" ] 75 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "flag" 19 | "fmt" 20 | "io/ioutil" 21 | "log" 22 | "math/rand" 23 | "os" 24 | "time" 25 | ) 26 | 27 | const Version = "0.2" 28 | 29 | // Proxima (Escape from Promixa 5) is a text-oriented game, utilizing 30 | // Unicode characters and text terminal display capabilties, demonstrating 31 | // the capabilities of tcell. It needs at least an 80x24 column terminal 32 | // to run well, and the highest baud rade you can arrange. 33 | 34 | func main() { 35 | 36 | var logfile string 37 | 38 | flag.StringVar(&logfile, "log", logfile, "Log file for debugging log") 39 | flag.Parse() 40 | 41 | rand.Seed(time.Now().UnixNano()) 42 | if logfile != "" { 43 | if f, e := os.Create(logfile); e == nil { 44 | log.SetOutput(f) 45 | } 46 | } else { 47 | log.SetOutput(ioutil.Discard) 48 | } 49 | game := &Game{} 50 | if err := game.Init(); err != nil { 51 | fmt.Printf("Failed to initialize game: %v\n", err) 52 | os.Exit(1) 53 | } 54 | if err := game.Run(); err != nil { 55 | fmt.Printf("Failed to run game: %v\n", err) 56 | os.Exit(1) 57 | } 58 | fmt.Printf("Escape from Proxima 5, %s\n", Version) 59 | fmt.Printf("Copyright 2016 Garrett D'Amore\n") 60 | fmt.Printf("Licensed under the Apache 2 license.\n") 61 | fmt.Printf("See http://www.apache.org/licenses/LICENSE-2.0\n") 62 | fmt.Printf("Thanks for playing!\n") 63 | } 64 | -------------------------------------------------------------------------------- /s-ship.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: Ship 17 | width: 5 18 | height: 4 19 | originx: 2 20 | originy: 0 21 | layer: 6 22 | glyphs: 23 | "A": 24 | background: black 25 | foreground: body 26 | display: "Ѧ" 27 | 28 | "E": 29 | background: body 30 | foreground: marking 31 | display: "Ξ" 32 | 33 | "M": 34 | background: body 35 | foreground: marking 36 | display: "Ж" 37 | 38 | "d": 39 | background: black 40 | foreground: body 41 | display: "▗" 42 | 43 | "b": 44 | background: black 45 | foreground: body 46 | display: "▖" 47 | 48 | "v": 49 | background: black 50 | foreground: dark 51 | display: "Ѧ" 52 | 53 | "u": 54 | background: black 55 | foreground: bright 56 | display: "Ѧ" 57 | palette: 58 | bright: gainsboro 59 | dark: darkgrey 60 | body: silver 61 | marking: red 62 | 63 | frames: 64 | - names: [ UP, FWD ] 65 | data: 66 | - " A " 67 | - " dEb " 68 | - "AEMEA" 69 | - " AEA " 70 | 71 | - names: [ LEFT ] 72 | data: 73 | - " A " 74 | - " dEb " 75 | - "vEMEu" 76 | - " vEu " 77 | 78 | - names: [ RIGHT ] 79 | data: 80 | - " A " 81 | - " dEb " 82 | - "uEMEv" 83 | - " uEv " 84 | -------------------------------------------------------------------------------- /f-gantry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/gdamore/tcell" 19 | ) 20 | 21 | type gantry struct { 22 | sprite *Sprite 23 | level *Level 24 | startx int 25 | } 26 | 27 | func (o *gantry) HandleEvent(ev tcell.Event) bool { 28 | s := o.sprite 29 | switch ev := ev.(type) { 30 | case *EventSpriteMove: 31 | x, _ := o.sprite.Position() 32 | if o.startx != 0 && o.startx-5 >= x { 33 | o.sprite.SetVelocity(0, 0) 34 | } 35 | case *EventCollision: 36 | switch ev.Collider().Layer() { 37 | case LayerShot, LayerPlayer: 38 | o.sprite.Hide() 39 | x, y, _, _ := s.Bounds() 40 | props := GameObjectProps{} 41 | props.PropSetInt("x", x) 42 | props.PropSetInt("y", y) 43 | props.PropSetInt("count", 2) 44 | MakeGameObject(o.level, "explosion", props) 45 | } 46 | case *EventLevelStart: 47 | o.sprite.SetVelocity(-3.0, 0) 48 | o.sprite.SetFrame("RETRACT") 49 | o.startx, _ = o.sprite.Position() 50 | } 51 | return false 52 | } 53 | 54 | func makeGantry(level *Level, props GameObjectProps) error { 55 | 56 | x := props.PropInt("x", 0) 57 | y := props.PropInt("y", 0) 58 | frame := props.PropString("frame", "START") 59 | sname := props.PropString("sprite", "Gantry") 60 | 61 | sprite := GetSprite(sname) 62 | o := &gantry{sprite: sprite, level: level} 63 | sprite.SetLayer(LayerHazard) 64 | sprite.Watch(o) 65 | sprite.SetFrame(frame) 66 | sprite.SetPosition(x, y) 67 | level.AddSprite(sprite) 68 | return nil 69 | } 70 | 71 | func init() { 72 | RegisterGameObjectMaker("gantry", makeGantry) 73 | } 74 | -------------------------------------------------------------------------------- /s-tinyexplosion.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: TinyExplosion 16 | width: 1 17 | height: 1 18 | layer: 7 19 | palette: 20 | color1: black 21 | color2: white 22 | color3: yellow 23 | color4: gold 24 | color5: orange 25 | color6: red 26 | 27 | glyphs: 28 | d: 29 | background: color1 30 | foreground: color2 31 | display: "•" 32 | s: 33 | background: color1 34 | foreground: color2 35 | display: "*" 36 | 37 | o: 38 | background: color1 39 | foreground: color3 40 | display: "o" 41 | 42 | O: 43 | background: color1 44 | foreground: color4 45 | display: "O" 46 | 47 | Q: 48 | background: color1 49 | foreground: color5 50 | display: "⁐" 51 | 52 | R: 53 | background: color1 54 | foreground: color6 55 | display: "⁐" 56 | 57 | frames: 58 | - names: [ F1 ] 59 | next: F2 60 | time: 100 61 | data: [ "d" ] 62 | 63 | - names: [ F2 ] 64 | next: F3 65 | time: 50 66 | data: [ "s" ] 67 | 68 | - names: [ F3 ] 69 | next: F4 70 | time: 70 71 | data: [ "o" ] 72 | 73 | - names: [ F4 ] 74 | next: F5 75 | time: 70 76 | data: [ "O" ] 77 | 78 | - names: [ F5 ] 79 | next: "F6" 80 | time: 70 81 | data: [ "Q" ] 82 | 83 | - names: [ F6 ] 84 | next: "" 85 | time: 70 86 | data: [ "R" ] 87 | -------------------------------------------------------------------------------- /s-hfence.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: HFence 16 | width: 2 17 | height: 1 18 | layer: 2 19 | palette: 20 | color1: black 21 | color2: gray 22 | color3: dodgerblue 23 | color4: blue 24 | glyphs: 25 | "L": 26 | background: color1 27 | foreground: color2 28 | display: "{" 29 | 30 | "R": 31 | background: color1 32 | foreground: color2 33 | display: "}" 34 | "X": 35 | background: color1 36 | foreground: color2 37 | display: "X" 38 | 39 | "z": 40 | background: color1 41 | foreground: color3 42 | display: "~" 43 | "Z": 44 | background: color1 45 | foreground: color4 46 | display: "═" 47 | 48 | frames: 49 | - names: [ LL ] 50 | next: "" 51 | data: 52 | - "LL" 53 | 54 | - names: [ LX ] 55 | next: "" 56 | data: 57 | - "LX" 58 | 59 | - names: [ RR ] 60 | next: "" 61 | data: 62 | - "RR" 63 | 64 | - names: [ RX ] 65 | next: "" 66 | data: 67 | - "XR" 68 | 69 | - names: [ OFF ] 70 | next: ON 71 | time: 4000 72 | data: 73 | - " " 74 | 75 | - names: [ ON ] 76 | next: ON1 77 | time: 500 78 | data: 79 | - "zz" 80 | 81 | - names: [ ON1 ] 82 | next: ON2 83 | time: 1000 84 | data: 85 | - "ZZ" 86 | 87 | - names: [ ON2 ] 88 | next: OFF 89 | time: 500 90 | data: 91 | - "zz" 92 | -------------------------------------------------------------------------------- /s-bullet.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xcf, 0x6e, 0xda, 0x40, 8 | 0x10, 0xc6, 0xe7, 0xf3, 0xae, 0x6d, 0x8a, 0x90, 0xaa, 0x5e, 0xb8, 0xf4, 0xd4, 0x43, 0x6f, 0x55, 9 | 0x8f, 0x7d, 0x00, 0x84, 0xa8, 0x0f, 0x15, 0xad, 0xd4, 0x88, 0x24, 0x42, 0x39, 0x2c, 0xb0, 0xd8, 10 | 0x2b, 0x6c, 0x63, 0xd9, 0x8b, 0x14, 0x0e, 0x91, 0x12, 0x42, 0xfe, 0x11, 0xc2, 0x43, 0xe4, 0xd1, 11 | 0xf2, 0x22, 0x13, 0xd9, 0x56, 0x10, 0x52, 0x72, 0xda, 0x6f, 0xbe, 0xfd, 0x34, 0xf3, 0x9b, 0xb9, 12 | 0xe0, 0x2b, 0x01, 0x34, 0xff, 0x67, 0xb9, 0xb1, 0xba, 0xab, 0xac, 0x02, 0xaf, 0x08, 0x9f, 0x20, 13 | 0xfb, 0x2a, 0xd1, 0x68, 0x11, 0xdc, 0x63, 0x33, 0xb1, 0x11, 0x24, 0xc1, 0x0b, 0xb4, 0x09, 0x23, 14 | 0x5b, 0x4a, 0xff, 0x6f, 0x6e, 0x42, 0x93, 0x9e, 0x1c, 0xe8, 0xd3, 0x52, 0xbb, 0x7f, 0xd4, 0x52, 15 | 0xe7, 0x55, 0xf8, 0x77, 0xbc, 0xcc, 0xa2, 0x02, 0x7c, 0x4b, 0xf0, 0xff, 0xa9, 0x58, 0x5b, 0xab, 16 | 0xc1, 0xf7, 0x04, 0xaf, 0x97, 0xab, 0x44, 0x17, 0xe0, 0x1d, 0x11, 0xfd, 0xe0, 0x1b, 0x09, 0x7c, 17 | 0x4d, 0x54, 0x36, 0x2c, 0x6c, 0x6e, 0xd2, 0xf0, 0x2c, 0x51, 0x26, 0xfd, 0x59, 0xd3, 0x54, 0x1d, 18 | 0xaa, 0x06, 0x2d, 0xf0, 0x9a, 0xe8, 0x17, 0x5f, 0x0b, 0x38, 0xbc, 0x26, 0x08, 0xf8, 0x5d, 0x53, 19 | 0x64, 0xb1, 0x5a, 0x96, 0x84, 0xcd, 0xde, 0x3c, 0xd7, 0x61, 0x3e, 0x5f, 0xa4, 0x93, 0xaa, 0xec, 20 | 0xa8, 0xf1, 0x6c, 0x5f, 0x12, 0x7d, 0xe3, 0x3b, 0x09, 0x7c, 0x39, 0x98, 0x51, 0x3f, 0x15, 0x4d, 21 | 0xab, 0x8c, 0x7c, 0xe7, 0x27, 0x07, 0x68, 0x0f, 0x0f, 0x87, 0x57, 0x94, 0xf5, 0x3d, 0x76, 0x04, 22 | 0xde, 0x10, 0x75, 0xf9, 0x41, 0x00, 0x9f, 0xdf, 0x7d, 0x6f, 0x08, 0x12, 0x6e, 0xbf, 0x5e, 0x6a, 23 | 0x4b, 0x90, 0x7d, 0x7d, 0x6e, 0x4b, 0x10, 0x79, 0x64, 0x12, 0x5d, 0xde, 0x42, 0xd6, 0xc1, 0x2d, 24 | 0x11, 0xb5, 0xf9, 0xd1, 0x01, 0x1a, 0xc3, 0x3d, 0xc3, 0x96, 0x4a, 0x82, 0x29, 0xaf, 0xe0, 0x75, 25 | 0x16, 0x71, 0xac, 0x2d, 0x1c, 0x38, 0xa2, 0x01, 0x07, 0x03, 0x88, 0x97, 0xcb, 0x67, 0x78, 0xa3, 26 | 0xda, 0x76, 0x47, 0xb1, 0x1a, 0xcf, 0x08, 0xc1, 0xc7, 0xf6, 0x9b, 0xe1, 0x4f, 0x17, 0xe3, 0xa8, 27 | 0x30, 0x0a, 0x0e, 0x80, 0x81, 0x00, 0x06, 0x04, 0x20, 0x10, 0x40, 0x40, 0xf4, 0x1a, 0x00, 0x00, 28 | 0xff, 0xff, 0xf3, 0xfb, 0x72, 0x0b, 0xed, 0x01, 0x00, 0x00, 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /l-level1.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | # 17 | # Level Map, starting in the cavern. 18 | # 19 | name: level1 20 | properties: 21 | title: Level One 22 | timer: 90 23 | gravity: 1.0 24 | wind: 0.0 25 | width: 320 26 | height: 220 27 | 28 | objects: 29 | terrain: 30 | class: terrain 31 | sprite: Cavern1 32 | 33 | player: 34 | class: ship 35 | x: "138" 36 | y: "215" 37 | 38 | pad: 39 | class: pad 40 | x: 138 41 | y: 219 42 | 43 | gantry: 44 | class: gantry 45 | x: 128 46 | y: 212 47 | 48 | pad2: 49 | class: pad 50 | x: 231 51 | y: 91 52 | 53 | A1: 54 | class: alien1 55 | x: 169 56 | y: 16 57 | vx: 20 58 | 59 | A2: 60 | class: alien1 61 | x: 169 62 | y: 12 63 | vx: -10 64 | 65 | A3: 66 | class: alien1 67 | x: 10 68 | y: 180 69 | vx: 14 70 | 71 | A4: 72 | class: alien1 73 | x: 186 74 | y: 186 75 | vx: 16 76 | 77 | A5: 78 | class: alien1 79 | x: 290 80 | y: 94 81 | vx: 24 82 | 83 | F1: 84 | class: hfence 85 | x: 236 86 | y: 93 87 | width: 21 88 | height: 2 89 | 90 | F2: 91 | class: hfence 92 | x: 204 93 | y: 210 94 | width: 40 95 | height: 2 96 | 97 | F3: 98 | class: hfence 99 | x: 108 100 | y: 148 101 | width: 40 102 | height: 4 103 | 104 | -------------------------------------------------------------------------------- /s-smexplosion.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: SmallExplosion 16 | width: 11 17 | height: 5 18 | layer: 7 19 | originx: 5 20 | originy: 2 21 | glyphs: 22 | A: 23 | background: yellow 24 | foreground: yellow 25 | display: "▒" 26 | 27 | B: 28 | background: orange 29 | foreground: yellow 30 | display: "▒" 31 | 32 | C: 33 | background: orange 34 | foreground: red 35 | display: "▒" 36 | D: 37 | background: red 38 | foreground: red 39 | display: "▒" 40 | 41 | frames: 42 | - names: [ F1 ] 43 | next: F2 44 | time: 70 45 | data: 46 | - " AAA " 47 | - " AAAAAAA " 48 | - " AAAAAAA " 49 | - " AAA " 50 | - " " 51 | 52 | - names: [ F2 ] 53 | next: F3 54 | time: 70 55 | data: 56 | - " BBBBB " 57 | - " BBBBBBBBB " 58 | - "BBBBBBBBBBB" 59 | - " BBBBBBBBB " 60 | - " BBBBB " 61 | 62 | - names: [ F3 ] 63 | next: F4 64 | time: 70 65 | data: 66 | - " CC " 67 | - " CCD " 68 | - " C CCCDD" 69 | - " CCCCCCCDD " 70 | - " CCDDD " 71 | 72 | - names: [ F4 ] 73 | next: F5 74 | time: 100 75 | data: 76 | - " D " 77 | - " D " 78 | - " DD" 79 | - " DD DD " 80 | - " DDDDD " 81 | 82 | - names: [ F5 ] 83 | next: "" 84 | time: 140 85 | data: 86 | - " " 87 | - " D " 88 | - " D" 89 | - " D D " 90 | - " D D D " 91 | -------------------------------------------------------------------------------- /s-exhaust.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: Exhaust 17 | width: 9 18 | height: 4 19 | originx: 4 20 | originy: 0 21 | layer: 2 22 | glyphs: 23 | ".": 24 | background: color1 25 | foreground: color2 26 | display: "." 27 | 28 | "O": 29 | background: color1 30 | foreground: color3 31 | display: "O" 32 | 33 | "o": 34 | background: color1 35 | foreground: color2 36 | display: "o" 37 | 38 | "@": 39 | background: color1 40 | foreground: color4 41 | display: "O" 42 | 43 | palette: 44 | color1: black 45 | color2: silver 46 | color3: lightslategrey 47 | color4: gray 48 | 49 | frames: 50 | - names: [ "LIFTOFF" ] 51 | next: "LIFTOFF1" 52 | time: 70 53 | data: 54 | - " " 55 | - " " 56 | - " " 57 | - " . . " 58 | 59 | - names: [ LIFTOFF1 ] 60 | next: "LIFTOFF2" 61 | time: 120 62 | data: 63 | - " " 64 | - " " 65 | - " " 66 | - " o. .o " 67 | 68 | - names: [ LIFTOFF2 ] 69 | next: "LIFTOFF3" 70 | time: 150 71 | data: 72 | - " " 73 | - " " 74 | - " " 75 | - "Oo. .oO" 76 | 77 | - names: [ LIFTOFF3 ] 78 | next: "LIFTOFF4" 79 | time: 150 80 | data: 81 | - " " 82 | - " " 83 | - " " 84 | - "Oo oO" 85 | 86 | - names: [ LIFTOFF4 ] 87 | next: "" 88 | time: 150 89 | data: 90 | - " " 91 | - " " 92 | - " " 93 | - "O O" 94 | -------------------------------------------------------------------------------- /s-pad.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x31, 0x8f, 0xd3, 0x40, 8 | 0x10, 0x85, 0xe7, 0x79, 0xd7, 0x36, 0x44, 0x11, 0x88, 0x26, 0x0d, 0x15, 0x05, 0x1d, 0xa2, 0xa4, 9 | 0x0f, 0x58, 0x81, 0x02, 0x82, 0x05, 0x91, 0x00, 0x45, 0x14, 0xeb, 0x78, 0xe5, 0xac, 0x62, 0x3b, 10 | 0xd6, 0x7a, 0x11, 0x18, 0x89, 0x82, 0x10, 0xe0, 0x2e, 0x97, 0x8b, 0x74, 0x4d, 0xee, 0xff, 0xdd, 11 | 0x1f, 0xd9, 0xd3, 0xda, 0xba, 0x28, 0xba, 0x9b, 0xc6, 0x6f, 0x9e, 0x9f, 0x66, 0xbe, 0x9d, 0x5f, 12 | 0xf6, 0x37, 0x03, 0x7a, 0x1f, 0x2b, 0xad, 0x8c, 0x8c, 0x84, 0x11, 0xb0, 0x2b, 0xc2, 0x7d, 0xf0, 13 | 0xb1, 0x28, 0x24, 0xfa, 0x04, 0xff, 0x93, 0x4a, 0xcd, 0x1c, 0x9c, 0x10, 0xbc, 0x91, 0x2a, 0x9b, 14 | 0x1b, 0x27, 0xc3, 0xf7, 0x5a, 0x65, 0xaa, 0xfc, 0x7c, 0xa4, 0xbf, 0x38, 0xed, 0xbf, 0x15, 0x8d, 15 | 0xd4, 0x6d, 0xf8, 0x75, 0xde, 0x54, 0xf3, 0x1a, 0xf6, 0x1f, 0x21, 0x8c, 0x45, 0x2e, 0x8d, 0x91, 16 | 0xb0, 0x27, 0x84, 0x60, 0xa4, 0x45, 0x21, 0x6b, 0xd8, 0x1d, 0x11, 0x3d, 0xb3, 0x7f, 0x39, 0xf0, 17 | 0xb8, 0x10, 0xd5, 0xb4, 0x36, 0x5a, 0x95, 0xd9, 0xd7, 0x42, 0xa8, 0xf2, 0x79, 0x47, 0xd3, 0x4e, 18 | 0x68, 0x07, 0xf4, 0x61, 0xd7, 0x44, 0x2f, 0xec, 0x1f, 0x06, 0xcf, 0xae, 0x09, 0x0c, 0x61, 0xa4, 19 | 0xea, 0x2a, 0x17, 0x8d, 0x23, 0xec, 0x8d, 0x96, 0x5a, 0x66, 0x7a, 0xf9, 0xad, 0x4c, 0xdb, 0xf6, 20 | 0xa5, 0x98, 0x2d, 0x0e, 0x2d, 0xd1, 0x13, 0xfb, 0x9f, 0x03, 0x8f, 0x8e, 0x76, 0x74, 0x9f, 0x96, 21 | 0xa6, 0xef, 0x22, 0x4f, 0xed, 0xb9, 0x07, 0x0c, 0xa6, 0xc7, 0xcb, 0x5b, 0xca, 0xee, 0x1e, 0x3b, 22 | 0x82, 0xdd, 0x10, 0x45, 0xf6, 0x94, 0x01, 0x0f, 0xef, 0xfc, 0xde, 0x10, 0x38, 0xfc, 0x71, 0xf7, 23 | 0xa8, 0x2d, 0x81, 0x8f, 0xe5, 0x0f, 0xe3, 0x40, 0xf8, 0x44, 0x15, 0xd2, 0xdd, 0x82, 0x77, 0xc1, 24 | 0x2d, 0x11, 0x0d, 0xec, 0x99, 0x07, 0xdc, 0x9b, 0x1e, 0x18, 0xb6, 0xe4, 0x08, 0x7e, 0xda, 0x15, 25 | 0x58, 0x2c, 0x52, 0x3c, 0x80, 0x87, 0xc0, 0xeb, 0x81, 0x21, 0x05, 0xbb, 0xda, 0x5f, 0xc2, 0x4f, 26 | 0xf4, 0xf2, 0x7b, 0x09, 0x3f, 0xc9, 0xc5, 0x6c, 0x41, 0x48, 0x9c, 0xbb, 0xbf, 0xed, 0xc6, 0xce, 27 | 0xbd, 0x00, 0xcf, 0xb4, 0x68, 0x6e, 0x4c, 0xcf, 0x03, 0x58, 0x3c, 0x8c, 0x18, 0xc2, 0x34, 0x76, 28 | 0x95, 0x10, 0x10, 0x4c, 0x86, 0x1f, 0xde, 0x0d, 0x5f, 0x31, 0x84, 0x71, 0x57, 0x44, 0xd7, 0x01, 29 | 0x00, 0x00, 0xff, 0xff, 0x25, 0x0e, 0x78, 0x22, 0x01, 0x02, 0x00, 0x00, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /f-explosion.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "math/rand" 19 | "time" 20 | 21 | "github.com/gdamore/tcell" 22 | ) 23 | 24 | // 25 | // "explosion" understands these class-specific properties: 26 | // 27 | // count - number of little explosion clouds 28 | // interval - intervalin msec between clouds 29 | // xdistance - max x distance in cells between explosion clouds 30 | // ydistance - max y distance in cells between explosion clouds 31 | // 32 | type explosion struct { 33 | level *Level 34 | } 35 | 36 | func (o *explosion) HandleEvent(ev tcell.Event) bool { 37 | switch ev := ev.(type) { 38 | case *EventSpriteFrame: 39 | if ev.Frame() == "" { 40 | o.level.RemoveSprite(ev.Sprite()) 41 | } 42 | } 43 | return false 44 | } 45 | 46 | func makeExplosion(level *Level, props GameObjectProps) error { 47 | 48 | x := props.PropInt("x", 0) 49 | y := props.PropInt("y", 0) 50 | o := &explosion{level: level} 51 | 52 | frame := props.PropString("frame", "0") 53 | sname := props.PropString("sprite", "Explosion") 54 | count := props.PropInt("count", 1) 55 | interval := props.PropInt("interval", 80) 56 | xdist := props.PropInt("xdistance", 5) 57 | ydist := props.PropInt("ydistance", 3) 58 | 59 | lx, ly := x, y 60 | d := time.Duration(0) 61 | for i := 0; i < count; i++ { 62 | sprite := GetSprite(sname) 63 | if i > 0 { 64 | lx += rand.Intn(xdist*2) - xdist 65 | ly += rand.Intn(xdist*2) - ydist 66 | d += time.Millisecond * time.Duration(interval) 67 | sprite.SetFrame("") 68 | sprite.ScheduleFrame(frame, time.Now().Add(d)) 69 | } else { 70 | sprite.SetFrame("0") 71 | } 72 | sprite.SetPosition(lx, ly) 73 | sprite.SetLayer(LayerExplosion) 74 | sprite.Watch(o) 75 | level.AddSprite(sprite) 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func init() { 82 | RegisterGameObjectMaker("explosion", makeExplosion) 83 | } 84 | -------------------------------------------------------------------------------- /alarm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | ) 20 | 21 | // Alarm represents an alarm event. 22 | type Alarm struct { 23 | expire time.Time 24 | h EventHandler 25 | } 26 | 27 | // AlarmClock manages Alarms. Each Alarm is set on a clock, and a 28 | // single AlarmClock can have unlimited alarms registered with it. 29 | // However, the actual alarms only ring (run the handlers) when the 30 | // AlarmClock's Tick method is executed; no implicit asynchronous 31 | // handling is performed. 32 | type AlarmClock map[*Alarm]struct{} 33 | 34 | // NewAlarmClock creates an AlarmClock. 35 | func NewAlarmClock() AlarmClock { 36 | return AlarmClock{} 37 | } 38 | 39 | // Schedule schedules an alarm to fire after the duration has expired. 40 | func (c AlarmClock) Schedule(d time.Duration, h EventHandler) *Alarm { 41 | a := &Alarm{expire: time.Now().Add(d), h: h} 42 | c[a] = struct{}{} 43 | return a 44 | } 45 | 46 | // Cancel cancels a previously scheduled alarm. 47 | func (c AlarmClock) Cancel(a *Alarm) { 48 | delete(c, a) 49 | } 50 | 51 | // Tick runs through the list of alarms on this clock,, and runs the 52 | // event handler for any that have expired. Any alarm that is fired 53 | // is descheduled. Note that handlers all run synchornously within 54 | // the caller's context. 55 | func (c AlarmClock) Tick(now time.Time) { 56 | for a := range c { 57 | if a.expire.Before(now) { 58 | delete(c, a) 59 | ev := &EventAlarm{when: now} 60 | a.h.HandleEvent(ev) 61 | } 62 | } 63 | } 64 | 65 | // Reset resets the clock, clearing all alarms. 66 | func (c AlarmClock) Reset() { 67 | for a := range c { 68 | delete(c, a) 69 | } 70 | } 71 | 72 | // EventAlarm is dispatched to the handler for an Alarm. 73 | type EventAlarm struct { 74 | when time.Time 75 | } 76 | 77 | // When returns the time when the alarm fired. 78 | func (ev *EventAlarm) When() time.Time { 79 | return ev.when 80 | } 81 | -------------------------------------------------------------------------------- /f-alien1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/gdamore/tcell" 19 | ) 20 | 21 | type alien1 struct { 22 | sprite *Sprite 23 | level *Level 24 | } 25 | 26 | func (o *alien1) HandleEvent(ev tcell.Event) bool { 27 | s := o.sprite 28 | switch ev := ev.(type) { 29 | case *EventSpriteAccelerate: 30 | if ev.s != s { 31 | return false 32 | } 33 | vx, _ := s.Velocity() 34 | if vx > 0 { 35 | s.SetFrame("F1") 36 | } else { 37 | s.SetFrame("R1") 38 | } 39 | case *EventCollision: 40 | switch ev.Collider().Layer() { 41 | case LayerTerrain, LayerHazard: 42 | x, y, _, _ := s.Bounds() 43 | vx, vy := s.Velocity() 44 | vx = -vx 45 | vy = -vy 46 | s.SetVelocity(vx, vy) 47 | if vx < 0 { 48 | x-- 49 | } else if vx > 0 { 50 | x++ 51 | } 52 | if vy < 0 { 53 | y-- 54 | } else if vy > 0 { 55 | y++ 56 | } 57 | s.SetPosition(x, y) 58 | case LayerShot, LayerPlayer: 59 | s.Hide() 60 | x, y, _, _ := s.Bounds() 61 | props := GameObjectProps{} 62 | props.PropSetInt("x", x) 63 | props.PropSetInt("y", y) 64 | props.PropSetInt("count", 1) 65 | MakeGameObject(o.level, "smexplosion", props) 66 | o.level.RemoveSprite(o.sprite) 67 | } 68 | } 69 | return false 70 | } 71 | 72 | func makeAlien1(level *Level, props GameObjectProps) error { 73 | 74 | x := props.PropInt("x", 0) 75 | y := props.PropInt("y", 0) 76 | velx := props.PropFloat64("vx", 3.5) 77 | vely := props.PropFloat64("vy", 0) 78 | frame := props.PropString("frame", "F1") 79 | sname := props.PropString("sprite", "Alien1") 80 | 81 | sprite := GetSprite(sname) 82 | o := &alien1{sprite: sprite, level: level} 83 | sprite.SetLayer(LayerHazard) 84 | sprite.Watch(o) 85 | sprite.SetFrame(frame) 86 | sprite.SetPosition(x, y) 87 | sprite.SetVelocity(velx, vely) 88 | level.AddSprite(sprite) 89 | return nil 90 | } 91 | 92 | func init() { 93 | RegisterGameObjectMaker("alien1", makeAlien1) 94 | } 95 | -------------------------------------------------------------------------------- /f-smexplosion.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "math/rand" 19 | "time" 20 | 21 | "github.com/gdamore/tcell" 22 | ) 23 | 24 | // 25 | // "smexplosion" understands these properties: 26 | // 27 | // count - number of little explosion clouds 28 | // interval - intervalin msec between clouds 29 | // xdistance - max x distance in cells between explosion clouds 30 | // ydistance - max y distance in cells between explosion clouds 31 | // 32 | type smExplosion struct { 33 | level *Level 34 | } 35 | 36 | func (o *smExplosion) HandleEvent(ev tcell.Event) bool { 37 | switch ev := ev.(type) { 38 | case *EventSpriteFrame: 39 | if ev.Frame() == "" { 40 | o.level.RemoveSprite(ev.Sprite()) 41 | } 42 | } 43 | return false 44 | } 45 | 46 | func makeSmExplosion(level *Level, props GameObjectProps) error { 47 | 48 | x := props.PropInt("x", 0) 49 | y := props.PropInt("y", 0) 50 | o := &smExplosion{level: level} 51 | 52 | frame := props.PropString("frame", "0") 53 | sname := props.PropString("sprite", "SmallExplosion") 54 | count := props.PropInt("count", 1) 55 | interval := props.PropInt("interval", 80) 56 | xdist := props.PropInt("xdistance", 3) 57 | ydist := props.PropInt("ydistance", 2) 58 | delay := props.PropInt("delay", 0) 59 | 60 | lx, ly := x, y 61 | d := time.Duration(delay) * time.Millisecond 62 | for i := 0; i < count; i++ { 63 | sprite := GetSprite(sname) 64 | if i > 0 { 65 | d += time.Millisecond * time.Duration(interval) 66 | lx += rand.Intn(xdist*2) - xdist 67 | ly += rand.Intn(xdist*2) - ydist 68 | } 69 | if i > 0 || delay != 0 { 70 | sprite.SetFrame("") 71 | sprite.ScheduleFrame(frame, time.Now().Add(d)) 72 | } else { 73 | sprite.SetFrame("0") 74 | } 75 | sprite.SetPosition(lx, ly) 76 | sprite.SetLayer(LayerExplosion) 77 | sprite.Watch(o) 78 | level.AddSprite(sprite) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func init() { 85 | RegisterGameObjectMaker("smexplosion", makeSmExplosion) 86 | } 87 | -------------------------------------------------------------------------------- /s-blastwave.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x50, 0xcb, 0x8a, 0x13, 0x41, 8 | 0x14, 0xbd, 0xa7, 0xba, 0xd2, 0x71, 0x86, 0x80, 0xba, 0x99, 0x8d, 0x1b, 0x5d, 0xb8, 0x13, 0x21, 9 | 0xe9, 0xc6, 0x0f, 0x18, 0x42, 0xeb, 0x42, 0xa2, 0xa0, 0xa0, 0x12, 0x5c, 0x14, 0xd3, 0x45, 0xa7, 10 | 0xb0, 0xfa, 0x41, 0x75, 0xe9, 0x4c, 0x2f, 0x5c, 0x38, 0x8e, 0xaf, 0x31, 0xce, 0x7f, 0xfa, 0x13, 11 | 0x5e, 0xe9, 0xea, 0x64, 0x12, 0xd0, 0xd5, 0x79, 0xd4, 0xe1, 0xde, 0x53, 0xf7, 0x23, 0x7f, 0x8a, 12 | 0x80, 0xc3, 0x17, 0x8d, 0x33, 0x5e, 0xcf, 0x95, 0x57, 0xe0, 0x73, 0xc2, 0x01, 0xe4, 0x42, 0x95, 13 | 0x1a, 0x13, 0xc2, 0xe8, 0x95, 0xc9, 0xfd, 0x0a, 0x92, 0x10, 0x3f, 0xd1, 0xa6, 0x58, 0xf9, 0x9e, 14 | 0x8e, 0x9f, 0x39, 0x53, 0x98, 0xea, 0xf5, 0x1e, 0x7f, 0xd3, 0xf3, 0xd1, 0x53, 0xd5, 0x69, 0x17, 15 | 0xc2, 0x8f, 0x6d, 0xd7, 0xac, 0x5a, 0xf0, 0x57, 0xc2, 0xf8, 0xb9, 0xb2, 0xda, 0x7b, 0x0d, 0xfe, 16 | 0x4e, 0x88, 0x33, 0xa7, 0x4a, 0xdd, 0x82, 0xaf, 0x88, 0xe8, 0x01, 0x7f, 0x91, 0xc0, 0x9d, 0x52, 17 | 0x35, 0xcb, 0xd6, 0x3b, 0x53, 0x15, 0x6f, 0x4b, 0x65, 0xaa, 0x87, 0x43, 0x9b, 0x30, 0x21, 0x0c, 18 | 0x98, 0x80, 0x2f, 0x88, 0x1e, 0xf1, 0xe7, 0x08, 0x82, 0x2f, 0x08, 0x11, 0xc6, 0x73, 0xd3, 0x36, 19 | 0x56, 0x75, 0x7d, 0xc3, 0xc3, 0xac, 0x76, 0xba, 0x70, 0xf5, 0xfb, 0x2a, 0x0f, 0xf2, 0x58, 0x9d, 20 | 0xbc, 0xbb, 0x96, 0x44, 0xf7, 0xf8, 0x9b, 0x04, 0x6e, 0xef, 0xed, 0x18, 0x20, 0xb4, 0x99, 0xf4, 21 | 0x91, 0xfb, 0xfc, 0x4b, 0x00, 0x47, 0xcb, 0xfd, 0xe5, 0xa1, 0xe5, 0x70, 0x8f, 0x2b, 0x02, 0x5f, 22 | 0x12, 0xcd, 0xf9, 0x47, 0x04, 0xdc, 0xfc, 0xe7, 0xf9, 0x92, 0x20, 0x31, 0x5a, 0x0c, 0x9f, 0x5a, 23 | 0x13, 0xe4, 0x42, 0x9f, 0xf9, 0xbe, 0x88, 0x7c, 0x69, 0x4a, 0xdd, 0xdf, 0x42, 0x0e, 0xc1, 0x35, 24 | 0x11, 0x1d, 0xf1, 0x4f, 0x01, 0xdc, 0x58, 0x5e, 0x77, 0x58, 0x53, 0xdf, 0x80, 0x7f, 0xf3, 0x39, 25 | 0x0e, 0x8e, 0xad, 0x6a, 0xfd, 0xa9, 0xfa, 0xa0, 0x21, 0x20, 0xa2, 0x5b, 0x90, 0x98, 0x01, 0x77, 26 | 0x11, 0x9f, 0xd4, 0xb6, 0x76, 0xb3, 0x2d, 0x12, 0x92, 0x9d, 0x9b, 0x6c, 0x91, 0x90, 0xfe, 0x37, 27 | 0x3b, 0xdd, 0xb9, 0xd3, 0x2d, 0x12, 0xe4, 0x86, 0xc5, 0x9d, 0xb6, 0xb6, 0x3e, 0xdd, 0xa4, 0x65, 28 | 0x51, 0xdb, 0x7c, 0x33, 0x2f, 0xae, 0x9d, 0xaa, 0x0a, 0x3d, 0xa8, 0x34, 0x72, 0x3a, 0x87, 0x04, 29 | 0x44, 0x36, 0x85, 0xc8, 0x66, 0xc8, 0x01, 0x4c, 0x09, 0x81, 0x8b, 0x2c, 0x09, 0x7a, 0x16, 0x74, 30 | 0x02, 0x91, 0xa5, 0xf8, 0x33, 0x3a, 0x03, 0x90, 0x04, 0x27, 0x0d, 0xee, 0xe0, 0xa4, 0x44, 0x7f, 31 | 0x03, 0x00, 0x00, 0xff, 0xff, 0x09, 0xd4, 0x9e, 0xab, 0x71, 0x02, 0x00, 0x00, 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /s-press.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: PressSpace 17 | width: 31 18 | height: 1 19 | originx: 15 20 | originy: 1 21 | layer: 9 22 | glyphs: 23 | "P": 24 | background: color1 25 | foreground: color2 26 | display: "P" 27 | 28 | "R": 29 | background: color1 30 | foreground: color2 31 | display: "R" 32 | 33 | "E": 34 | background: color1 35 | foreground: color2 36 | display: "E" 37 | 38 | "S": 39 | background: color1 40 | foreground: color2 41 | display: "S" 42 | 43 | "S": 44 | background: color1 45 | foreground: color2 46 | display: "S" 47 | 48 | "<": 49 | background: color1 50 | foreground: color2 51 | display: "<" 52 | 53 | ">": 54 | background: color1 55 | foreground: color2 56 | display: ">" 57 | 58 | "C": 59 | background: color1 60 | foreground: color2 61 | display: "C" 62 | 63 | "T": 64 | background: color1 65 | foreground: color2 66 | display: "T" 67 | 68 | "O": 69 | background: color1 70 | foreground: color2 71 | display: "O" 72 | 73 | "N": 74 | background: color1 75 | foreground: color2 76 | display: "N" 77 | 78 | "I": 79 | background: color1 80 | foreground: color2 81 | display: "I" 82 | 83 | "U": 84 | background: color1 85 | foreground: color2 86 | display: "U" 87 | 88 | "_": 89 | background: color1 90 | foreground: color2 91 | display: " " 92 | 93 | palette: 94 | color1: black 95 | color2: fuchsia 96 | 97 | frames: 98 | - names: [ F0 ] 99 | data: [ "___PRESS__TO_CONTINUE___" ] 100 | -------------------------------------------------------------------------------- /s-complete.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x3b, 0x6f, 0x13, 0x41, 8 | 0x10, 0xc7, 0xe7, 0x7f, 0xbe, 0xb3, 0x49, 0x64, 0x84, 0xa0, 0x48, 0x43, 0x93, 0x14, 0x74, 0x11, 9 | 0x02, 0x0a, 0x3e, 0x40, 0xec, 0x33, 0x14, 0xe7, 0x87, 0x84, 0x65, 0x40, 0x16, 0x1a, 0xad, 0x92, 10 | 0xd1, 0x79, 0xc5, 0xbd, 0xb4, 0x5e, 0x1e, 0x2e, 0x28, 0x08, 0xe1, 0x15, 0x4c, 0x3e, 0x32, 0x8b, 11 | 0xee, 0xce, 0x8a, 0x2c, 0x6d, 0xb5, 0xff, 0xfd, 0xed, 0x68, 0xe6, 0xb7, 0xf3, 0xc5, 0x7d, 0xed, 12 | 0x00, 0x87, 0xaf, 0x2a, 0xa3, 0xad, 0x0c, 0x95, 0x55, 0x70, 0x97, 0x84, 0x03, 0x84, 0x13, 0x95, 13 | 0x0b, 0xfa, 0x84, 0xe8, 0xb5, 0xbe, 0xb0, 0x2b, 0x84, 0x84, 0xee, 0x4b, 0xd1, 0xe9, 0xca, 0xd6, 14 | 0xb1, 0x37, 0x35, 0x3a, 0xd5, 0xc5, 0x9b, 0xbd, 0xfc, 0xb6, 0xce, 0x51, 0xa2, 0x36, 0x62, 0x9a, 15 | 0xe2, 0x17, 0xd9, 0xa6, 0x5a, 0xad, 0xe1, 0x7e, 0x10, 0x7a, 0x33, 0x95, 0x89, 0xb5, 0x02, 0xf7, 16 | 0x8b, 0xd0, 0x1d, 0x19, 0x95, 0xcb, 0x1a, 0xee, 0x86, 0x88, 0x4e, 0xdd, 0xf7, 0x10, 0x78, 0x98, 17 | 0xab, 0x6a, 0xb9, 0xb6, 0x46, 0x17, 0xe9, 0xbb, 0x5c, 0xe9, 0xe2, 0x71, 0x6b, 0xd3, 0x74, 0x68, 18 | 0x1a, 0xf4, 0xe1, 0xae, 0x88, 0x9e, 0xbb, 0x6f, 0x1d, 0x04, 0xee, 0x8a, 0xd0, 0x41, 0x6f, 0xa8, 19 | 0xd7, 0x55, 0xa6, 0x36, 0xb5, 0xe1, 0xe1, 0xa8, 0x34, 0x92, 0x9a, 0xf2, 0x43, 0x71, 0xd1, 0x5c, 20 | 0xcf, 0xd4, 0xf9, 0xfb, 0xdb, 0x2b, 0xd1, 0x89, 0xfb, 0x19, 0x02, 0xf7, 0xf7, 0x66, 0xb4, 0x47, 21 | 0x63, 0xd3, 0xaf, 0x4b, 0x1e, 0xb9, 0xbf, 0x01, 0x70, 0xb4, 0xdc, 0x1f, 0xde, 0x58, 0xb6, 0xfb, 22 | 0xb8, 0x21, 0xb8, 0x6b, 0xa2, 0xa1, 0xfb, 0xdd, 0x01, 0xee, 0x79, 0xcf, 0xd7, 0x84, 0x10, 0xd1, 23 | 0xa4, 0xfd, 0xd4, 0x96, 0x10, 0x4e, 0xe4, 0xb3, 0xad, 0x45, 0xc2, 0xb9, 0xce, 0xa5, 0xde, 0x45, 24 | 0xd8, 0x16, 0x6e, 0x89, 0xe8, 0xc8, 0xfd, 0x09, 0x80, 0x3b, 0xcb, 0x5b, 0x87, 0x2d, 0xd5, 0x06, 25 | 0xff, 0x70, 0xea, 0x2e, 0x71, 0x37, 0x91, 0x8f, 0x92, 0x0d, 0xca, 0xbc, 0xca, 0xc4, 0x0a, 0xce, 26 | 0x10, 0xe0, 0x18, 0x01, 0x1e, 0xe0, 0x00, 0x53, 0x60, 0x8a, 0xee, 0x79, 0x99, 0x95, 0xe6, 0xd9, 27 | 0xee, 0x7c, 0x4a, 0x98, 0x01, 0x33, 0x9f, 0x8e, 0x81, 0xb1, 0x4f, 0xe7, 0xc0, 0xdc, 0xa7, 0x0b, 28 | 0x60, 0xe1, 0xd3, 0x18, 0x88, 0x7d, 0xca, 0xc0, 0xb1, 0x4f, 0x13, 0x20, 0xf1, 0xe9, 0x00, 0x18, 29 | 0xf8, 0x34, 0xd8, 0xa5, 0x28, 0x35, 0x22, 0xc5, 0xee, 0x39, 0xfa, 0xb4, 0xd2, 0x56, 0x00, 0x20, 30 | 0x18, 0x3d, 0xe9, 0xe0, 0x84, 0x99, 0x13, 0x8e, 0x79, 0xc1, 0x31, 0x27, 0xcc, 0x3c, 0xe0, 0x29, 31 | 0x8f, 0x79, 0xd6, 0xb0, 0x39, 0xc7, 0xcc, 0x4c, 0xf4, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x26, 0x4b, 32 | 0x65, 0x2c, 0xb5, 0x02, 0x00, 0x00, 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /l-level1.go: -------------------------------------------------------------------------------- 1 | // Level data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterLevelGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xc1, 0x6e, 0xd4, 0x30, 8 | 0x10, 0x86, 0x3d, 0x76, 0x92, 0x15, 0x68, 0x11, 0x8f, 0x50, 0x4e, 0xdc, 0x58, 0x79, 0x9c, 0x34, 9 | 0xda, 0x1c, 0x38, 0x44, 0xa0, 0x72, 0x41, 0x94, 0x3b, 0xe2, 0x60, 0x76, 0x87, 0x8d, 0x51, 0x62, 10 | 0x22, 0xc7, 0x4a, 0x37, 0x47, 0x4a, 0xe9, 0x63, 0x13, 0xe4, 0x24, 0x5b, 0xa9, 0x5d, 0x71, 0xf4, 11 | 0xcc, 0x37, 0x9e, 0xf9, 0xfe, 0xb7, 0xe3, 0x2f, 0x01, 0xf0, 0xec, 0x23, 0xf5, 0x54, 0xbf, 0xd7, 12 | 0x5e, 0xc3, 0x78, 0xcb, 0x40, 0x40, 0xf4, 0x49, 0x37, 0x04, 0x6b, 0x06, 0xcf, 0x3f, 0xbb, 0x9f, 13 | 0x2d, 0x39, 0x6f, 0xa8, 0x83, 0xf1, 0x8e, 0xc1, 0xea, 0xfa, 0xdb, 0x0f, 0xda, 0xf9, 0x0e, 0xc6, 14 | 0x7b, 0xc6, 0xd8, 0xc5, 0xf8, 0x3b, 0x02, 0x78, 0xf9, 0x41, 0x37, 0x34, 0xd7, 0x03, 0x3d, 0x83, 15 | 0x6b, 0x58, 0x33, 0x26, 0xc7, 0x3f, 0x11, 0xc0, 0x45, 0xa3, 0xdb, 0x2f, 0x9d, 0x77, 0xc6, 0x1e, 16 | 0xbe, 0x36, 0xda, 0xd8, 0xcd, 0x19, 0x7f, 0x1f, 0xf8, 0xf1, 0x8e, 0xb1, 0xbf, 0xfc, 0xf5, 0x78, 17 | 0x0b, 0x49, 0x1d, 0xce, 0x41, 0x48, 0x56, 0x07, 0xa7, 0x7b, 0xe3, 0x07, 0x81, 0x1b, 0x19, 0xdd, 18 | 0x18, 0xbb, 0x17, 0x72, 0x23, 0xe3, 0x1b, 0xb3, 0xf7, 0x95, 0x48, 0x95, 0x4c, 0x2a, 0x32, 0x87, 19 | 0xca, 0x0b, 0xa5, 0x64, 0xec, 0x8d, 0xaf, 0x69, 0xf6, 0x78, 0x75, 0x6d, 0x29, 0xf6, 0xa6, 0x21, 20 | 0xc7, 0x0b, 0x09, 0x2f, 0x78, 0x79, 0x19, 0xc1, 0x51, 0xa8, 0x42, 0xc2, 0xc0, 0x8b, 0x8c, 0xf7, 21 | 0x47, 0xae, 0xb2, 0x78, 0x57, 0xeb, 0xae, 0x4b, 0x74, 0x6d, 0xc8, 0x22, 0xbf, 0xc2, 0x78, 0x29, 22 | 0x54, 0xdf, 0xc9, 0xee, 0x28, 0xe0, 0x69, 0x1e, 0xf0, 0x74, 0xde, 0xc6, 0x15, 0x2e, 0xcb, 0x40, 23 | 0x25, 0x6d, 0xad, 0x07, 0x72, 0x62, 0x9e, 0x88, 0xba, 0xca, 0xb4, 0x70, 0x14, 0x98, 0x6e, 0x61, 24 | 0x10, 0x0a, 0x2f, 0x93, 0x83, 0xb6, 0xde, 0x0d, 0x4b, 0x7b, 0x79, 0x05, 0x40, 0xcd, 0x80, 0x8a, 25 | 0x5a, 0xbd, 0x57, 0x4b, 0x5b, 0xb4, 0x7a, 0x3f, 0x2d, 0xc3, 0xb0, 0x0c, 0x79, 0xa9, 0xa2, 0x47, 26 | 0x97, 0x85, 0xb9, 0xbc, 0x80, 0x81, 0xa3, 0xe2, 0xfd, 0x51, 0xbc, 0x41, 0xc9, 0xcb, 0xf4, 0x29, 27 | 0xc2, 0x51, 0xc2, 0x20, 0x70, 0x2b, 0x83, 0x1a, 0x66, 0xbc, 0xcc, 0x82, 0x2f, 0x6e, 0xf3, 0xa9, 28 | 0x9a, 0x4f, 0xd5, 0xfc, 0x89, 0xb0, 0x8a, 0x1f, 0x7c, 0xce, 0xcc, 0x65, 0x36, 0x1d, 0xba, 0x04, 29 | 0xcd, 0x33, 0xc9, 0xaf, 0xd2, 0xb3, 0x7c, 0x50, 0x06, 0x1d, 0xcc, 0xb6, 0x0f, 0xd4, 0xe9, 0xc3, 30 | 0x6c, 0xe5, 0xc9, 0x39, 0x6d, 0x2c, 0x9f, 0x67, 0x4e, 0xcf, 0xa4, 0x6b, 0x9d, 0xf1, 0xb4, 0x7a, 31 | 0xa7, 0x7b, 0x72, 0x16, 0x83, 0xfa, 0xe3, 0x14, 0x4e, 0x11, 0x16, 0xbc, 0xc4, 0xff, 0xc5, 0x30, 32 | 0xd9, 0x28, 0xc9, 0xfe, 0x05, 0x00, 0x00, 0xff, 0xff, 0x59, 0x53, 0xb2, 0x2b, 0xb8, 0x02, 0x00, 33 | 0x00, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /s-press.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xcf, 0x8b, 0xd3, 0x40, 8 | 0x14, 0xc7, 0xdf, 0x37, 0x49, 0x5b, 0x2d, 0x15, 0xd1, 0xc3, 0x5e, 0x04, 0x7f, 0x1c, 0xbc, 0x89, 9 | 0xa8, 0x07, 0x4f, 0xa5, 0x07, 0xdb, 0x54, 0x0b, 0x92, 0x84, 0x26, 0x45, 0xa5, 0xc8, 0x30, 0x66, 10 | 0xc7, 0x76, 0xd8, 0xa4, 0x09, 0x93, 0x59, 0xb0, 0x07, 0x0f, 0xbb, 0xae, 0xbf, 0xd6, 0xba, 0x7f, 11 | 0xb2, 0x23, 0x49, 0xca, 0x52, 0x98, 0xd3, 0xbc, 0xf7, 0x79, 0x8f, 0xf7, 0x3e, 0xf3, 0xbe, 0x9a, 12 | 0x33, 0x17, 0xe8, 0xc7, 0xa5, 0x92, 0x5a, 0x4c, 0xb8, 0xe6, 0x30, 0xe7, 0x84, 0x9b, 0xf0, 0x02, 13 | 0x9e, 0x0b, 0x0c, 0x08, 0x9d, 0x77, 0xf2, 0x58, 0xaf, 0xe1, 0x11, 0xba, 0x6f, 0x84, 0x5c, 0xad, 14 | 0x75, 0x1d, 0xf6, 0x42, 0x25, 0x57, 0x72, 0xf3, 0xfe, 0x20, 0xfe, 0x50, 0xc7, 0x9d, 0xb7, 0x7c, 15 | 0x2b, 0x54, 0xd3, 0xfc, 0x3a, 0xdb, 0x96, 0xeb, 0x0a, 0xe6, 0x07, 0xa1, 0x17, 0xf1, 0x4c, 0x68, 16 | 0x2d, 0x60, 0x7e, 0x11, 0xba, 0x53, 0xc5, 0x73, 0x51, 0xc1, 0x5c, 0x11, 0xd1, 0x13, 0xf3, 0xdd, 17 | 0x03, 0xee, 0xe5, 0xbc, 0x5c, 0x56, 0x5a, 0xc9, 0xcd, 0xea, 0x63, 0xce, 0xe5, 0xe6, 0x69, 0x6b, 18 | 0xd3, 0x4c, 0x68, 0x06, 0x0c, 0x60, 0x2e, 0x88, 0x5e, 0x9a, 0x6f, 0x2e, 0x1c, 0x73, 0x41, 0x70, 19 | 0xd1, 0x9b, 0xc8, 0xaa, 0xcc, 0xf8, 0xb6, 0x36, 0xec, 0x4f, 0x0b, 0x25, 0x56, 0xaa, 0x38, 0xdd, 20 | 0x1c, 0x37, 0xe9, 0x2b, 0x9e, 0x9e, 0x5c, 0xa7, 0x44, 0x8f, 0xcc, 0x4f, 0x0f, 0xb8, 0x73, 0xb0, 21 | 0xa3, 0x7d, 0x1a, 0x9b, 0x41, 0xdd, 0xf2, 0xd8, 0xfc, 0x75, 0x80, 0xa3, 0xe5, 0xe1, 0xf2, 0xc6, 22 | 0xb2, 0xbd, 0xc7, 0x15, 0xc1, 0x5c, 0x12, 0x4d, 0xcc, 0x6f, 0x17, 0xb8, 0x6d, 0x95, 0x2f, 0x09, 23 | 0x1e, 0x3a, 0x41, 0xfb, 0xa9, 0x1d, 0xc1, 0x0b, 0xc4, 0x17, 0x5d, 0x8b, 0x78, 0x89, 0xcc, 0x45, 24 | 0x7d, 0x0b, 0xaf, 0x6d, 0xdc, 0x11, 0xd1, 0x91, 0xf9, 0xe3, 0x00, 0x37, 0x96, 0xd7, 0x0e, 0x3b, 25 | 0xaa, 0x0d, 0xfe, 0xe1, 0xcc, 0x9c, 0xa3, 0x1f, 0x29, 0x51, 0x55, 0x71, 0xc9, 0x53, 0x81, 0x11, 26 | 0x1c, 0xdc, 0x87, 0x83, 0xbb, 0xb8, 0x85, 0x08, 0x88, 0xd0, 0x4d, 0x8b, 0xac, 0x50, 0x2f, 0xf6, 27 | 0xef, 0x73, 0xc2, 0x10, 0x18, 0xda, 0x74, 0x04, 0x8c, 0x6c, 0x1a, 0x00, 0x81, 0x4d, 0xe7, 0xc0, 28 | 0xdc, 0xa6, 0x3e, 0xe0, 0xdb, 0x34, 0x06, 0x62, 0x9b, 0x8e, 0x81, 0xb1, 0x4d, 0x13, 0x20, 0xb1, 29 | 0x69, 0x08, 0x84, 0x36, 0x9d, 0x01, 0x33, 0x9b, 0x2e, 0x80, 0x85, 0x4d, 0x19, 0xf0, 0xd0, 0xa6, 30 | 0xce, 0x9e, 0xf4, 0x3e, 0x9f, 0xa6, 0xeb, 0x4a, 0xf2, 0x7d, 0xa1, 0xf3, 0x29, 0xe3, 0xe9, 0x09, 31 | 0x00, 0x38, 0xd3, 0x67, 0x2e, 0x1e, 0x30, 0xc6, 0xa2, 0xb9, 0x1f, 0xc7, 0x6c, 0xe8, 0x07, 0x89, 32 | 0x3f, 0x1f, 0xb1, 0x24, 0x64, 0xe3, 0x30, 0x48, 0x66, 0xc1, 0xc2, 0x67, 0x8c, 0x11, 0xfd, 0x0f, 33 | 0x00, 0x00, 0xff, 0xff, 0x2b, 0xb4, 0x1e, 0x99, 0x0a, 0x03, 0x00, 0x00, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ESCAPE FROM PROXIMA 5 3 | --------------------- 4 | 5 | Version 0.1 6 | 7 | (For the in-game backstory, see story.txt.) 8 | 9 | To build it: ./rebuild.sh 10 | 11 | THIS GAME IS A WORK IN PROGRESS; PROBABLY OF LITTLE ACTUAL PLAYABLE VALUE 12 | OTHER THAN AS A NOVELTY. 13 | 14 | 15 | GAME PLAY VIDEO 16 | --------------- 17 | 18 | If you just want to see gameplay videos, check these out: 19 | 20 | 21 | * [Video 2, xterm-256color](https://www.youtube.com/watch?v=jNxKTCmY_bQ) 22 | * [Video 1, Legacy Terminals](https://www.youtube.com/watch?v=DiOPBBM7-Xc) 23 | 24 | 25 | More Info 26 | --------- 27 | 28 | Only a single game level is implemented so far. 29 | 30 | I built this intending to show off the capabilities of my Tcell package for 31 | Go (the library used for "terminal" graphics under the hood.) 32 | 33 | I got kind of caught up in it, and now there is a pretty powerful game engine 34 | under the hood. There is full support for animated sprites, with collision 35 | detection, layering, etc. And I've made some pretty cool visual effects too. 36 | 37 | It runs in a text window. You want a reasonably big window (80x25 will work, 38 | but its harder to play with smaller screens), and you'll want a fast display. 39 | 40 | You'll also want a color terminal -- 256 color support in xterm or Terminal 41 | is highly recommended. Oh, and if you can, try to use a UTF-8 locale. The 42 | game will work in basic ASCII with black and white, and it isn't completely 43 | garbage in that mode, but its *soo* much better with a more capable terminal. 44 | 45 | Oh, don't try to run this over a 300 baud modem. You won't be happy. 46 | 47 | Local terminals work fine. 9600 baud is probably pushing it. I haven't 48 | tested anything other than local connections; there are a lot of display 49 | updates in the game, so you do want to have a reasonably fast display. 50 | You can reduce the screen size if its too slow, that may help a little. 51 | 52 | Yes, it should run in Windows too. 53 | 54 | There is no sound (yet!) 55 | 56 | Much of the code here will probably get cleaned up and structured properly 57 | for reuse in nice library form. Probably sub-packages under Tcell, for both 58 | the dynamic views and probably also the sprite management code and maybe even 59 | the full game loop. 60 | 61 | 62 | Reusing Code & Assets 63 | --------------------- 64 | 65 | If you want to reuse any of the code or assets here, feel free, per the 66 | terms of the Apache 2 license. I would however appreciate an email 67 | letting me know how you're using this stuff -- I'm really hopeful that 68 | this work will inspire new creative efforts by others and I'm anxious 69 | to see what you create! 70 | 71 | There are no special requirements to do so, but if you do use this code, 72 | I'd appreciate a mention somewhere in your project, with a link back to 73 | this github repo. 74 | 75 | -------------------------------------------------------------------------------- /s-hfence.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x4f, 0x8b, 0x13, 0x41, 8 | 0x10, 0xc5, 0xeb, 0x75, 0xf7, 0x24, 0x6b, 0x08, 0x88, 0x97, 0xbd, 0x78, 0x70, 0x3d, 0x78, 0x13, 9 | 0x21, 0x51, 0x3c, 0x79, 0x10, 0x09, 0xe3, 0x1e, 0xc2, 0x8c, 0x44, 0xc1, 0x31, 0xc1, 0x43, 0x6f, 10 | 0xd2, 0x4c, 0x9a, 0x9d, 0x3f, 0xa1, 0x33, 0x0b, 0x26, 0xb2, 0x82, 0xeb, 0xfa, 0x6f, 0x8d, 0x8b, 11 | 0x17, 0xbf, 0x97, 0x9f, 0xc1, 0x93, 0x9f, 0x22, 0x2d, 0x3d, 0x33, 0x24, 0x81, 0x5c, 0x6a, 0x7e, 12 | 0xf5, 0xea, 0x51, 0xf5, 0xa6, 0xcf, 0xed, 0x47, 0x0e, 0xb4, 0x5e, 0xce, 0x8c, 0x2e, 0x54, 0x4f, 13 | 0x16, 0x12, 0xf6, 0x82, 0x70, 0x03, 0x22, 0x90, 0xa9, 0x42, 0x9b, 0xe0, 0xbd, 0xd6, 0x93, 0x62, 14 | 0x0a, 0x41, 0x68, 0x1c, 0x2b, 0x1d, 0x4f, 0x0b, 0x87, 0xcd, 0xd0, 0xe8, 0x58, 0x67, 0xd1, 0x0e, 15 | 0xbf, 0x71, 0xec, 0xf5, 0xe5, 0x42, 0x99, 0xd2, 0xfc, 0x3c, 0x59, 0xcc, 0xa6, 0x73, 0xd8, 0x2f, 16 | 0x84, 0xe6, 0x0b, 0x99, 0xa8, 0xa2, 0x50, 0xb0, 0xdf, 0x08, 0x0d, 0xdf, 0xc8, 0x54, 0xcd, 0x61, 17 | 0xaf, 0x89, 0xe8, 0xbe, 0xfd, 0x2c, 0x80, 0xdb, 0xa9, 0x9c, 0x8d, 0xe6, 0x85, 0xd1, 0x59, 0xfc, 18 | 0x36, 0x95, 0x3a, 0x7b, 0x50, 0xa5, 0x29, 0x37, 0x94, 0x0b, 0xda, 0xb0, 0x97, 0x44, 0x8f, 0xed, 19 | 0x27, 0x0e, 0x66, 0x2f, 0x09, 0x1c, 0xcd, 0x9e, 0x9e, 0xcf, 0x12, 0xb9, 0x70, 0x09, 0x5b, 0x7e, 20 | 0x6e, 0x54, 0x6c, 0xf2, 0xb3, 0x6c, 0x52, 0xb6, 0xcf, 0xe4, 0xf8, 0x74, 0xd3, 0x12, 0xdd, 0xb5, 21 | 0x5f, 0x05, 0x70, 0x6b, 0xe7, 0x46, 0xf5, 0x29, 0xd3, 0xb4, 0x9d, 0xe5, 0x9e, 0xfd, 0xc9, 0x80, 22 | 0xc3, 0xd1, 0xee, 0xf1, 0x32, 0x65, 0xf5, 0x1e, 0xd7, 0x04, 0x7b, 0x45, 0xd4, 0xb3, 0xdf, 0x39, 23 | 0x70, 0x73, 0x6f, 0x7c, 0x45, 0x10, 0xf0, 0x82, 0xea, 0xa7, 0x56, 0x04, 0x11, 0xa8, 0x77, 0x85, 24 | 0x0b, 0x22, 0x5e, 0xe9, 0x54, 0xb9, 0xb7, 0x10, 0x95, 0x71, 0x45, 0x44, 0x87, 0xf6, 0x07, 0x03, 25 | 0x0e, 0x46, 0x9b, 0x0c, 0x2b, 0x72, 0x09, 0xd6, 0x78, 0x62, 0x2f, 0xd0, 0x38, 0xf6, 0x55, 0x36, 26 | 0x56, 0x10, 0x60, 0x5c, 0xc0, 0xc3, 0x12, 0xf8, 0x80, 0xc6, 0x38, 0x4f, 0x72, 0xf3, 0xb0, 0xfe, 27 | 0x76, 0x08, 0x43, 0xf0, 0xbf, 0xbf, 0x7f, 0xd5, 0xfd, 0xa3, 0xad, 0xde, 0x07, 0xde, 0xd7, 0x5d, 28 | 0x77, 0xab, 0x0e, 0x80, 0xf3, 0x7d, 0x35, 0x02, 0xa2, 0x7d, 0x55, 0xd4, 0xe4, 0x9d, 0x24, 0x72, 29 | 0x7c, 0x5a, 0x8f, 0x45, 0x6c, 0xe4, 0xa2, 0x0e, 0xd1, 0x9a, 0xe4, 0x93, 0x58, 0x99, 0x93, 0xe4, 30 | 0x4c, 0xd5, 0xe7, 0x85, 0x63, 0x1c, 0x00, 0xac, 0xdf, 0xe7, 0xae, 0x90, 0xc3, 0xc8, 0x61, 0xe4, 31 | 0x70, 0x30, 0xe0, 0xae, 0x94, 0xe8, 0xd4, 0xc8, 0x21, 0x0f, 0x7d, 0x1f, 0x2c, 0x0c, 0xb0, 0xbe, 32 | 0xf3, 0x14, 0x60, 0x47, 0x47, 0x6e, 0x1c, 0x06, 0xe0, 0x61, 0xd0, 0xc1, 0x9a, 0xff, 0x03, 0xd8, 33 | 0x72, 0x59, 0xfa, 0x82, 0x8e, 0x2b, 0x5d, 0xac, 0x9b, 0x7f, 0x00, 0x36, 0x1c, 0x56, 0x62, 0xb7, 34 | 0xda, 0xb0, 0x71, 0xd2, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1b, 0x69, 0xfa, 0x95, 0xc5, 0x02, 35 | 0x00, 0x00, 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /s-exhaust.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xcd, 0x6f, 0xd3, 0x40, 8 | 0x10, 0xc5, 0xe7, 0x39, 0xeb, 0x38, 0xad, 0x22, 0x10, 0x1c, 0x7a, 0xe1, 0x52, 0x0e, 0xdc, 0xaa, 9 | 0x48, 0xf9, 0x10, 0x67, 0x84, 0x82, 0x01, 0xa9, 0x8a, 0x91, 0xa8, 0x04, 0x28, 0xe2, 0xb0, 0xa4, 10 | 0x2b, 0x67, 0x55, 0x3b, 0x8e, 0xd6, 0x5b, 0xd4, 0x1c, 0x38, 0x50, 0xca, 0x57, 0x09, 0xfd, 0x5b, 11 | 0x39, 0x71, 0xec, 0x20, 0xdb, 0xdb, 0xc4, 0xa8, 0xaa, 0x1a, 0x4b, 0xd6, 0xbc, 0x59, 0xbf, 0xf9, 12 | 0xed, 0xf3, 0xee, 0x27, 0xfe, 0xdc, 0x00, 0xb6, 0x5f, 0xcf, 0x8d, 0xb6, 0x6a, 0x28, 0xad, 0x04, 13 | 0x9f, 0x12, 0xb6, 0x20, 0x46, 0x32, 0x55, 0x68, 0x13, 0xfc, 0x37, 0xfa, 0xd0, 0x4e, 0x21, 0x08, 14 | 0xcd, 0x17, 0x4a, 0xc7, 0x53, 0x5b, 0xc8, 0x20, 0x32, 0x3a, 0xd6, 0xb3, 0xb7, 0x35, 0xfd, 0xae, 15 | 0xd0, 0xfe, 0xbe, 0x5c, 0x28, 0x53, 0x9a, 0x9f, 0x27, 0x8b, 0xf9, 0x34, 0x07, 0x7f, 0x23, 0x04, 16 | 0xaf, 0x64, 0xa2, 0xac, 0x55, 0xe0, 0x1f, 0x84, 0x66, 0x68, 0x64, 0xaa, 0x72, 0xf0, 0x05, 0x11, 17 | 0xed, 0xf1, 0x57, 0x01, 0x3c, 0x48, 0xe5, 0x7c, 0x9c, 0x5b, 0xa3, 0x67, 0xf1, 0xfb, 0x54, 0xea, 18 | 0x59, 0xa7, 0x4a, 0x53, 0x12, 0x4a, 0x40, 0x1b, 0x7c, 0x46, 0xf4, 0x98, 0xbf, 0x34, 0xe0, 0xf1, 19 | 0x19, 0xa1, 0x81, 0x60, 0xa8, 0xf3, 0x79, 0x22, 0x17, 0x45, 0xc2, 0xed, 0x30, 0x33, 0x2a, 0x36, 20 | 0xd9, 0xf1, 0xec, 0xb0, 0x6c, 0x9f, 0xca, 0xc9, 0xd1, 0xaa, 0x25, 0x7a, 0xc8, 0xdf, 0x05, 0x70, 21 | 0xaf, 0xb6, 0x47, 0x55, 0xca, 0x34, 0xed, 0xc2, 0xf2, 0x88, 0x7f, 0x7b, 0xc0, 0xce, 0xb8, 0xbe, 22 | 0x79, 0x99, 0xb2, 0x3a, 0x8f, 0x0b, 0x02, 0x9f, 0x13, 0x0d, 0xf9, 0x67, 0x03, 0xb8, 0x7b, 0xed, 23 | 0xf3, 0x39, 0x41, 0xc0, 0x1f, 0x55, 0x3f, 0xb5, 0x24, 0x88, 0x91, 0x3a, 0xb1, 0x45, 0x10, 0x71, 24 | 0xa0, 0x53, 0x55, 0x9c, 0x85, 0xa8, 0x8c, 0x4b, 0x22, 0xda, 0xe1, 0x5f, 0x1e, 0xd0, 0x1a, 0xaf, 25 | 0x32, 0x2c, 0xa9, 0x48, 0x70, 0x89, 0xbf, 0x7c, 0x8a, 0xe0, 0xd9, 0xc9, 0x54, 0x1e, 0xe7, 0x16, 26 | 0xf7, 0xd1, 0x42, 0xcb, 0x13, 0x10, 0xe8, 0x00, 0x1d, 0x34, 0x27, 0x59, 0x92, 0x99, 0x9e, 0xab, 27 | 0x5d, 0x42, 0x04, 0x44, 0xae, 0xeb, 0xaf, 0x57, 0x33, 0x20, 0xbb, 0xee, 0x7d, 0xb2, 0xf6, 0x0e, 28 | 0xd6, 0xab, 0xc2, 0xf9, 0x9a, 0xb9, 0x4e, 0x3e, 0x2a, 0xe3, 0x58, 0x77, 0x92, 0xe2, 0x82, 0xf3, 29 | 0x44, 0x5a, 0x15, 0x1b, 0xb5, 0x70, 0x53, 0x22, 0x36, 0xd2, 0xe9, 0xae, 0xff, 0x21, 0x91, 0x93, 30 | 0x23, 0xf8, 0x40, 0xb0, 0xff, 0x32, 0x3c, 0x88, 0xc2, 0x10, 0x2d, 0x27, 0xba, 0xe0, 0x25, 0xc4, 31 | 0xd6, 0xee, 0xd5, 0x73, 0x83, 0xea, 0xec, 0x96, 0x2f, 0xa1, 0x36, 0x78, 0xa5, 0x7a, 0xe0, 0x3f, 32 | 0xb7, 0x23, 0xb2, 0x12, 0x91, 0xd5, 0x11, 0xbd, 0x95, 0xea, 0xe3, 0x12, 0x7b, 0xb7, 0x32, 0x22, 33 | 0xc7, 0x88, 0x6a, 0x8c, 0xfe, 0x4a, 0x0d, 0x36, 0x64, 0x94, 0xf5, 0x3f, 0xc6, 0xc0, 0xdb, 0x68, 34 | 0xd2, 0x89, 0x88, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x29, 0x8c, 0xd3, 0xb9, 0x7f, 0x03, 35 | 0x00, 0x00, 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /s-tinyexplosion.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xcb, 0x6e, 0xd3, 0x40, 8 | 0x18, 0x85, 0xff, 0x33, 0x76, 0x1c, 0xa8, 0x02, 0x88, 0x4d, 0x37, 0xac, 0x58, 0xb0, 0x41, 0x08, 9 | 0x29, 0xcd, 0xe5, 0x01, 0x50, 0x30, 0x2c, 0x50, 0x42, 0x4b, 0xc5, 0x45, 0x11, 0x8b, 0x21, 0x1e, 10 | 0x39, 0xa3, 0xda, 0x1e, 0x6b, 0x3c, 0xa8, 0xf5, 0x02, 0x89, 0x96, 0x72, 0x2b, 0xa1, 0xe2, 0x19, 11 | 0x78, 0x1a, 0x9e, 0xa3, 0x0f, 0xc2, 0x54, 0x1e, 0x8f, 0x9a, 0x48, 0x5d, 0xcd, 0x7f, 0xbe, 0x39, 12 | 0x3a, 0xff, 0x99, 0xf9, 0x64, 0x8f, 0x03, 0x60, 0xeb, 0x55, 0xa9, 0xa5, 0x11, 0x13, 0x6e, 0x38, 13 | 0xec, 0x09, 0xe1, 0x26, 0xc2, 0x29, 0xcf, 0x05, 0x7a, 0x84, 0xce, 0x1b, 0x99, 0x98, 0x25, 0x42, 14 | 0x42, 0xf4, 0x5c, 0xc8, 0x74, 0x69, 0x9a, 0xb1, 0x3b, 0xd3, 0x32, 0x95, 0xc5, 0xdb, 0x8d, 0xf9, 15 | 0x5d, 0x33, 0x77, 0x5e, 0xf0, 0x5a, 0x68, 0x67, 0x7e, 0x96, 0xd5, 0xe5, 0xb2, 0x82, 0xfd, 0x46, 16 | 0xe8, 0xbe, 0xe4, 0x99, 0x30, 0x46, 0xc0, 0xfe, 0x20, 0x44, 0xb1, 0xe6, 0xb9, 0xa8, 0x60, 0xcf, 17 | 0x89, 0xe8, 0x91, 0xfd, 0x1a, 0x02, 0xf7, 0x72, 0x5e, 0xce, 0x2b, 0xa3, 0x65, 0x91, 0xbe, 0xcf, 18 | 0xb9, 0x2c, 0x1e, 0xb7, 0x6d, 0x5c, 0x82, 0x0b, 0xe8, 0xc1, 0x9e, 0x12, 0x8d, 0xed, 0x97, 0x00, 19 | 0xcc, 0x9e, 0x12, 0x02, 0x74, 0x27, 0xb2, 0x2a, 0x33, 0x5e, 0x37, 0x0d, 0xb7, 0x62, 0xa5, 0x45, 20 | 0xaa, 0xd5, 0xc7, 0x22, 0x71, 0xf2, 0x09, 0x5f, 0x1c, 0x5c, 0x49, 0xa2, 0xfb, 0xf6, 0x7b, 0x08, 21 | 0xdc, 0xdd, 0xd8, 0xd1, 0x1e, 0xae, 0x4d, 0xaf, 0xb1, 0x3c, 0xb0, 0xbf, 0x19, 0xb0, 0x3d, 0xdf, 22 | 0x5c, 0xee, 0x5a, 0xb6, 0xff, 0x71, 0x4e, 0xb0, 0x67, 0x44, 0x13, 0xfb, 0x33, 0x00, 0xee, 0x5c, 23 | 0xbb, 0x3e, 0x23, 0x84, 0xe8, 0x4c, 0xdb, 0x47, 0xad, 0x08, 0xe1, 0x54, 0x1c, 0x99, 0xa6, 0x48, 24 | 0xb8, 0x2f, 0x73, 0xd1, 0xfc, 0x45, 0xd8, 0x1a, 0x57, 0x44, 0xb4, 0x6d, 0x7f, 0x31, 0xe0, 0xc6, 25 | 0xfc, 0xaa, 0xc3, 0x8a, 0x9a, 0x06, 0xff, 0xf1, 0xda, 0x9e, 0xe0, 0xd6, 0xbe, 0x2c, 0xea, 0xa7, 26 | 0x47, 0x65, 0xa6, 0x2a, 0xa9, 0x0a, 0x30, 0xb0, 0xe0, 0x36, 0x22, 0x54, 0xc0, 0x43, 0x44, 0x0b, 27 | 0x95, 0x29, 0xbd, 0xe3, 0xcf, 0x3e, 0x41, 0x01, 0xca, 0xab, 0xc1, 0x9a, 0xce, 0x80, 0x99, 0x57, 28 | 0xc3, 0x35, 0xdd, 0x45, 0x70, 0x71, 0xfc, 0xc7, 0xeb, 0xd1, 0x9a, 0xef, 0x6d, 0xf2, 0xf1, 0x9a, 29 | 0x27, 0x08, 0x2e, 0x3e, 0xff, 0xbd, 0xbe, 0x33, 0xf2, 0xc9, 0x61, 0xaa, 0xb2, 0xc4, 0xa7, 0x45, 30 | 0x4a, 0xf3, 0x22, 0x15, 0x3e, 0x23, 0xd0, 0xc2, 0x5f, 0xf4, 0x3b, 0x1f, 0x32, 0xbe, 0x38, 0xf0, 31 | 0x19, 0x9d, 0xc3, 0xa5, 0x34, 0xde, 0x34, 0x88, 0x6a, 0x91, 0x65, 0xea, 0x10, 0x11, 0xc0, 0xe2, 32 | 0x3e, 0x58, 0xbc, 0x03, 0xfb, 0x0f, 0x40, 0x42, 0x70, 0x82, 0xc5, 0x03, 0x24, 0x00, 0x2a, 0xa7, 33 | 0x07, 0x60, 0xf1, 0x10, 0x76, 0x05, 0x40, 0x39, 0x30, 0x04, 0x8b, 0x47, 0x2d, 0x98, 0x39, 0x30, 34 | 0x02, 0x8b, 0xc7, 0x2d, 0xd8, 0x75, 0x60, 0xcc, 0x9c, 0xd8, 0x23, 0xba, 0x0c, 0x00, 0x00, 0xff, 35 | 0xff, 0xd2, 0x38, 0xe3, 0x99, 0xdf, 0x02, 0x00, 0x00, 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /f-bullet.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/gdamore/tcell" 19 | "time" 20 | ) 21 | 22 | type bullet struct { 23 | sprite *Sprite 24 | level *Level 25 | alarm *Alarm 26 | } 27 | 28 | func (o *bullet) destroy() { 29 | o.sprite.Hide() 30 | o.level.RemoveSprite(o.sprite) 31 | o.level.RemoveAlarm(o.alarm) 32 | } 33 | 34 | func (o *bullet) HandleEvent(ev tcell.Event) bool { 35 | switch ev := ev.(type) { 36 | case *EventSpriteAccelerate: 37 | if ev.s != o.sprite { 38 | return false 39 | } 40 | 41 | vx, _ := o.sprite.Velocity() 42 | if vx > 0 { 43 | o.sprite.SetFrame("H") 44 | } else { 45 | o.sprite.SetFrame("V") 46 | } 47 | case *EventSpriteMove: 48 | if ev.s != o.sprite { 49 | return false 50 | } 51 | 52 | x, y, _, _ := o.sprite.Bounds() 53 | w, h := o.level.Size() 54 | if x < 0 || y < 0 || x >= w || y >= h { 55 | o.destroy() 56 | } 57 | case *EventCollision: 58 | switch ev.Collider().Layer() { 59 | 60 | case LayerTerrain, LayerHazard, LayerPlayer, LayerExplosion: 61 | // Impact with most solid objects removes the shot. 62 | // The impacted object is responsible for painting 63 | // any explosive effect. 64 | o.destroy() 65 | } 66 | case *EventAlarm: 67 | o.destroy() 68 | } 69 | return false 70 | } 71 | 72 | func makeBullet(level *Level, props GameObjectProps) error { 73 | 74 | x := props.PropInt("x", 0) 75 | y := props.PropInt("y", 0) 76 | velx := props.PropFloat64("vx", 0) 77 | vely := props.PropFloat64("vy", -20.0) 78 | frame := props.PropString("frame", "V") 79 | sname := props.PropString("sprite", "Bullet") 80 | life := props.PropInt("lifetime", 2000) 81 | delay := props.PropInt("delay", 0) 82 | sprite := GetSprite(sname) 83 | o := &bullet{sprite: sprite, level: level} 84 | 85 | if life > 0 { 86 | a := level.AddAlarm(time.Millisecond*time.Duration(life), o) 87 | o.alarm = a 88 | } 89 | 90 | sprite.SetLayer(LayerShot) 91 | sprite.Watch(o) 92 | 93 | if delay != 0 { 94 | d := time.Duration(delay) * time.Millisecond 95 | sprite.SetFrame("") 96 | sprite.ScheduleFrame(frame, time.Now().Add(d)) 97 | } else { 98 | sprite.SetFrame(frame) 99 | } 100 | 101 | sprite.SetPosition(x, y) 102 | sprite.SetVelocity(velx, vely) 103 | level.AddSprite(sprite) 104 | return nil 105 | } 106 | 107 | func init() { 108 | RegisterGameObjectMaker("bullet", makeBullet) 109 | } 110 | -------------------------------------------------------------------------------- /s-ship.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xbd, 0x6e, 0x13, 0x4f, 8 | 0x14, 0xc5, 0xef, 0xd9, 0x9d, 0xb5, 0x93, 0xc8, 0xd2, 0x5f, 0xff, 0x26, 0x0d, 0xcd, 0x50, 0xd0, 9 | 0x21, 0x4a, 0xfa, 0x45, 0x1e, 0x27, 0x88, 0xd8, 0x44, 0x24, 0x28, 0x20, 0x8b, 0x62, 0x36, 0x3b, 10 | 0x5a, 0x8f, 0xec, 0xb5, 0xad, 0xd9, 0x0f, 0xe1, 0x82, 0x82, 0x10, 0xbe, 0x82, 0x49, 0x69, 0x3a, 11 | 0x4a, 0x1e, 0x07, 0xc1, 0x2b, 0xf0, 0x20, 0x0c, 0x9a, 0x59, 0xb0, 0x2c, 0xa7, 0xd9, 0xbd, 0xf7, 12 | 0xdc, 0xa3, 0x73, 0x7f, 0x73, 0x5f, 0xd9, 0xd7, 0x21, 0xb0, 0x77, 0x32, 0x37, 0xba, 0x54, 0x5d, 13 | 0x59, 0x4a, 0xd8, 0x0b, 0xc2, 0x2e, 0xd8, 0x40, 0xe6, 0x0a, 0x1d, 0x42, 0x74, 0xa6, 0xd3, 0x72, 14 | 0x04, 0x46, 0x68, 0x1d, 0x2a, 0x9d, 0x8d, 0x4a, 0x57, 0xb6, 0x1f, 0x1b, 0x9d, 0xe9, 0xe9, 0xb3, 15 | 0x8d, 0xfa, 0xb9, 0xab, 0xa3, 0x23, 0xb9, 0x50, 0xc6, 0x9b, 0x0f, 0x26, 0x8b, 0xf9, 0xa8, 0x80, 16 | 0x7d, 0x47, 0x68, 0x1f, 0xcb, 0x89, 0x2a, 0x4b, 0x05, 0xfb, 0x81, 0xd0, 0xea, 0x19, 0x99, 0xab, 17 | 0x02, 0xf6, 0x9a, 0x88, 0xee, 0xda, 0xb7, 0x0c, 0xb8, 0x95, 0xcb, 0xf9, 0xb0, 0x28, 0x8d, 0x9e, 18 | 0x66, 0x2f, 0x72, 0xa9, 0xa7, 0xf7, 0x1a, 0x1a, 0x9f, 0xe0, 0x03, 0x3a, 0xb0, 0x97, 0x44, 0xf7, 19 | 0xed, 0x9b, 0x10, 0x81, 0xbd, 0x24, 0x84, 0x68, 0x77, 0x75, 0x31, 0x9f, 0xc8, 0x85, 0x23, 0xdc, 20 | 0xeb, 0xcd, 0x8c, 0xca, 0xcc, 0xac, 0x9a, 0xa6, 0xbe, 0x7d, 0x20, 0xcf, 0xc7, 0xeb, 0x96, 0xe8, 21 | 0xb6, 0x7d, 0xcf, 0x80, 0xff, 0x37, 0x76, 0x34, 0x3f, 0x4f, 0xd3, 0x71, 0x96, 0x3b, 0xf6, 0x73, 22 | 0x00, 0xec, 0x0f, 0x37, 0x97, 0x7b, 0xca, 0xe6, 0x1e, 0xd7, 0x04, 0x7b, 0x45, 0xd4, 0xb5, 0x1f, 23 | 0x43, 0xe0, 0xbf, 0x1b, 0xe3, 0x2b, 0x02, 0x43, 0x34, 0x68, 0x1e, 0xb5, 0x24, 0xb0, 0x81, 0x7a, 24 | 0x59, 0x3a, 0x10, 0x76, 0xaa, 0x73, 0xe5, 0x6e, 0xc1, 0x1a, 0xe3, 0x92, 0x88, 0xf6, 0xed, 0xa7, 25 | 0x00, 0xd8, 0x19, 0xae, 0x19, 0x96, 0xe4, 0x08, 0x7e, 0xe3, 0x91, 0xbd, 0x00, 0x3b, 0x19, 0xe9, 26 | 0x39, 0xf6, 0xb0, 0x03, 0x16, 0x74, 0xd0, 0x46, 0x85, 0xe0, 0xe7, 0x37, 0xb4, 0x12, 0xe3, 0xcf, 27 | 0x1e, 0x25, 0x13, 0x79, 0x3e, 0x26, 0xc4, 0x5e, 0x65, 0xc9, 0x2c, 0x5d, 0xac, 0x35, 0x81, 0xe0, 28 | 0xfb, 0x57, 0xb4, 0x73, 0x69, 0xc6, 0x2e, 0xd5, 0x0f, 0x09, 0x7d, 0x04, 0x3f, 0x56, 0x37, 0xd4, 29 | 0x14, 0xe1, 0xaf, 0xd5, 0x97, 0xad, 0x80, 0xc4, 0x89, 0xab, 0x2d, 0xb1, 0x6e, 0x36, 0xa5, 0xd2, 30 | 0x8c, 0xd7, 0x1a, 0xfb, 0x8b, 0xb3, 0x9b, 0x49, 0x3d, 0x2d, 0x92, 0x99, 0x99, 0xf9, 0xf9, 0x8e, 31 | 0xfb, 0x64, 0x46, 0x2d, 0x7c, 0x42, 0xab, 0xd0, 0x93, 0x5a, 0x99, 0x7f, 0x9b, 0x43, 0xa3, 0x52, 32 | 0x84, 0x08, 0x82, 0xa7, 0xc7, 0x61, 0xef, 0xac, 0x1b, 0xb2, 0x88, 0xf3, 0x98, 0xf3, 0x88, 0xa7, 33 | 0x22, 0xe1, 0x51, 0x2c, 0xfa, 0x22, 0x8e, 0x78, 0x2c, 0x62, 0x4e, 0x00, 0x3b, 0x12, 0xbd, 0xd3, 34 | 0x2d, 0x47, 0x2d, 0xfa, 0xa2, 0x8a, 0x78, 0x2d, 0x2a, 0xe7, 0x88, 0x9e, 0x3c, 0x3c, 0x38, 0xdc, 35 | 0xb6, 0x54, 0xa2, 0x2f, 0xea, 0x88, 0x57, 0xa2, 0xe6, 0x44, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 36 | 0x91, 0x4e, 0x09, 0x2a, 0xd4, 0x02, 0x00, 0x00, 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /s-smexplosion.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xcd, 0x6e, 0xd3, 0x40, 8 | 0x14, 0x85, 0xef, 0x19, 0x4f, 0x12, 0x88, 0xa3, 0x22, 0x36, 0x11, 0x12, 0x9b, 0x61, 0xc1, 0x0e, 9 | 0x21, 0x41, 0x5b, 0xf6, 0x89, 0x07, 0xc3, 0x02, 0x05, 0xa4, 0x22, 0x01, 0x8a, 0x58, 0x8c, 0xc8, 10 | 0x28, 0x19, 0xe1, 0x9f, 0xc8, 0x31, 0xa2, 0x59, 0xb0, 0xa0, 0x94, 0xbf, 0x12, 0xba, 0x60, 0xc1, 11 | 0x33, 0xf1, 0x1c, 0xac, 0x79, 0x86, 0x0e, 0x9a, 0x31, 0x71, 0x9c, 0xa2, 0xde, 0x48, 0xb9, 0x67, 12 | 0xae, 0x8f, 0xbf, 0x7b, 0x3c, 0xef, 0xec, 0xfb, 0x00, 0xe8, 0x1e, 0xcc, 0x0b, 0x53, 0x6a, 0xa9, 13 | 0x4a, 0x05, 0x7b, 0x44, 0xb8, 0x0c, 0x3e, 0x52, 0xa9, 0x46, 0x8f, 0xd0, 0x7a, 0x66, 0x26, 0xe5, 14 | 0x0c, 0x9c, 0xd0, 0x7e, 0xa8, 0xcd, 0x74, 0x56, 0x3a, 0xd9, 0x79, 0x5c, 0x98, 0xa9, 0xc9, 0x9e, 15 | 0x37, 0xf4, 0x0b, 0xa7, 0x5b, 0x8f, 0xd4, 0x52, 0x17, 0xde, 0xfc, 0x20, 0x59, 0xce, 0x67, 0x0b, 16 | 0xd8, 0x4f, 0x84, 0xce, 0x13, 0x95, 0xe8, 0xb2, 0xd4, 0xb0, 0x5f, 0x08, 0xed, 0xb8, 0x50, 0xa9, 17 | 0x5e, 0xc0, 0x9e, 0x12, 0xd1, 0x2d, 0xfb, 0x91, 0x03, 0xd7, 0x53, 0x35, 0x1f, 0x2f, 0xca, 0xc2, 18 | 0x64, 0xd3, 0x97, 0xa9, 0x32, 0xd9, 0xed, 0x2a, 0x8d, 0x27, 0x78, 0x40, 0x0f, 0xf6, 0x98, 0xe8, 19 | 0x9e, 0xfd, 0x10, 0x80, 0xd9, 0x63, 0x42, 0x80, 0x8e, 0x34, 0x8b, 0x79, 0xa2, 0x96, 0x2e, 0x61, 20 | 0x37, 0xce, 0x0b, 0x3d, 0x2d, 0xf2, 0x37, 0xd9, 0xc4, 0x1f, 0x87, 0xea, 0xd5, 0xeb, 0xfa, 0x48, 21 | 0x74, 0xc3, 0x7e, 0xe6, 0xc0, 0xd5, 0xc6, 0x8e, 0xaa, 0xf9, 0x34, 0x3d, 0x67, 0xb9, 0x69, 0xbf, 22 | 0x33, 0xa0, 0x3f, 0x6e, 0x2e, 0xf7, 0x29, 0xab, 0xfb, 0x38, 0x25, 0xd8, 0x13, 0x22, 0x69, 0xbf, 23 | 0x06, 0xc0, 0x95, 0xff, 0x1e, 0x9f, 0x10, 0x38, 0x5a, 0xa3, 0xea, 0xa3, 0x56, 0x04, 0x3e, 0xd2, 24 | 0x87, 0xa5, 0x0b, 0xc2, 0x9f, 0x9a, 0x54, 0xbb, 0xbb, 0xe0, 0x95, 0x71, 0x45, 0x44, 0x7d, 0xfb, 25 | 0x8d, 0x01, 0x97, 0xc6, 0x75, 0x86, 0x15, 0xb9, 0x04, 0x67, 0xf8, 0x63, 0x8f, 0xb0, 0x73, 0x90, 26 | 0xaa, 0x24, 0xb9, 0x7f, 0x38, 0x4f, 0xf2, 0x85, 0xc9, 0x33, 0xf4, 0xd1, 0x45, 0x17, 0x1c, 0x3b, 27 | 0xe0, 0x88, 0x10, 0xfc, 0xfe, 0xf9, 0x03, 0x41, 0xa1, 0x27, 0x68, 0xe7, 0x85, 0xca, 0xa6, 0x9a, 28 | 0x20, 0x1b, 0x43, 0xf7, 0x47, 0x18, 0x54, 0x93, 0xf6, 0x52, 0x27, 0x49, 0xfe, 0x76, 0xdd, 0x09, 29 | 0xc3, 0x73, 0xf3, 0x7f, 0x04, 0xd6, 0x02, 0x58, 0x7c, 0x07, 0x2c, 0xbe, 0x0b, 0xbb, 0x42, 0x2b, 30 | 0x14, 0x42, 0x88, 0xc1, 0x60, 0xe0, 0x5a, 0xe8, 0xd5, 0xc0, 0x9f, 0xce, 0xe9, 0xa6, 0xa7, 0x2e, 31 | 0x82, 0xc7, 0xb0, 0x78, 0xb7, 0x46, 0x0d, 0x5d, 0x79, 0xdb, 0x70, 0x5d, 0x22, 0x1c, 0x6e, 0x6a, 32 | 0x6b, 0xde, 0xf0, 0x7b, 0xd4, 0x2e, 0x58, 0xbc, 0xb7, 0x49, 0x25, 0x44, 0x14, 0x35, 0x36, 0x46, 33 | 0x91, 0x14, 0xa1, 0x88, 0x2a, 0x19, 0x49, 0x19, 0xba, 0x16, 0x79, 0xe9, 0x3d, 0x4e, 0xc8, 0x35, 34 | 0x6a, 0x0f, 0x2c, 0xde, 0x87, 0xfd, 0x55, 0xa3, 0x84, 0xdc, 0x0a, 0x2f, 0x9b, 0x5a, 0x86, 0xc2, 35 | 0xbf, 0xe9, 0xa4, 0x9f, 0x4b, 0xd9, 0x40, 0xed, 0xb3, 0x33, 0x5c, 0xdb, 0x70, 0xc4, 0x85, 0x1c, 36 | 0xe1, 0x38, 0x5b, 0x73, 0xe9, 0x7f, 0x42, 0x10, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x44, 0x34, 37 | 0x81, 0xa0, 0x75, 0x03, 0x00, 0x00, 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /spritereg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "bytes" 19 | "compress/gzip" 20 | "encoding/gob" 21 | "strconv" 22 | "sync" 23 | "time" 24 | 25 | "github.com/gdamore/tcell" 26 | ) 27 | 28 | var spriteData map[string]*SpriteData 29 | var spriteDataL sync.Mutex 30 | 31 | func addSprite(data *SpriteData) { 32 | spriteDataL.Lock() 33 | if spriteData == nil { 34 | spriteData = make(map[string]*SpriteData) 35 | } 36 | spriteData[data.Name] = data 37 | spriteDataL.Unlock() 38 | } 39 | 40 | func RegisterSpriteGobZ(b []byte) { 41 | r, e := gzip.NewReader(bytes.NewBuffer(b)) 42 | if e != nil { 43 | panic("Malformed GZIP sprite data: " + e.Error()) 44 | } 45 | dec := gob.NewDecoder(r) 46 | data := &SpriteData{} 47 | if e := dec.Decode(data); e != nil { 48 | panic("Malformed GOB sprite data: " + e.Error()) 49 | } 50 | addSprite(data) 51 | } 52 | 53 | func GetSprite(name string) *Sprite { 54 | 55 | spriteDataL.Lock() 56 | data, ok := spriteData[name] 57 | spriteDataL.Unlock() 58 | if !ok { 59 | return nil 60 | } 61 | 62 | s := NewSprite(data.Width, data.Height) 63 | 64 | glyphs := make(map[byte]rune) 65 | styles := make(map[byte]tcell.Style) 66 | count := data.Width * data.Height 67 | 68 | for k, g := range data.Glyphs { 69 | st := tcell.StyleDefault 70 | fn := g.Foreground 71 | if cn, ok := data.Palette[fn]; ok { 72 | fn = cn 73 | } 74 | bn := g.Background 75 | if cn, ok := data.Palette[bn]; ok { 76 | bn = cn 77 | } 78 | 79 | fg := tcell.GetColor(fn) 80 | st = st.Foreground(fg) 81 | 82 | bg := tcell.GetColor(bn) 83 | st = st.Background(bg) 84 | 85 | glyphs[k[0]] = []rune(g.Display)[0] 86 | styles[k[0]] = st 87 | } 88 | 89 | for i, fr := range data.Frames { 90 | f := &spriteFrame{nextFrame: fr.Next} 91 | f.timer = time.Millisecond * time.Duration(fr.Time) 92 | f.runes = make([]rune, count) 93 | f.styles = make([]tcell.Style, count) 94 | for y, line := range fr.Data { 95 | for x := range line { 96 | c := line[x] 97 | i := y*data.Width + x 98 | if c == ' ' { 99 | continue 100 | } 101 | f.runes[i] = glyphs[c] 102 | f.styles[i] = styles[c] 103 | } 104 | } 105 | for _, name := range fr.Names { 106 | s.frames[name] = f 107 | } 108 | // Also insert a default based on index position 109 | name := strconv.Itoa(i) 110 | if _, ok := s.frames[name]; ok { 111 | s.frames[name] = f 112 | } 113 | s.frames[name] = f 114 | } 115 | s.SetOrigin(data.OriginX, data.OriginY) 116 | s.SetLayer(data.Layer) 117 | return s 118 | } 119 | -------------------------------------------------------------------------------- /leveldata.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | // LevelData describes the set up for the level. 18 | // 19 | // In order to keep this as flexible as possible, we use dictionaries of 20 | // name value pairs. The level has some initial properties that are 21 | // global to the level itself, and then a series of objects to create. 22 | // One of those objects must be a player object. 23 | // 24 | // Here are the properties we known about for the level (the Properties 25 | // member of the LevelData): 26 | // 27 | // title Printable title for level 28 | // terrain Name of terrain sprite. The terrain sprite is expected 29 | // to have a starting frame called "TERRAIN". This is 30 | // likely the only frame in the terrain sprite. 31 | // player Name of player object (found in Objects array) 32 | // time Timer in seconds for completion of the level. Use 33 | // 0 to create a non-expiring level. 34 | // gravity (cells/sec*sec) This value is added to Y velocity. 35 | // 36 | // Each game object may have the following standard properties. Different 37 | // classes of objects may have others. These are used for the Objects 38 | // member. 39 | // 40 | // label Label for the instance. This string value is set implictly 41 | // to the key of map, and it is not overridable. 42 | // class Name of the object class (string). This is used to obtain 43 | // an instance of the object, with MakeGameObject(). 44 | // Note that the detailed list of class-specific bheaviors 45 | // and properties, as well as any requirements, are part 46 | // of each class. For example, many classes require that 47 | // the sprites with which they are used meet certain 48 | // requirements. 49 | // sprite Name of the sprite to use (string). Each class provides 50 | // a default value here, but a different sprite can be 51 | // chosen with this. (Not all classes support changing 52 | // the sprite.) 53 | // x X position (int) (using the sprite's X origin) 54 | // y Y position (int) (using the sprite's Y origin) 55 | // vx X velocity (float64), cells/sec. Negative values are 56 | // motion to the left. 57 | // vy Y velocity (float64), cells/sec. Negative values are 58 | // motion up. 59 | // width Width of sprite in cells (int) if adjustable. 60 | // height Height of sprite in cells (int) if adjustable. 61 | // colorX Name of color (string) to substitute for given color 62 | // in the sprite's color map. 63 | // 64 | type LevelData struct { 65 | Name string 66 | Properties GameObjectProps 67 | Objects map[string]GameObjectProps 68 | } 69 | -------------------------------------------------------------------------------- /s-alien1.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xcf, 0x6b, 0x13, 0x41, 8 | 0x1c, 0xc5, 0xbf, 0x6f, 0x76, 0x32, 0xad, 0x35, 0x20, 0x5e, 0x7a, 0xf1, 0x32, 0x1e, 0x84, 0x04, 9 | 0xec, 0x86, 0xfe, 0xc0, 0x5b, 0x20, 0x96, 0x12, 0x3d, 0xd8, 0x2a, 0x51, 0xb0, 0x52, 0x65, 0x99, 10 | 0x34, 0xc3, 0x76, 0xe8, 0xee, 0x26, 0xec, 0x6e, 0x21, 0x39, 0x18, 0xb0, 0xd6, 0x5f, 0xb5, 0xf6, 11 | 0x4f, 0xee, 0xc8, 0xce, 0x26, 0xe9, 0x1a, 0xa1, 0xa7, 0xfd, 0xce, 0xdb, 0xcf, 0xf7, 0xbd, 0x37, 12 | 0xf3, 0xd9, 0x7e, 0xf1, 0x80, 0xb5, 0xb7, 0xa3, 0xd4, 0xe4, 0x7a, 0x4f, 0xe5, 0x0a, 0xf6, 0x9c, 13 | 0x70, 0x0f, 0xfc, 0x40, 0xc5, 0x1a, 0x75, 0x42, 0xed, 0xbd, 0x19, 0xe4, 0x27, 0xe0, 0x04, 0xf1, 14 | 0x52, 0x9b, 0xf0, 0x24, 0x2f, 0xc6, 0x95, 0xd7, 0xa9, 0x09, 0x4d, 0x72, 0x58, 0x99, 0x3f, 0x14, 15 | 0x73, 0xed, 0x95, 0x9a, 0xe8, 0xd4, 0xc1, 0x2f, 0xa2, 0xc9, 0xe8, 0x24, 0x83, 0xfd, 0x4e, 0x58, 16 | 0x79, 0xa3, 0x22, 0x9d, 0xe7, 0x1a, 0xf6, 0x27, 0x41, 0x74, 0x53, 0x15, 0xeb, 0x0c, 0xf6, 0x9a, 17 | 0x88, 0x9e, 0xda, 0x6f, 0x1c, 0x78, 0x14, 0xab, 0xd1, 0x51, 0x96, 0xa7, 0x26, 0x09, 0x3f, 0xc5, 18 | 0xca, 0x24, 0x7e, 0xd9, 0xc6, 0x39, 0x38, 0x83, 0x3a, 0xec, 0x05, 0xd1, 0x33, 0xfb, 0xd5, 0x03, 19 | 0xb3, 0x17, 0x04, 0x0f, 0x2b, 0x7b, 0x26, 0x1b, 0x45, 0x6a, 0x52, 0x34, 0x5c, 0xeb, 0x0e, 0x53, 20 | 0x1d, 0xa6, 0xc3, 0xb3, 0x64, 0xe0, 0x8e, 0xbb, 0xea, 0xf8, 0x74, 0x71, 0x24, 0x7a, 0x6c, 0x7f, 21 | 0x70, 0xe0, 0x61, 0x25, 0xa3, 0xfc, 0xb8, 0x36, 0xf5, 0x02, 0x79, 0x62, 0xff, 0x30, 0x60, 0xfd, 22 | 0xa8, 0x1a, 0xee, 0x5a, 0x96, 0xef, 0x71, 0x4d, 0xb0, 0x97, 0x44, 0x7b, 0xf6, 0x97, 0x07, 0x3c, 23 | 0xf8, 0xef, 0xf7, 0x25, 0x81, 0xa3, 0x76, 0x50, 0x5e, 0xea, 0x8a, 0xc0, 0x0f, 0xf4, 0x38, 0x2f, 24 | 0x8a, 0xf0, 0x77, 0x26, 0xd6, 0xc5, 0x5b, 0xf0, 0x12, 0xbc, 0x22, 0xa2, 0x75, 0xfb, 0x9b, 0x01, 25 | 0xab, 0x47, 0x8b, 0x0e, 0x57, 0x54, 0x34, 0xb8, 0x61, 0xbb, 0xf6, 0x1c, 0xe2, 0x79, 0x64, 0x74, 26 | 0xb2, 0x89, 0x3a, 0x84, 0x27, 0x70, 0x1f, 0x3e, 0xe0, 0x83, 0xe7, 0xca, 0x44, 0xa8, 0xf5, 0x23, 27 | 0x75, 0x7c, 0x4a, 0xd8, 0x00, 0x36, 0xc0, 0xfb, 0x26, 0xba, 0x95, 0x3a, 0x40, 0x07, 0x5c, 0x4f, 28 | 0x74, 0x06, 0xde, 0x1f, 0x0e, 0x26, 0x84, 0x7d, 0x20, 0x00, 0xcf, 0x4e, 0x4d, 0x32, 0x57, 0x5a, 29 | 0x40, 0x6b, 0x69, 0x6d, 0x0c, 0x7c, 0x5c, 0x92, 0x1a, 0x40, 0x63, 0xb6, 0x37, 0x97, 0x9a, 0x40, 30 | 0x73, 0x49, 0x6a, 0x03, 0xed, 0xa5, 0xc5, 0xe0, 0x36, 0x70, 0x2e, 0x4d, 0x9d, 0xf4, 0x0f, 0x55, 31 | 0x73, 0x25, 0xbd, 0x54, 0x0f, 0xdc, 0x9d, 0x78, 0x3f, 0x3a, 0xd3, 0x6e, 0x89, 0x47, 0x26, 0xd6, 32 | 0xae, 0x69, 0x2d, 0x4c, 0xb5, 0x4e, 0xdc, 0x9a, 0x18, 0xa6, 0x2a, 0x09, 0x35, 0x56, 0x01, 0xd6, 33 | 0xdd, 0x04, 0xeb, 0x6e, 0xe1, 0x86, 0x1d, 0xc2, 0x13, 0x52, 0x06, 0x41, 0x4b, 0x0a, 0xbf, 0xb1, 34 | 0xdf, 0x69, 0x4a, 0x21, 0xa5, 0x94, 0x63, 0x49, 0x70, 0x00, 0xeb, 0x6e, 0xcf, 0x20, 0x5f, 0x06, 35 | 0x41, 0x30, 0x15, 0xb2, 0x80, 0xa6, 0x0e, 0x92, 0x25, 0xb4, 0x0d, 0xd6, 0xdd, 0xa9, 0x38, 0x05, 36 | 0x33, 0xa7, 0x76, 0x15, 0xda, 0x71, 0x91, 0x77, 0x3b, 0xf5, 0x36, 0xc1, 0x7a, 0x8b, 0x4e, 0xe3, 37 | 0x20, 0x90, 0x52, 0xc8, 0x46, 0x67, 0xbf, 0xe9, 0x0b, 0xd9, 0x5a, 0x40, 0x5b, 0x60, 0xbd, 0x79, 38 | 0xa7, 0x69, 0x91, 0xe6, 0x8b, 0x69, 0x01, 0xc9, 0xaa, 0xd3, 0x36, 0x58, 0x6f, 0xd1, 0x29, 0x70, 39 | 0x4e, 0xed, 0x99, 0xd3, 0x2d, 0xb4, 0xe3, 0x22, 0xef, 0x70, 0xa2, 0xbf, 0x01, 0x00, 0x00, 0xff, 40 | 0xff, 0x3f, 0x98, 0xd1, 0xa3, 0xcb, 0x03, 0x00, 0x00, 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /s-alien1.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: Alien1 16 | width: 6 17 | height: 3 18 | layer: 3 19 | glyphs: 20 | "_": 21 | background: black 22 | foreground: skin 23 | display: "_" 24 | 25 | "@": 26 | background: body 27 | foreground: eyes 28 | display: "@" 29 | "M": 30 | background: body 31 | foreground: skin 32 | display: "_" 33 | 34 | "/": 35 | background: black 36 | foreground: bill 37 | display: "/" 38 | "x": 39 | background: black 40 | foreground: bill 41 | display: "\\" 42 | "(": 43 | background: black 44 | foreground: skin 45 | display: "(" 46 | ")": 47 | background: black 48 | foreground: skin 49 | display: ")" 50 | "=": 51 | background: black 52 | foreground: bill 53 | display: "=" 54 | "-": 55 | background: black 56 | foreground: bill 57 | display: "-" 58 | "~": 59 | background: black 60 | foreground: bill 61 | display: "_" 62 | ".": 63 | background: black 64 | foreground: tail 65 | display: "." 66 | palette: 67 | eyes: red 68 | tail: blue 69 | skin: lime 70 | body: green 71 | bill: orange 72 | 73 | frames: 74 | - names: [ F1 ] 75 | next: F2 76 | time: 300 77 | data: 78 | - " __/ " 79 | - ".(M@) " 80 | - " x " 81 | 82 | - names: [ F2 ] 83 | next: F3 84 | time: 300 85 | data: 86 | - ". ___~" 87 | - " (M@)~" 88 | - " " 89 | 90 | - names: [ F3 ] 91 | next: F4 92 | time: 300 93 | data: 94 | - " ___ " 95 | - ".(M@)=" 96 | - " " 97 | 98 | - names: [ F4 ] 99 | next: F1 100 | time: 300 101 | data: 102 | - ". ___~" 103 | - " (M@)~" 104 | - " " 105 | 106 | - names: [ R1 ] 107 | next: R2 108 | time: 300 109 | data: 110 | - " x__ " 111 | - " (@M)." 112 | - " / " 113 | 114 | - names: [ R2 ] 115 | next: R3 116 | time: 300 117 | data: 118 | - "~___ ." 119 | - "~(@M) " 120 | - " " 121 | 122 | - names: [ R3 ] 123 | next: R4 124 | time: 300 125 | data: 126 | - " ___ " 127 | - "=(@M)." 128 | - " " 129 | 130 | - names: [ R4 ] 131 | next: R1 132 | time: 300 133 | data: 134 | - "~___ ." 135 | - "~(@M) " 136 | - " " 137 | -------------------------------------------------------------------------------- /collider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | ) 20 | 21 | type Collider interface { 22 | // Collides returns true if the Collider would collide with an 23 | // object at the given coordinates. 24 | Collides(x, y int) bool 25 | 26 | // Bounds is used to report the boundary for the Collider. We 27 | // make use of this to optimize our checks, so we begin looking 28 | // at the more tightly constrained object first. We also are 29 | // able to quickly eliminate two objects that do not overlap. 30 | // If an object never collides with anything, it sould specify 31 | // the value -1 for these coordinates. Nothing collides at any -1 32 | // coordinate, even with other objects also at -1 coordinates. 33 | Bounds() (x1, y1, x2, y2 int) 34 | 35 | // GetLayer returns the layer that the collider exists at. 36 | // This is mostly useful to class collisions as a group, e.g. 37 | // all hazards can be set to LayerHazard. 38 | Layer() int 39 | 40 | EventHandler 41 | } 42 | 43 | // CollidersCollide returns true if the two colliders collide. 44 | func CollidersCollide(a, b Collider) bool { 45 | ax1, ay1, ax2, ay2 := a.Bounds() 46 | bx1, by1, bx2, by2 := b.Bounds() 47 | 48 | // Clip to the intersection of the two objects, and check 49 | // to make sure we still have overlap in each dimension. 50 | 51 | if bx1 > ax1 { 52 | ax1 = bx1 53 | } 54 | if bx2 < ax2 { 55 | ax2 = bx2 56 | } 57 | if ax1 > ax2 { 58 | return false 59 | } 60 | 61 | if by1 > ay1 { 62 | ay1 = by1 63 | } 64 | if by2 < ay2 { 65 | ay2 = by2 66 | } 67 | if ay1 > ay2 { 68 | return false 69 | } 70 | 71 | for y := ay1; y <= ay2; y++ { 72 | for x := ax1; x <= ax2; x++ { 73 | if a.Collides(x, y) && b.Collides(x, y) { 74 | return true 75 | } 76 | } 77 | } 78 | return false 79 | } 80 | 81 | // EventCollision is delivered to a Collider when a collision with another 82 | // object is detected. Both Colliders will receive an event, but it will 83 | // not be the same event, since they see each other's collision. 84 | type EventCollision struct { 85 | when time.Time 86 | collider Collider 87 | target Collider 88 | } 89 | 90 | // When returns the time when the collision occurred. 91 | func (ev *EventCollision) When() time.Time { 92 | return ev.when 93 | } 94 | 95 | // Collider returns the object impacting the notified object. 96 | func (ev *EventCollision) Collider() Collider { 97 | return ev.collider 98 | } 99 | 100 | // Target returns the object that is being hit. 101 | func (ev *EventCollision) Target() Collider { 102 | return ev.target 103 | } 104 | 105 | // HandleCollision generates two EventCollisions, one for the each Collider, 106 | // and deliveres them to the two Colliders. 107 | func HandleCollision(c1, c2 Collider) { 108 | when := time.Now() 109 | ev1 := &EventCollision{when: when, collider: c2, target: c1} 110 | ev2 := &EventCollision{when: when, collider: c1, target: c2} 111 | 112 | c1.HandleEvent(ev1) 113 | c2.HandleEvent(ev2) 114 | } 115 | -------------------------------------------------------------------------------- /s-thrust.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x50, 0x4b, 0x4f, 0xdb, 0x4c, 8 | 0x14, 0xbd, 0xc7, 0x1e, 0x9b, 0x0f, 0x14, 0xe9, 0x53, 0x37, 0x6c, 0xba, 0x71, 0x17, 0xdd, 0xa1, 9 | 0x4a, 0xa4, 0x8f, 0x15, 0x62, 0x51, 0xb9, 0x40, 0xa5, 0x28, 0x44, 0x34, 0x16, 0xad, 0x50, 0x91, 10 | 0x06, 0x3c, 0x4d, 0x2c, 0x9c, 0x87, 0x26, 0x4e, 0xda, 0x2c, 0xba, 0x28, 0x8f, 0x3e, 0x68, 0xca, 11 | 0x1f, 0xcb, 0xdf, 0xe9, 0x9a, 0xa9, 0x66, 0x6c, 0xc2, 0x10, 0x13, 0xb5, 0x5e, 0xdc, 0x7b, 0xe6, 12 | 0xfa, 0x9c, 0x33, 0xf7, 0xcc, 0x67, 0xf5, 0xc5, 0x05, 0x56, 0xde, 0xf4, 0x65, 0x92, 0x89, 0x90, 13 | 0x67, 0x1c, 0xea, 0x94, 0xb0, 0x0c, 0x56, 0xe7, 0x1d, 0x81, 0x0a, 0xc1, 0xdb, 0x4f, 0xe2, 0xac, 14 | 0x0d, 0x46, 0xf0, 0x77, 0x44, 0xd2, 0x6a, 0x67, 0x1a, 0x2e, 0xed, 0xca, 0xa4, 0x95, 0x74, 0xdf, 15 | 0x5a, 0xf8, 0x9d, 0xc6, 0x5e, 0x8d, 0x8f, 0x85, 0x34, 0xe4, 0xed, 0x74, 0xdc, 0x6f, 0x0f, 0xa0, 16 | 0xbe, 0x12, 0x96, 0x1a, 0x3c, 0x15, 0x59, 0x26, 0xa0, 0xbe, 0x13, 0xfc, 0x2d, 0xc9, 0x3b, 0x62, 17 | 0x00, 0x75, 0x45, 0x44, 0x6b, 0xea, 0x82, 0x01, 0x0f, 0x3b, 0xbc, 0x7f, 0x30, 0xc8, 0x64, 0xd2, 18 | 0x6d, 0xbd, 0xef, 0xf0, 0xa4, 0xfb, 0x24, 0xdf, 0xc6, 0x38, 0x18, 0x83, 0x0a, 0xd4, 0x39, 0xd1, 19 | 0x0b, 0x75, 0xe6, 0xc2, 0x51, 0xe7, 0x04, 0x17, 0x4b, 0x61, 0x32, 0xe8, 0xa7, 0x7c, 0xac, 0x37, 20 | 0x5c, 0xd9, 0xea, 0x49, 0xd1, 0x92, 0xbd, 0x61, 0x37, 0x36, 0xc7, 0x97, 0xfc, 0xf8, 0x64, 0x76, 21 | 0x24, 0x7a, 0xa4, 0xbe, 0x31, 0xe0, 0x81, 0x75, 0x47, 0xde, 0xcc, 0x36, 0x15, 0x4d, 0x79, 0xac, 22 | 0x7e, 0x39, 0xc0, 0xea, 0x81, 0x7d, 0xb9, 0xd9, 0x32, 0x7f, 0x8f, 0x2b, 0x82, 0xba, 0x24, 0x0a, 23 | 0xd5, 0x0f, 0x17, 0xf8, 0xbf, 0xf4, 0xfb, 0x92, 0xc0, 0xe0, 0xd5, 0xf3, 0x50, 0x13, 0x02, 0xab, 24 | 0x8b, 0x4f, 0x99, 0x5e, 0x84, 0x35, 0x93, 0x8e, 0xd0, 0x6f, 0xc1, 0x72, 0xe2, 0x84, 0x88, 0x56, 25 | 0xd5, 0x4f, 0x07, 0xf8, 0xef, 0x60, 0xb6, 0xc3, 0x84, 0xf4, 0x06, 0xd7, 0x8c, 0xd4, 0x29, 0xfc, 26 | 0x66, 0x5b, 0x0e, 0x07, 0x19, 0x56, 0x50, 0x01, 0x73, 0x18, 0x2a, 0x38, 0x84, 0x33, 0x3d, 0x87, 27 | 0x7f, 0xdc, 0x4b, 0x7b, 0xb2, 0x5a, 0xf4, 0x75, 0x42, 0x04, 0x67, 0x7a, 0x51, 0x1c, 0x9f, 0xdd, 28 | 0x8e, 0x43, 0x8b, 0x6d, 0x8d, 0x37, 0xe0, 0x4c, 0x4f, 0xcb, 0x26, 0x9b, 0x70, 0xa6, 0x67, 0xe5, 29 | 0xf1, 0xc8, 0xf2, 0xb6, 0xc6, 0xb1, 0xe5, 0xfd, 0xf4, 0x76, 0x5c, 0xb3, 0xbc, 0xad, 0x2b, 0xf7, 30 | 0x2c, 0x6f, 0x6b, 0x9c, 0x5a, 0x6c, 0xcb, 0x44, 0x5a, 0x6c, 0x6b, 0x3c, 0xb4, 0x36, 0xb1, 0xc6, 31 | 0x5e, 0x61, 0xeb, 0x4a, 0x11, 0xe7, 0xf0, 0xf9, 0xf2, 0x87, 0x44, 0x8a, 0x23, 0x99, 0x1c, 0x9f, 32 | 0x14, 0x2c, 0xef, 0x28, 0xe5, 0x37, 0x87, 0xaa, 0x3f, 0x16, 0x69, 0xda, 0xfb, 0x58, 0x18, 0xf9, 33 | 0x3d, 0xc9, 0xbb, 0x2d, 0x81, 0x0a, 0xc0, 0xc2, 0xdd, 0xfd, 0x3a, 0x3c, 0x5d, 0xd7, 0x71, 0x8d, 34 | 0x35, 0xf8, 0x5e, 0x70, 0x18, 0x1c, 0x06, 0x5e, 0xa0, 0xbf, 0x45, 0x95, 0x00, 0x6f, 0xef, 0xf5, 35 | 0xf6, 0x4e, 0x13, 0xbe, 0x69, 0x33, 0xa9, 0xa1, 0x6c, 0x58, 0xf4, 0x8d, 0x7b, 0xa4, 0xac, 0xf6, 36 | 0x6a, 0xab, 0x09, 0x4f, 0xd7, 0xbb, 0x42, 0x5d, 0x36, 0x17, 0x60, 0x2d, 0x74, 0xa2, 0x06, 0xdc, 37 | 0xa8, 0x51, 0x16, 0xcd, 0xd5, 0x51, 0x30, 0xd2, 0x78, 0x94, 0x2f, 0x9a, 0x67, 0x33, 0xad, 0x8a, 38 | 0x6b, 0xfc, 0xd6, 0xca, 0x38, 0x88, 0xff, 0x1e, 0xf1, 0x26, 0x5b, 0xde, 0x67, 0x5a, 0xc3, 0x49, 39 | 0x2d, 0x7e, 0x7a, 0xdf, 0xf3, 0xe4, 0xe9, 0x4c, 0xbb, 0xab, 0xd4, 0x45, 0x2e, 0xc0, 0x5a, 0x69, 40 | 0x02, 0xba, 0x51, 0xa3, 0xac, 0x9a, 0xab, 0xc3, 0x60, 0xa8, 0xf1, 0xf0, 0x36, 0x65, 0xd5, 0x89, 41 | 0xb5, 0x20, 0x0c, 0xc2, 0x7f, 0x0d, 0x57, 0x75, 0xd4, 0x64, 0x76, 0x47, 0xcd, 0xa2, 0xd5, 0x16, 42 | 0x65, 0xba, 0xa3, 0xd0, 0x65, 0x6f, 0x01, 0x2e, 0xb2, 0x94, 0xf8, 0x73, 0x35, 0x0a, 0x22, 0x8d, 43 | 0xa3, 0x20, 0x20, 0xfa, 0x13, 0x00, 0x00, 0xff, 0xff, 0xea, 0x35, 0x9c, 0x13, 0x89, 0x05, 0x00, 44 | 0x00, 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /spritemgr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/gdamore/tcell" 21 | "github.com/gdamore/tcell/views" 22 | ) 23 | 24 | type SpriteManager struct { 25 | width int 26 | height int 27 | maxlayer int 28 | minlayer int 29 | view *views.ViewPort 30 | sprites map[int]map[*Sprite]struct{} 31 | } 32 | 33 | func NewSpriteManager(width, height int) *SpriteManager { 34 | mgr := &SpriteManager{} 35 | mgr.sprites = make(map[int]map[*Sprite]struct{}) 36 | mgr.width = width 37 | mgr.height = height 38 | return mgr 39 | } 40 | 41 | func (m *SpriteManager) forAllReverse(fn func(*Sprite)) { 42 | for layer := m.maxlayer; layer >= m.minlayer; layer-- { 43 | if sprites, ok := m.sprites[layer]; ok { 44 | for s := range sprites { 45 | fn(s) 46 | } 47 | } 48 | } 49 | } 50 | 51 | func (m *SpriteManager) forAll(fn func(*Sprite)) { 52 | for layer := m.minlayer; layer <= m.maxlayer; layer++ { 53 | if sprites, ok := m.sprites[layer]; ok { 54 | for s := range sprites { 55 | fn(s) 56 | } 57 | } 58 | } 59 | } 60 | 61 | func (m *SpriteManager) Update(now time.Time) { 62 | 63 | m.forAll(func(s *Sprite) { 64 | s.Update(now) 65 | }) 66 | 67 | // Check for collisions, and report them 68 | // Note: because we report the collision *after* a move has occurred, 69 | // any corrective move (e.g. reverse position and move back) needs to 70 | // be done in the event handlers. 71 | 72 | // We go in reverse order, because higher layers get more precedence. 73 | // This way a sprite sitting on top of the terrain gets its handling 74 | // done first. 75 | 76 | sprites := make(map[*Sprite]struct{}) 77 | m.forAllReverse(func(s1 *Sprite) { 78 | sprites[s1] = struct{}{} 79 | m.forAllReverse(func(s2 *Sprite) { 80 | if _, ok := sprites[s2]; !ok { 81 | if CollidersCollide(s1, s2) { 82 | HandleCollision(s1, s2) 83 | } 84 | } 85 | }) 86 | }) 87 | } 88 | 89 | func (m *SpriteManager) SetView(v *views.ViewPort) { 90 | m.view = v 91 | v.SetContentSize(m.width, m.height, true) 92 | } 93 | 94 | func (m *SpriteManager) Draw(v views.View) { 95 | 96 | v.Clear() 97 | 98 | m.forAll(func(s *Sprite) { s.Draw(v) }) 99 | } 100 | 101 | func (m *SpriteManager) Size() (int, int) { 102 | return m.width, m.height 103 | } 104 | 105 | func (m *SpriteManager) HandleEvent(ev tcell.Event) bool { 106 | 107 | for layer := m.minlayer; layer <= m.maxlayer; layer++ { 108 | if sprites, ok := m.sprites[layer]; ok { 109 | for s := range sprites { 110 | if s.HandleEvent(ev) { 111 | return true 112 | } 113 | } 114 | } 115 | } 116 | return false 117 | } 118 | 119 | func (m *SpriteManager) AddSprite(s *Sprite) { 120 | 121 | layer := s.Layer() 122 | if m.sprites[layer] == nil { 123 | m.sprites[layer] = make(map[*Sprite]struct{}) 124 | } 125 | m.sprites[layer][s] = struct{}{} 126 | if layer < m.minlayer { 127 | m.minlayer = layer 128 | } 129 | if layer > m.maxlayer { 130 | m.maxlayer = layer 131 | } 132 | } 133 | 134 | func (m *SpriteManager) RemoveSprite(s *Sprite) { 135 | delete(m.sprites[s.Layer()], s) 136 | } 137 | 138 | func (m *SpriteManager) Reset() { 139 | m.forAll(m.RemoveSprite) 140 | } 141 | -------------------------------------------------------------------------------- /s-gantry.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0xcd, 0x4e, 0x13, 0x61, 8 | 0x18, 0x85, 0xdf, 0x33, 0x7f, 0xc5, 0xa6, 0xf1, 0x27, 0x31, 0x6c, 0xdc, 0xd4, 0x85, 0x3b, 0xe3, 9 | 0x04, 0x05, 0xd6, 0xa0, 0x38, 0x74, 0x61, 0xa8, 0x94, 0x26, 0xd6, 0xa0, 0x99, 0x7c, 0xd0, 0x8f, 10 | 0xe9, 0x84, 0xf9, 0x29, 0xd3, 0x21, 0x3a, 0xc9, 0x98, 0x28, 0xe2, 0x1f, 0x22, 0x89, 0x89, 0xe1, 11 | 0x02, 0xbc, 0x1a, 0x97, 0x5e, 0x03, 0x2b, 0xbd, 0x09, 0x3e, 0xf3, 0xcd, 0xd0, 0x61, 0xe2, 0xc0, 12 | 0xae, 0xd3, 0x45, 0xcf, 0x9c, 0xbe, 0x39, 0xcf, 0x79, 0xdf, 0xf4, 0x8d, 0x78, 0xa7, 0x02, 0xf5, 13 | 0xb5, 0x61, 0xe4, 0xc6, 0x7c, 0x89, 0xc5, 0x0c, 0x62, 0x8f, 0x70, 0x05, 0xda, 0x0a, 0xf3, 0x39, 14 | 0x1a, 0x04, 0xfd, 0x99, 0xdb, 0x8f, 0x07, 0xd0, 0x08, 0x46, 0x8b, 0xbb, 0xce, 0x20, 0x96, 0xb2, 15 | 0xd6, 0x8e, 0x5c, 0xc7, 0x0d, 0x7a, 0x25, 0xfd, 0x5c, 0x6a, 0xfd, 0x09, 0x4b, 0x78, 0x94, 0x0d, 16 | 0x2f, 0x7b, 0xc9, 0x70, 0x30, 0x82, 0xf8, 0x48, 0xa8, 0x3d, 0x65, 0x1e, 0x8f, 0x63, 0x0e, 0xf1, 17 | 0x99, 0x60, 0x58, 0x11, 0xf3, 0xf9, 0x08, 0xe2, 0x88, 0x88, 0xee, 0x8a, 0x0f, 0x1a, 0x70, 0xcb, 18 | 0x67, 0xc3, 0xf5, 0x51, 0x1c, 0xb9, 0x81, 0xf3, 0xd2, 0x67, 0x6e, 0x70, 0x2f, 0x6f, 0x93, 0x25, 19 | 0x64, 0x01, 0x0d, 0x88, 0x7d, 0xa2, 0x79, 0xf1, 0x5e, 0x85, 0x22, 0xf6, 0x09, 0x2a, 0x6a, 0x4b, 20 | 0xee, 0x68, 0xe8, 0xb1, 0x44, 0x36, 0xac, 0x5b, 0x61, 0xc4, 0x9d, 0x28, 0xdc, 0x0d, 0xfa, 0xd9, 21 | 0xeb, 0x43, 0xb6, 0xb9, 0x5d, 0xbc, 0x12, 0xdd, 0x16, 0x9f, 0x34, 0xe0, 0x46, 0x89, 0x91, 0x7f, 22 | 0x65, 0x6d, 0x1a, 0x72, 0xe4, 0x8e, 0xf8, 0xa6, 0x00, 0xd3, 0xeb, 0x65, 0x78, 0xd6, 0x32, 0xbf, 23 | 0xc7, 0x11, 0x41, 0x1c, 0x10, 0x2d, 0x89, 0x2f, 0x2a, 0x70, 0xad, 0xf2, 0xf3, 0x01, 0x41, 0x83, 24 | 0xbe, 0x92, 0x2f, 0x75, 0x48, 0xd0, 0x56, 0xf8, 0xeb, 0x58, 0x16, 0xd1, 0xba, 0xae, 0xcf, 0xe5, 25 | 0x2d, 0xb4, 0x7c, 0xf0, 0x90, 0x88, 0xa6, 0xc5, 0x57, 0x05, 0x98, 0x5a, 0x2f, 0x3a, 0x1c, 0x92, 26 | 0x6c, 0x70, 0xaa, 0xfe, 0x15, 0x7b, 0x30, 0x96, 0x59, 0x10, 0x47, 0x09, 0x6e, 0xe2, 0xba, 0x6a, 27 | 0xa0, 0x81, 0x05, 0xa8, 0x27, 0x6f, 0x7f, 0xa2, 0x36, 0x08, 0xbd, 0x7e, 0xb8, 0xb5, 0x05, 0x7d, 28 | 0xc3, 0x63, 0x9b, 0xdb, 0x84, 0x30, 0xf7, 0x75, 0x27, 0x2c, 0xbb, 0x3d, 0x28, 0xbf, 0x8f, 0x61, 29 | 0x38, 0x79, 0xc6, 0xd8, 0xb5, 0x01, 0xbb, 0x62, 0xa6, 0x50, 0x4f, 0x7e, 0xec, 0x55, 0xec, 0x55, 30 | 0xa0, 0x0d, 0x3d, 0x76, 0x23, 0x3e, 0x2a, 0x3c, 0x06, 0x84, 0x30, 0xb2, 0x06, 0x41, 0x61, 0xb6, 31 | 0xa5, 0xa9, 0x39, 0x61, 0xc9, 0x32, 0x01, 0xb3, 0x12, 0xd8, 0x01, 0x5e, 0x54, 0xcc, 0x16, 0xd4, 32 | 0x93, 0xe3, 0xef, 0x15, 0x7b, 0x47, 0x66, 0xfe, 0x07, 0x37, 0xce, 0xc0, 0x46, 0xc2, 0x3d, 0x2f, 33 | 0x7c, 0x35, 0x3e, 0x84, 0x1a, 0xf1, 0x7e, 0x46, 0xd7, 0x3c, 0xd7, 0xe7, 0xf9, 0x19, 0x74, 0x27, 34 | 0xe2, 0x3c, 0x38, 0x0b, 0xbd, 0xea, 0xc9, 0x3f, 0xea, 0x28, 0xe6, 0xdc, 0xdb, 0xf0, 0x76, 0x79, 35 | 0x9e, 0x5a, 0xeb, 0xbb, 0xbe, 0x13, 0xb1, 0x04, 0x06, 0x14, 0xc5, 0x9a, 0xd1, 0xd7, 0xba, 0x8b, 36 | 0x9d, 0x2e, 0x14, 0xeb, 0x3e, 0x4e, 0xd5, 0x3f, 0x98, 0xaa, 0x37, 0x8b, 0x47, 0xca, 0x85, 0x42, 37 | 0xda, 0xa9, 0x9d, 0x3d, 0xf5, 0x66, 0x33, 0xed, 0xa5, 0x63, 0x37, 0x6d, 0x9d, 0xcb, 0x5e, 0x6a, 38 | 0xdb, 0x52, 0x9a, 0xad, 0xb4, 0x95, 0x76, 0x32, 0x77, 0x67, 0x55, 0x7e, 0xa4, 0x24, 0x64, 0x0c, 39 | 0xc5, 0x9a, 0xb9, 0x90, 0xc3, 0x26, 0xc7, 0x51, 0x14, 0xeb, 0x41, 0xad, 0xf3, 0xb8, 0xdb, 0x59, 40 | 0x7c, 0x24, 0xf7, 0x9a, 0x85, 0xf8, 0x55, 0xc1, 0x85, 0x13, 0x5d, 0x6b, 0x16, 0x8a, 0x35, 0x87, 41 | 0x53, 0xb5, 0x79, 0xce, 0x31, 0x0b, 0x8e, 0x59, 0x70, 0xcc, 0xf3, 0xc4, 0x32, 0xc7, 0x2c, 0xdc, 42 | 0x7c, 0xe0, 0x72, 0xce, 0x1c, 0x14, 0x6b, 0xbe, 0x7c, 0xbe, 0x74, 0xbc, 0x4f, 0xbb, 0x90, 0x76, 43 | 0x6a, 0xa7, 0x17, 0x72, 0x2a, 0xee, 0xe5, 0x9c, 0xf9, 0xb3, 0x7d, 0xaa, 0x9c, 0x70, 0x62, 0x1c, 44 | 0xfa, 0x17, 0x00, 0x00, 0xff, 0xff, 0xd5, 0x08, 0x36, 0x79, 0x72, 0x05, 0x00, 0x00, 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /properties.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "log" 19 | "strconv" 20 | ) 21 | 22 | // GameObjectProps is a dictionary of properties for game objects. 23 | // We create our own type because we have convenience methods that 24 | // provide both type conversion and default values. 25 | type GameObjectProps map[string]string 26 | 27 | const ( 28 | GameObjectPropX = "x" // int 29 | GameObjectPropY = "y" // int 30 | GameObjectPropWidth = "width" // int 31 | GameObjectPropHeight = "height" // int 32 | GameObjectPropVX = "vx" // float64 33 | GameObjectPropVY = "vy" // float64 34 | GameObjectFrame = "frame" // start frame (string) 35 | GameObjectSprite = "sprite" // sprite name (string) 36 | GameObjectColor1 = "color1" // color name (string) 37 | GameObjectColor2 = "color2" // color name (string) 38 | GameObjectColor3 = "color3" // color name (string) 39 | GameObjectColor4 = "color4" // color name (string) 40 | GameObjectColor5 = "color5" // color name (string) 41 | GameObjectColor6 = "color6" // color name (string) 42 | GameObjectColor7 = "color7" // color name (string) 43 | GameObjectColor8 = "color8" // color name (string) 44 | GameObjectColor9 = "color9" // color name (string) 45 | GameObjectColor10 = "color10" // color name (string) 46 | GameObjectColor11 = "color11" // color name (string) 47 | GameObjectColor12 = "color12" // color name (string) 48 | GameObjectColor13 = "color13" // color name (string) 49 | GameObjectColor14 = "color14" // color name (string) 50 | GameObjectColor15 = "color15" // color name (string) 51 | GameObjectColor16 = "color16" // color name (string) 52 | ) 53 | 54 | func (p GameObjectProps) PropFloat64(name string, def float64) float64 { 55 | if s, ok := p[name]; !ok { 56 | return def 57 | } else if f, e := strconv.ParseFloat(s, 64); e != nil { 58 | log.Printf("Parse float64, prop %s: %v", name, e) 59 | return def 60 | } else { 61 | return f 62 | } 63 | } 64 | 65 | func (p GameObjectProps) PropInt(name string, def int) int { 66 | if s, ok := p[name]; !ok { 67 | return def 68 | } else if i, e := strconv.Atoi(s); e != nil { 69 | log.Printf("Parse int, prop %s: %v", name, e) 70 | return def 71 | } else { 72 | return i 73 | } 74 | } 75 | 76 | func (p GameObjectProps) PropBool(name string, def bool) bool { 77 | if s, ok := p[name]; !ok { 78 | return def 79 | } else if b, e := strconv.ParseBool(s); e != nil { 80 | log.Printf("Parse bool, prop %s: %v", name, e) 81 | return def 82 | } else { 83 | return b 84 | } 85 | } 86 | 87 | func (p GameObjectProps) PropString(name string, def string) string { 88 | if s, ok := p[name]; !ok { 89 | return def 90 | } else { 91 | return s 92 | } 93 | } 94 | 95 | func (p GameObjectProps) PropSetInt(name string, val int) { 96 | p[name] = strconv.Itoa(val) 97 | } 98 | 99 | func (p GameObjectProps) PropSetFloat64(name string, val float64) { 100 | p[name] = strconv.FormatFloat(val, 'f', 2, 64) 101 | } 102 | 103 | func (p GameObjectProps) PropSetBool(name string, val bool) { 104 | p[name] = strconv.FormatBool(val) 105 | } 106 | 107 | func (p GameObjectProps) PropSetString(name string, val string) { 108 | p[name] = val 109 | } 110 | -------------------------------------------------------------------------------- /s-explosion.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x95, 0xcd, 0x6e, 0xd3, 0x40, 8 | 0x14, 0x85, 0xef, 0x99, 0xb8, 0x09, 0x2d, 0x01, 0xc4, 0xa6, 0x0b, 0xc0, 0x92, 0x59, 0xb0, 0x43, 9 | 0x48, 0xd0, 0x3f, 0x89, 0x9d, 0xdd, 0x60, 0x58, 0xa0, 0x82, 0x04, 0x12, 0xa0, 0x8a, 0x85, 0x45, 10 | 0x46, 0xae, 0x85, 0x63, 0x47, 0x8e, 0x11, 0xcd, 0x02, 0x09, 0x4a, 0xf9, 0x2b, 0xa1, 0x0b, 0x16, 11 | 0x3c, 0x13, 0x0f, 0xc1, 0x8a, 0x27, 0xe0, 0x09, 0x18, 0x64, 0xbb, 0xb1, 0x9d, 0x78, 0x6c, 0x17, 12 | 0x68, 0x54, 0xc4, 0x59, 0xc4, 0xe3, 0xf9, 0x6e, 0x66, 0xee, 0xbd, 0x67, 0x46, 0x7e, 0x21, 0x5e, 13 | 0x35, 0x80, 0x85, 0x7b, 0xfd, 0xc0, 0x09, 0x79, 0xc7, 0x0a, 0x2d, 0x88, 0x1d, 0xc2, 0x3c, 0x94, 14 | 0x0d, 0xab, 0xc7, 0xd1, 0x26, 0xcc, 0x3d, 0x70, 0xba, 0xe1, 0x16, 0x14, 0x42, 0xf3, 0x16, 0x77, 15 | 0xec, 0xad, 0x30, 0x1a, 0xb6, 0xee, 0x04, 0x8e, 0xed, 0x78, 0x0f, 0x73, 0xe3, 0x47, 0xd1, 0x78, 16 | 0xee, 0xb6, 0x35, 0xe4, 0x41, 0x1c, 0x7c, 0xd3, 0x1d, 0xf6, 0xb7, 0x06, 0x10, 0x6f, 0x09, 0xad, 17 | 0xbb, 0x96, 0xcb, 0xc3, 0x90, 0x43, 0xbc, 0x27, 0x34, 0xcd, 0xc0, 0xea, 0xf1, 0x01, 0xc4, 0x3e, 18 | 0x11, 0x5d, 0x16, 0x6f, 0x14, 0xe0, 0x7c, 0xcf, 0xea, 0x6f, 0x0e, 0xc2, 0xc0, 0xf1, 0xec, 0xc7, 19 | 0x3d, 0xcb, 0xf1, 0xae, 0x24, 0xd9, 0xc4, 0x2b, 0xc4, 0x0b, 0xb4, 0x21, 0x76, 0x89, 0x56, 0xc5, 20 | 0xeb, 0x06, 0x98, 0xd8, 0x25, 0x34, 0xd0, 0xea, 0x38, 0x83, 0xbe, 0x6b, 0x0d, 0xa3, 0x0c, 0x17, 21 | 0x4c, 0x3f, 0xe0, 0x76, 0xe0, 0x3f, 0xf3, 0xba, 0xf1, 0xab, 0x61, 0x3d, 0x79, 0x9a, 0xbe, 0x12, 22 | 0x5d, 0x14, 0xef, 0x14, 0xe0, 0x6c, 0x6e, 0x8f, 0xe4, 0x11, 0x67, 0xd3, 0x8e, 0x42, 0x2e, 0x89, 23 | 0x4f, 0x0c, 0x58, 0xdc, 0xcc, 0x6f, 0x1e, 0x67, 0x99, 0xf4, 0x63, 0x9f, 0x20, 0xf6, 0x88, 0x3a, 24 | 0xe2, 0x43, 0x03, 0x38, 0x53, 0xc0, 0x7b, 0x04, 0x05, 0x73, 0x1b, 0x49, 0x51, 0x23, 0x82, 0xb2, 25 | 0xc1, 0xb7, 0xc3, 0x28, 0x11, 0xe5, 0xbe, 0xd3, 0xe3, 0x51, 0x2f, 0x94, 0x24, 0x70, 0x44, 0x44, 26 | 0x8b, 0xe2, 0x23, 0x03, 0x4e, 0x6c, 0xa6, 0x39, 0x8c, 0x28, 0xca, 0xe0, 0xe7, 0xc9, 0x97, 0x62, 27 | 0x07, 0xf3, 0x37, 0xb6, 0xfb, 0xae, 0x3f, 0x70, 0x7c, 0x0f, 0xd7, 0x71, 0x0e, 0x17, 0xd0, 0xc6, 28 | 0x69, 0x28, 0xd0, 0xd1, 0xf8, 0xfe, 0xe5, 0x33, 0x9a, 0x43, 0xee, 0xba, 0xfe, 0xf3, 0xf1, 0x93, 29 | 0x60, 0x4c, 0xce, 0x2b, 0xb6, 0xef, 0x76, 0x09, 0xeb, 0xc9, 0x6c, 0x23, 0xe0, 0x5d, 0x34, 0xfd, 30 | 0xc0, 0xf2, 0x6c, 0x4e, 0xe8, 0xe4, 0x26, 0xa3, 0x1f, 0x62, 0x2d, 0x80, 0x99, 0x57, 0xc1, 0xcc, 31 | 0x6b, 0x10, 0x23, 0x9c, 0x52, 0xb5, 0x2a, 0x1d, 0x19, 0xd5, 0x23, 0xc9, 0xa9, 0x9e, 0xa9, 0x48, 32 | 0x75, 0x5d, 0x82, 0x0f, 0x49, 0xab, 0x57, 0xae, 0xc9, 0x6a, 0xa6, 0xdd, 0x28, 0x88, 0x10, 0x1b, 33 | 0xc2, 0xcc, 0xa5, 0xa3, 0x34, 0xa5, 0x58, 0x7d, 0x4a, 0x75, 0x5d, 0xd6, 0x3b, 0x55, 0xc6, 0x52, 34 | 0xae, 0x96, 0xc0, 0x04, 0xff, 0x35, 0xad, 0xde, 0xb7, 0x26, 0xe7, 0xba, 0x7a, 0xff, 0xd8, 0x94, 35 | 0x25, 0x30, 0x73, 0x79, 0xca, 0x14, 0x23, 0xd3, 0xd4, 0x52, 0x46, 0x51, 0x63, 0x6a, 0x94, 0x29, 36 | 0xa2, 0x46, 0xb9, 0x34, 0xd5, 0xa8, 0xd2, 0xb1, 0xd1, 0xea, 0x9c, 0x6b, 0xea, 0xad, 0xe9, 0x55, 37 | 0x45, 0x9f, 0x63, 0x53, 0x96, 0xc1, 0xcc, 0x15, 0x88, 0xaf, 0x79, 0x53, 0x3a, 0x99, 0xa6, 0x4c, 38 | 0x49, 0x26, 0xd7, 0x0f, 0x94, 0x45, 0xa8, 0xe9, 0x9f, 0xd6, 0x27, 0x94, 0x04, 0xa8, 0x9a, 0x04, 39 | 0x8d, 0x03, 0x34, 0xb5, 0x8c, 0xc5, 0x52, 0xa7, 0x61, 0x76, 0xa8, 0x12, 0x2a, 0x45, 0x09, 0xcf, 40 | 0x53, 0xc9, 0x89, 0xcd, 0xa8, 0xf4, 0x3c, 0x57, 0xd3, 0x14, 0x97, 0xde, 0x85, 0xd2, 0x7d, 0xf3, 41 | 0x05, 0xc8, 0x6f, 0xca, 0x0a, 0x98, 0xb9, 0x0a, 0xf1, 0xed, 0x37, 0x4c, 0xc9, 0xc5, 0x69, 0x93, 42 | 0xa6, 0x48, 0x12, 0x1c, 0x9b, 0x52, 0x76, 0x5d, 0x63, 0x53, 0xaa, 0x2e, 0xfb, 0x71, 0x51, 0xad, 43 | 0x9a, 0x56, 0x94, 0x34, 0xee, 0x55, 0xc9, 0x0a, 0x13, 0x7d, 0x96, 0x9b, 0xb2, 0x0a, 0x66, 0xae, 44 | 0x41, 0xfc, 0x38, 0xbc, 0x29, 0x53, 0x3d, 0xcd, 0x99, 0x22, 0x6f, 0x7a, 0x6d, 0x81, 0x1d, 0xed, 45 | 0x5f, 0xa4, 0xb5, 0xa6, 0xcc, 0xf0, 0x43, 0xbf, 0xc6, 0x8a, 0x86, 0x1c, 0x5c, 0x82, 0xa2, 0x21, 46 | 0x5a, 0x99, 0x21, 0xe5, 0x29, 0xce, 0xd0, 0x10, 0xed, 0x7f, 0xa3, 0x44, 0xbf, 0x02, 0x00, 0x00, 47 | 0xff, 0xff, 0x6e, 0xab, 0xd0, 0xfa, 0x09, 0x0d, 0x00, 0x00, 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /s-gantry.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: Gantry 16 | width: 10 17 | height: 8 18 | layer: 3 19 | glyphs: 20 | "@": 21 | background: black 22 | foreground: holdoff 23 | display: "•" 24 | 25 | "a": 26 | background: black 27 | foreground: holdon 28 | display: "o" 29 | "o": 30 | background: black 31 | foreground: gooff 32 | display: "•" 33 | 34 | "O": 35 | background: black 36 | foreground: goon 37 | display: "o" 38 | 39 | "X": 40 | background: black 41 | foreground: gantry 42 | display: "Ж" 43 | 44 | "/": 45 | background: black 46 | foreground: gantry 47 | display: "/" 48 | "R": 49 | background: black 50 | foreground: gantry 51 | display: "\\" 52 | "_": 53 | background: black 54 | foreground: gantry 55 | display: "_" 56 | "|": 57 | background: black 58 | foreground: gantry 59 | display: "│" 60 | "H": 61 | background: black 62 | foreground: gantry 63 | display: "░" 64 | "q": 65 | background: black 66 | foreground: tires 67 | display: "o" 68 | "Q": 69 | background: black 70 | foreground: tires 71 | display: "O" 72 | palette: 73 | holdon: yellow 74 | holdoff: red 75 | goon: lime 76 | gooff: green 77 | gantry: lightsteelblue 78 | tires: dimgray 79 | 80 | frames: 81 | - names: [ F1, START ] 82 | next: F2 83 | time: 500 84 | data: 85 | - " " 86 | - " @ " 87 | - " _|______" 88 | - " |X| " 89 | - " |H| " 90 | - " |X|__ " 91 | - "/H|H|R " 92 | - "qQqQqQ " 93 | 94 | - names: [ F2 ] 95 | next: F1 96 | time: 500 97 | data: 98 | - " " 99 | - " a " 100 | - " _|______" 101 | - " |X| " 102 | - " |H| " 103 | - " |X|__ " 104 | - "/H|H|R " 105 | - "qQqQqQ " 106 | 107 | - names: [ F3, RETRACT ] 108 | next: F4 109 | time: 100 110 | data: 111 | - " " 112 | - " o " 113 | - " _|______" 114 | - " |X| " 115 | - " |H| " 116 | - " |X|__ " 117 | - "/H|H|R " 118 | - "qQqQqQ " 119 | 120 | - names: [ F4 ] 121 | next: F5 122 | time: 400 123 | data: 124 | - " / " 125 | - " o / " 126 | - " _|_/ " 127 | - " |X| " 128 | - " |H| / " 129 | - " |X|/ " 130 | - "/H|H|R " 131 | - "qQqQqQ " 132 | 133 | - names: [ F5 ] 134 | next: F6 135 | time: 500 136 | data: 137 | - " | " 138 | - " O | " 139 | - " _|_| " 140 | - " |X| " 141 | - " |H|| " 142 | - " |X| " 143 | - "/H|H|R " 144 | - "qQqQqQ " 145 | 146 | - names: [ F6 ] 147 | next: F5 148 | time: 500 149 | data: 150 | - " | " 151 | - " o | " 152 | - " _|_| " 153 | - " |X| " 154 | - " |H|| " 155 | - " |X| " 156 | - "/H|H|R " 157 | - "qQqQqQ " 158 | -------------------------------------------------------------------------------- /s-gameover.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0xcb, 0x6f, 0xea, 0x46, 8 | 0x14, 0xc6, 0xe7, 0x33, 0x06, 0xd2, 0x28, 0x52, 0xd5, 0x2e, 0xd2, 0x45, 0x37, 0xed, 0xa2, 0xab, 9 | 0x56, 0x51, 0x78, 0x65, 0x9f, 0x07, 0x26, 0x91, 0x1a, 0xc8, 0x4b, 0x49, 0xaa, 0xa8, 0xfa, 0x64, 10 | 0x60, 0x00, 0x2b, 0x18, 0x88, 0x71, 0x5e, 0x8b, 0x2e, 0x9a, 0xa6, 0xed, 0xbd, 0xb9, 0xb9, 0xf9, 11 | 0x93, 0xef, 0x5c, 0xd9, 0x98, 0x9c, 0x91, 0x60, 0xe5, 0x99, 0x8f, 0x9f, 0xce, 0xf9, 0x9d, 0x33, 12 | 0xfc, 0x65, 0xfe, 0xce, 0x01, 0xab, 0xa7, 0x93, 0x28, 0x88, 0xf5, 0x9e, 0x1f, 0xfb, 0x30, 0x4f, 13 | 0x0a, 0xdf, 0xc0, 0x6d, 0xfa, 0xa1, 0xc6, 0x9a, 0x42, 0xfe, 0x22, 0xe8, 0xc6, 0x03, 0xb8, 0x0a, 14 | 0x85, 0x7d, 0x1d, 0xf4, 0x07, 0x71, 0x72, 0x2c, 0xb6, 0xa2, 0xa0, 0x1f, 0x8c, 0x2e, 0xad, 0xf3, 15 | 0x1f, 0xc9, 0x39, 0xff, 0xbb, 0xff, 0xa8, 0xa3, 0x14, 0x6e, 0x0c, 0x1f, 0x27, 0x83, 0x29, 0xcc, 16 | 0x7f, 0x0a, 0xc5, 0x23, 0x7f, 0xa8, 0xe3, 0x58, 0xc3, 0x7c, 0x50, 0x28, 0x78, 0x91, 0x1f, 0xea, 17 | 0x29, 0xcc, 0x9b, 0x52, 0xea, 0x37, 0xf3, 0xaf, 0x0b, 0xfc, 0x18, 0xfa, 0x93, 0xab, 0x69, 0x1c, 18 | 0x05, 0xa3, 0xfe, 0x9f, 0xa1, 0x1f, 0x8c, 0x36, 0x66, 0x36, 0x69, 0x85, 0xb4, 0xc0, 0x1a, 0xcc, 19 | 0xb3, 0x52, 0x5b, 0xe6, 0x9f, 0x1c, 0x1c, 0xf3, 0xac, 0x90, 0x43, 0x71, 0x2f, 0x98, 0x4e, 0x86, 20 | 0xfe, 0x63, 0x62, 0xb8, 0xea, 0x8d, 0x23, 0xdd, 0x8f, 0xc6, 0xb7, 0xa3, 0x6e, 0x7a, 0xdd, 0xf1, 21 | 0x3b, 0xd7, 0xef, 0x57, 0xa5, 0x7e, 0x36, 0xff, 0xbb, 0xc0, 0x77, 0x56, 0x8f, 0xd9, 0x27, 0xb5, 22 | 0x59, 0x4b, 0x90, 0x5f, 0xcc, 0x67, 0x07, 0x58, 0xbf, 0xb2, 0x9b, 0xa7, 0x96, 0xb3, 0x7d, 0xbc, 23 | 0x29, 0x98, 0x17, 0xa5, 0xf6, 0xcc, 0xc7, 0x1c, 0xf0, 0xed, 0xc2, 0xcf, 0x2f, 0x0a, 0x2e, 0xf2, 24 | 0xcd, 0xd9, 0x50, 0xaf, 0x0a, 0x6e, 0x53, 0x3f, 0xc4, 0x89, 0x88, 0x7b, 0x16, 0x84, 0x3a, 0xd9, 25 | 0x85, 0x3b, 0x03, 0x5f, 0x95, 0x52, 0xeb, 0xe6, 0x93, 0x03, 0xac, 0x5c, 0xbd, 0x3b, 0xbc, 0xaa, 26 | 0xc4, 0xe0, 0x8b, 0x6b, 0xcc, 0x13, 0x56, 0x1a, 0x7e, 0xa8, 0x5b, 0x77, 0x3a, 0xc2, 0x06, 0x1c, 27 | 0xac, 0xc3, 0xc1, 0xf7, 0xf8, 0x15, 0x7d, 0xa0, 0x81, 0x42, 0x67, 0x3c, 0x1c, 0x47, 0x95, 0xec, 28 | 0x5b, 0x52, 0x68, 0x03, 0xdb, 0xd9, 0xad, 0x2a, 0xe9, 0x08, 0x38, 0x5c, 0x4c, 0x77, 0x85, 0xad, 29 | 0x49, 0x7a, 0x0c, 0xb4, 0x16, 0xd3, 0x8e, 0xb0, 0x5b, 0x92, 0xc6, 0xc0, 0xc9, 0x62, 0xba, 0x2d, 30 | 0x6c, 0x59, 0xd2, 0x3b, 0xe0, 0x7c, 0xd1, 0xf7, 0x42, 0x52, 0xcb, 0xec, 0x52, 0x52, 0xcb, 0xe1, 31 | 0x4c, 0xba, 0x59, 0x69, 0x59, 0x66, 0xb3, 0x1c, 0xce, 0xa5, 0x82, 0xe5, 0xb0, 0xb3, 0x74, 0x3b, 32 | 0x53, 0xa9, 0x6b, 0xa5, 0xa1, 0xd4, 0xb5, 0x7c, 0x3d, 0xa0, 0xbe, 0xc8, 0x6e, 0x0a, 0x6b, 0x99, 33 | 0x35, 0xe4, 0x85, 0x2c, 0x87, 0x43, 0x61, 0xad, 0x34, 0x12, 0x07, 0xab, 0x5b, 0x73, 0xe9, 0xbb, 34 | 0x9d, 0x2e, 0xf5, 0xed, 0x2d, 0x35, 0xbb, 0x91, 0xd7, 0xb4, 0xb6, 0xf3, 0x20, 0xdb, 0xb1, 0x52, 35 | 0x02, 0x3f, 0x2d, 0x9a, 0xb5, 0xa4, 0x82, 0x95, 0xfa, 0xb2, 0x49, 0xcb, 0x77, 0x22, 0xac, 0xe5, 36 | 0x50, 0x17, 0x33, 0xab, 0xc2, 0x58, 0x58, 0xab, 0xc2, 0xd1, 0xd2, 0x0a, 0x07, 0xb2, 0x49, 0x6b, 37 | 0xbf, 0x81, 0xa4, 0xd6, 0x14, 0x15, 0xe9, 0x66, 0xa5, 0x5a, 0x52, 0xab, 0xdb, 0xbe, 0x54, 0xb0, 38 | 0xba, 0x0d, 0x96, 0xa6, 0xf7, 0x4b, 0xff, 0xa9, 0x25, 0xa9, 0x6b, 0x99, 0x9d, 0xc8, 0x0b, 0x59, 39 | 0x13, 0x17, 0x32, 0xab, 0xfc, 0xfd, 0x20, 0x88, 0x75, 0x16, 0xe7, 0xdb, 0x43, 0xbf, 0x73, 0x9d, 40 | 0xb1, 0xb9, 0x48, 0x77, 0x33, 0x49, 0xb7, 0x3d, 0xbc, 0xcd, 0x98, 0xaa, 0x3b, 0x0c, 0xc2, 0xec, 41 | 0x5c, 0x2b, 0xf6, 0x6e, 0x3b, 0x83, 0x69, 0xe0, 0xa3, 0x00, 0x38, 0xde, 0x26, 0x1c, 0xaf, 0x84, 42 | 0x2e, 0xf0, 0x03, 0xc9, 0x06, 0xb7, 0x79, 0xc8, 0x3a, 0xc9, 0x16, 0xcf, 0x59, 0xe7, 0x09, 0x49, 43 | 0x95, 0x50, 0x65, 0x38, 0x5e, 0x65, 0x4e, 0xed, 0x73, 0x87, 0x4d, 0x7a, 0x24, 0x8f, 0x78, 0x41, 44 | 0x8f, 0xa7, 0x73, 0xaa, 0x94, 0x92, 0x19, 0xd5, 0xa7, 0xcf, 0x90, 0x9a, 0xe4, 0x98, 0x77, 0xd4, 45 | 0x8c, 0xe6, 0x54, 0x05, 0x8e, 0x57, 0x9d, 0x53, 0x03, 0xb6, 0x39, 0x62, 0x8f, 0xe4, 0x84, 0xf7, 46 | 0xec, 0x71, 0x3a, 0xa7, 0xaa, 0x70, 0xbc, 0xda, 0x9c, 0x3a, 0xe0, 0x2e, 0x37, 0x59, 0x22, 0x79, 47 | 0xcc, 0x4b, 0x96, 0x78, 0x36, 0xa7, 0x6a, 0xe9, 0x04, 0x19, 0x15, 0xb0, 0xc3, 0x32, 0x2b, 0x24, 48 | 0x6f, 0xf8, 0xc0, 0x0a, 0xe3, 0x84, 0x52, 0x5f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd8, 0xa1, 0xe6, 49 | 0xb6, 0x88, 0x06, 0x00, 0x00, 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /f-hfence.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/gdamore/tcell" 21 | ) 22 | 23 | type hfence struct { 24 | remit *Sprite 25 | lemit *Sprite 26 | beam *Sprite 27 | level *Level 28 | dead bool 29 | } 30 | 31 | func (o *hfence) destroy(primary, alternate *Sprite) { 32 | o.dead = true 33 | o.beam.SetFrame("") 34 | o.remit.SetFrame("RX") 35 | o.lemit.SetFrame("LX") 36 | 37 | x1, y1, x2, y2 := primary.Bounds() 38 | props := GameObjectProps{} 39 | props.PropSetInt("x", x1+(x2-x1)/2) 40 | props.PropSetInt("y", y1+(y2-y1)/2) 41 | props.PropSetInt("count", 2) 42 | MakeGameObject(o.level, "smexplosion", props) 43 | 44 | x1, y1, x2, y2 = alternate.Bounds() 45 | props = GameObjectProps{} 46 | props.PropSetInt("x", x1+(x2-x1)/2) 47 | props.PropSetInt("y", y1+(y2-y1)/2) 48 | props.PropSetInt("delay", 500) 49 | props.PropSetInt("count", 1) 50 | MakeGameObject(o.level, "smexplosion", props) 51 | } 52 | 53 | func (o *hfence) HandleEvent(ev tcell.Event) bool { 54 | if o.dead { 55 | return false 56 | } 57 | switch ev := ev.(type) { 58 | case *EventCollision: 59 | switch ev.Collider().Layer() { 60 | case LayerPlayer: 61 | switch ev.Target() { 62 | case o.lemit: 63 | o.destroy(o.lemit, o.remit) 64 | case o.remit: 65 | o.destroy(o.remit, o.lemit) 66 | } 67 | case LayerShot: 68 | switch ev.Target() { 69 | case o.lemit: 70 | o.destroy(o.lemit, o.remit) 71 | case o.remit: 72 | o.destroy(o.remit, o.lemit) 73 | case o.beam: 74 | x, y, _, _ := ev.Collider().Bounds() 75 | props := GameObjectProps{} 76 | props.PropSetInt("x", x) 77 | props.PropSetInt("y", y) 78 | props.PropSetInt("count", 1) 79 | props.PropSetString("sprite", "TinyExplosion") 80 | MakeGameObject(o.level, "explosion", props) 81 | } 82 | } 83 | } 84 | return false 85 | } 86 | 87 | func makeHFence(level *Level, props GameObjectProps) error { 88 | 89 | x := props.PropInt("x", 0) 90 | y := props.PropInt("y", 0) 91 | width := props.PropInt("width", 6) 92 | height := props.PropInt("height", 1) 93 | offtime := props.PropInt("offtime", 4000) 94 | ontime := props.PropInt("ontime", 2000) 95 | 96 | if width < 5 || height < 1 { 97 | // need at least 5 places 98 | return nil 99 | } 100 | sname := props.PropString("sprite", "HFence") 101 | 102 | o := &hfence{level: level} 103 | o.remit = GetSprite(sname) 104 | o.lemit = GetSprite(sname) 105 | o.beam = GetSprite(sname) 106 | 107 | o.remit.Resize(2, height) 108 | o.lemit.Resize(2, height) 109 | o.beam.Resize(width-4, height) 110 | 111 | o.lemit.SetFrame("LL") 112 | o.remit.SetFrame("RR") 113 | o.beam.SetFrame("OFF") 114 | 115 | o.beam.SetNextFrame("OFF", 116 | time.Millisecond*time.Duration(offtime), "ON") 117 | 118 | if ontime > 1000 { 119 | ontime -= 1000 120 | o.beam.SetNextFrame("ON", 500*time.Millisecond, "ON1") 121 | o.beam.SetNextFrame("ON1", 122 | time.Duration(ontime)*time.Millisecond, "ON2") 123 | o.beam.SetNextFrame("ON2", 500*time.Millisecond, "OFF") 124 | } else { 125 | // Too short to reach full charge 126 | o.beam.SetNextFrame("ON", 127 | time.Duration(ontime)*time.Millisecond, "OFF") 128 | } 129 | 130 | o.remit.SetLayer(LayerHazard) 131 | o.lemit.SetLayer(LayerHazard) 132 | o.beam.SetLayer(LayerHazard) 133 | 134 | o.lemit.SetPosition(x, y) 135 | o.remit.SetPosition(x+width-2, y) 136 | o.beam.SetPosition(x+2, y) 137 | 138 | o.lemit.Watch(o) 139 | o.remit.Watch(o) 140 | o.beam.Watch(o) 141 | o.level = level 142 | level.AddSprite(o.lemit) 143 | level.AddSprite(o.remit) 144 | level.AddSprite(o.beam) 145 | 146 | return nil 147 | } 148 | 149 | func init() { 150 | RegisterGameObjectMaker("hfence", makeHFence) 151 | } 152 | -------------------------------------------------------------------------------- /s-thrust.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: Thrust 17 | width: 5 18 | height: 6 19 | originx: 2 20 | originy: 0 21 | layer: 2 22 | glyphs: 23 | "<": 24 | background: color1 25 | foreground: color2 26 | display: "˂" 27 | 28 | ">": 29 | background: color1 30 | foreground: color2 31 | display: "˃" 32 | 33 | "v": 34 | background: color1 35 | foreground: color2 36 | display: "˅" 37 | 38 | "^": 39 | background: color1 40 | foreground: color2 41 | display: "˄" 42 | 43 | "l": 44 | background: color1 45 | foreground: color3 46 | display: "˂" 47 | 48 | "r": 49 | background: color1 50 | foreground: color3 51 | display: "˃" 52 | 53 | "u": 54 | background: color1 55 | foreground: color3 56 | display: "˅" 57 | 58 | "d": 59 | background: color1 60 | foreground: color3 61 | display: "˄" 62 | 63 | "L": 64 | background: color1 65 | foreground: color4 66 | display: "˂" 67 | 68 | "R": 69 | background: color1 70 | foreground: color4 71 | display: "˃" 72 | 73 | "U": 74 | background: color1 75 | foreground: color4 76 | display: "˅" 77 | 78 | "D": 79 | background: color1 80 | foreground: color4 81 | display: "˄" 82 | 83 | palette: 84 | color1: black 85 | color2: yellow 86 | color3: orange 87 | color4: red 88 | color5: firebrick 89 | 90 | frames: 91 | - names: [ DOWN ] 92 | next: DOWN1 93 | time: 150 94 | data: 95 | - " ^ ^ " 96 | - " " 97 | - " " 98 | - " " 99 | - " " 100 | - " " 101 | 102 | - names: [ RIGHT ] 103 | next: RIGHT1 104 | time: 150 105 | data: 106 | - " " 107 | - "< " 108 | - " " 109 | - "< " 110 | - " " 111 | - " " 112 | 113 | - names: [ LEFT ] 114 | next: LEFT1 115 | time: 150 116 | data: 117 | - " " 118 | - " >" 119 | - " " 120 | - " >" 121 | - " " 122 | - " " 123 | 124 | - names: [ UP ] 125 | next: UP1 126 | time: 150 127 | data: 128 | - " " 129 | - " " 130 | - " " 131 | - " " 132 | - " v v " 133 | - " v " 134 | 135 | - names: [ DOWN1 ] 136 | next: DOWN2 137 | time: 250 138 | data: 139 | - " d d " 140 | - " " 141 | - " " 142 | - " " 143 | - " " 144 | - " " 145 | 146 | - names: [ RIGHT1 ] 147 | next: RIGHT2 148 | time: 250 149 | data: 150 | - " " 151 | - "l " 152 | - " " 153 | - "l " 154 | - " " 155 | - " " 156 | 157 | - names: [ LEFT1 ] 158 | next: LEFT2 159 | time: 250 160 | data: 161 | - " " 162 | - " r" 163 | - " " 164 | - " r" 165 | - " " 166 | - " " 167 | 168 | 169 | - names: [ UP1 ] 170 | next: UP2 171 | time: 250 172 | data: 173 | - " " 174 | - " " 175 | - " " 176 | - " " 177 | - " u u " 178 | - " u " 179 | 180 | - names: [ DOWN2 ] 181 | next: "" 182 | time: 50 183 | data: 184 | - " D D " 185 | - " " 186 | - " " 187 | - " " 188 | - " " 189 | - " " 190 | 191 | - names: [ RIGHT2 ] 192 | next: "" 193 | time: 70 194 | data: 195 | - " " 196 | - "L " 197 | - " " 198 | - "L " 199 | - " " 200 | - " " 201 | 202 | - names: [ LEFT2 ] 203 | next: "" 204 | time: 70 205 | data: 206 | - " " 207 | - " R" 208 | - " " 209 | - " R" 210 | - " " 211 | - " " 212 | 213 | 214 | - names: [ UP2 ] 215 | next: "" 216 | time: 70 217 | data: 218 | - " " 219 | - " " 220 | - " " 221 | - " " 222 | - " U U " 223 | - " U " 224 | -------------------------------------------------------------------------------- /mkassets.go: -------------------------------------------------------------------------------- 1 | //+build ignore 2 | 3 | // Copyright 2015 Garrett D'Amore 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use file except in compliance with the License. 7 | // You may obtain a copy of the license at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package main 18 | 19 | import ( 20 | "bytes" 21 | "compress/gzip" 22 | "encoding/gob" 23 | "flag" 24 | "fmt" 25 | "io" 26 | "io/ioutil" 27 | "os" 28 | "strings" 29 | 30 | "gopkg.in/yaml.v2" 31 | ) 32 | 33 | var pkg = "main" 34 | 35 | // This program builds sprite data as go declarations, loading the data 36 | // from YAML. 37 | 38 | func die(format string, v ...interface{}) { 39 | fmt.Fprintf(os.Stderr, format, v...) 40 | fmt.Fprintln(os.Stderr, "") 41 | os.Exit(1) 42 | } 43 | 44 | func validateSprite(iname string, data *SpriteData) { 45 | if data.Name == "" { 46 | die("%s: Missing name", iname) 47 | } 48 | 49 | for fri, frame := range data.Frames { 50 | if len(frame.Data) != data.Height { 51 | die("Frame %d bad lines (%d != %d)", 52 | fri, len(frame.Data), data.Height) 53 | } 54 | for lno, line := range frame.Data { 55 | if len(line) != data.Width { 56 | die("%s: Frame %d, line %d wrong len "+ 57 | "(%d != %d)", iname, fri, lno, 58 | len(line), data.Width) 59 | } 60 | for i := range line { 61 | if line[i] == ' ' { 62 | continue 63 | } 64 | ss := string(line[i]) 65 | if _, ok := data.Glyphs[ss]; !ok { 66 | die("%s: Frame %d: line %d: "+ 67 | "unknown glyph", 68 | iname, fri, i) 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | func writeSpriteGo(w io.Writer, data *SpriteData) { 76 | buf := new(bytes.Buffer) 77 | zbuf, e := gzip.NewWriterLevel(buf, gzip.BestCompression) 78 | if e != nil { 79 | die("Cannot gzip gob: %v", e) 80 | } 81 | enc := gob.NewEncoder(zbuf) 82 | if e := enc.Encode(data); e != nil { 83 | die("Cannot encode gob: %v", e) 84 | } 85 | zbuf.Close() 86 | 87 | fmt.Fprintf(w, "// Sprite data file\n") 88 | fmt.Fprintf(w, "// Generated automatically, do not edit!\n") 89 | fmt.Fprintf(w, "package %s\n\n", pkg) 90 | 91 | fmt.Fprintf(w, "func init() {\n") 92 | fmt.Fprintf(w, "\tRegisterSpriteGobZ([]byte{\n") 93 | x := buf.Bytes() 94 | for i, b := range x { 95 | if i%16 == 0 { 96 | fmt.Fprint(w, "\t\t") 97 | } else { 98 | fmt.Fprint(w, " ") 99 | } 100 | fmt.Fprintf(w, "0x%02x,", b) 101 | if i%16 == 15 { 102 | fmt.Fprint(w, "\n") 103 | } 104 | } 105 | if len(x)%16 != 0 { 106 | fmt.Fprint(w, "\n") 107 | } 108 | fmt.Fprint(w, "\t})\n}\n") 109 | } 110 | 111 | func validateLevel(iname string, level *LevelData) { 112 | if level.Name == "" { 113 | die("Missing level label") 114 | } 115 | if level.Properties.PropInt("width", 0) < 1 || 116 | level.Properties.PropInt("height", 0) < 1 { 117 | die("Impossible level dimensions") 118 | } 119 | } 120 | 121 | func writeLevelGo(w io.Writer, level *LevelData) { 122 | 123 | buf := new(bytes.Buffer) 124 | zbuf, e := gzip.NewWriterLevel(buf, gzip.BestCompression) 125 | if e != nil { 126 | die("Cannot gzip gob: %v", e) 127 | } 128 | enc := gob.NewEncoder(zbuf) 129 | if e := enc.Encode(level); e != nil { 130 | die("Cannot encode gob: %v", e) 131 | } 132 | zbuf.Close() 133 | 134 | fmt.Fprintf(w, "// Level data file\n") 135 | fmt.Fprintf(w, "// Generated automatically, do not edit!\n") 136 | fmt.Fprintf(w, "package %s\n\n", pkg) 137 | 138 | fmt.Fprintf(w, "func init() {\n") 139 | fmt.Fprintf(w, "\tRegisterLevelGobZ([]byte{\n") 140 | x := buf.Bytes() 141 | for i, b := range x { 142 | if i%16 == 0 { 143 | fmt.Fprint(w, "\t\t") 144 | } else { 145 | fmt.Fprint(w, " ") 146 | } 147 | fmt.Fprintf(w, "0x%02x,", b) 148 | if i%16 == 15 { 149 | fmt.Fprint(w, "\n") 150 | } 151 | } 152 | if len(x)%16 != 0 { 153 | fmt.Fprint(w, "\n") 154 | } 155 | fmt.Fprint(w, "\t})\n}\n") 156 | } 157 | 158 | func main() { 159 | 160 | var inf *os.File 161 | var e error 162 | var asset string = "sprite" 163 | var oname string = "" 164 | var format string = "go" 165 | 166 | flag.StringVar(&asset, "type", asset, "asset type [sprite|level]") 167 | flag.StringVar(&pkg, "pkg", pkg, "package name") 168 | flag.StringVar(&oname, "out", "", "output file name") 169 | flag.StringVar(&format, "format", "go", "output format [go|gob]") 170 | 171 | flag.Parse() 172 | 173 | for _, arg := range flag.Args() { 174 | 175 | data := &SpriteData{} 176 | level := &LevelData{} 177 | 178 | iname := arg 179 | if inf, e = os.Open(iname); e != nil { 180 | die("%s: open: %v", iname, e) 181 | } 182 | 183 | nname := oname 184 | if nname == "" { 185 | nname = iname + ".go" 186 | if strings.HasSuffix(iname, ".yml") { 187 | nname = iname[:len(iname)-4] + ".go" 188 | } else if strings.HasSuffix(iname, ".yaml") { 189 | nname = iname[:len(iname)-5] + ".go" 190 | } 191 | } 192 | all, e := ioutil.ReadAll(inf) 193 | if e != nil { 194 | die("%s; Cannot load: %v", iname, e) 195 | } 196 | 197 | buf := new(bytes.Buffer) 198 | 199 | switch asset { 200 | case "sprite": 201 | 202 | if err := yaml.Unmarshal(all, &data); err != nil { 203 | die("%s: YAML error: %v", iname, err) 204 | } 205 | validateSprite(iname, data) 206 | writeSpriteGo(buf, data) 207 | 208 | case "level": 209 | if err := yaml.Unmarshal(all, &level); err != nil { 210 | die("%s: YAML error: %v", iname, err) 211 | } 212 | validateLevel(iname, level) 213 | writeLevelGo(buf, level) 214 | 215 | default: 216 | panic("unknown asset class") 217 | } 218 | 219 | allb := buf.Bytes() 220 | if nname != "" && nname != "-" { 221 | e := ioutil.WriteFile(nname, allb, 0644) 222 | if e != nil { 223 | die("Cannot create output %s: %v", nname, e) 224 | } 225 | } else { 226 | os.Stdout.Write(allb) 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /game.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "github.com/gdamore/tcell" 21 | "github.com/gdamore/tcell/views" 22 | "sync" 23 | "time" 24 | ) 25 | 26 | // go-proxima (Escape from Proxima Five) is a terminal-oriented game, 27 | // utilizing Unicode characters and text terminal display capabilties, 28 | // demonstrating the capabilities of tcell. It needs at leasst an 80x24 29 | // column terminal to run well. 30 | 31 | type Game struct { 32 | quitq chan struct{} 33 | screen tcell.Screen 34 | eventq chan tcell.Event 35 | errmsg string 36 | quitone sync.Once 37 | level *Level 38 | lview *views.ViewPort 39 | sview *views.ViewPort 40 | sbar *views.TextBar 41 | lives int 42 | gameover bool 43 | started bool 44 | 45 | sync.Mutex 46 | } 47 | 48 | func (g *Game) Init() error { 49 | g.lives = 5 50 | if screen, err := tcell.NewScreen(); err != nil { 51 | return err 52 | } else if err = screen.Init(); err != nil { 53 | return err 54 | } else { 55 | screen.SetStyle(tcell.StyleDefault. 56 | Background(tcell.ColorBlack). 57 | Foreground(tcell.ColorWhite)) 58 | g.screen = screen 59 | } 60 | 61 | // XXX: Add a main screen 62 | g.screen.EnableMouse() 63 | g.level = GetLevel("level1") 64 | if g.level == nil { 65 | g.screen.Fini() 66 | return errors.New("Cannot find data (did you run rebuild.sh?)") 67 | } 68 | g.lview = views.NewViewPort(g.screen, 0, 1, -1, -1) 69 | g.level.SetView(g.lview) 70 | g.level.SetGame(g) 71 | 72 | g.sview = views.NewViewPort(g.screen, 0, 0, -1, 1) 73 | g.sbar = views.NewTextBar() 74 | g.sbar.SetView(g.sview) 75 | 76 | g.quitq = make(chan struct{}) 77 | g.eventq = make(chan tcell.Event) 78 | 79 | g.level.Reset() 80 | g.level.ShowPress() 81 | 82 | RegisterFallbacks(g.screen) 83 | 84 | return nil 85 | } 86 | 87 | func (g *Game) Quit() { 88 | g.quitone.Do(func() { 89 | close(g.quitq) 90 | }) 91 | } 92 | 93 | func (g *Game) Draw() { 94 | g.Lock() 95 | sbnorm := tcell.StyleDefault. 96 | Background(tcell.ColorAqua). 97 | Foreground(tcell.ColorBlack) 98 | sbwarn := tcell.StyleDefault. 99 | Background(tcell.ColorAqua). 100 | Foreground(tcell.ColorRed) 101 | sbalert := tcell.StyleDefault. 102 | Background(tcell.ColorRed). 103 | Foreground(tcell.ColorWhite) 104 | 105 | g.sbar.SetStyle(sbnorm) 106 | timer := g.level.GetTimer() 107 | times := fmt.Sprintf("%02d:%02d.%1d", 108 | timer/time.Minute, 109 | (timer%time.Minute)/time.Second, 110 | timer%time.Second/(100*time.Millisecond)) 111 | 112 | g.sbar.SetCenter(g.level.Title(), sbnorm) 113 | if timer < 30*time.Second { 114 | g.sbar.SetLeft(times, sbwarn) 115 | if timer < 10*time.Second { 116 | g.sbar.SetCenter( 117 | fmt.Sprintf("-=: BLASTWAVE IN %s :=-", 118 | times), sbalert) 119 | } 120 | } else { 121 | g.sbar.SetLeft(times, sbnorm) 122 | } 123 | 124 | if g.gameover { 125 | g.sbar.SetCenter("-=: G A M E O V E R :=- ", sbwarn) 126 | } 127 | 128 | right := "" 129 | if g.lives > 5 { 130 | right += fmt.Sprintf("%d x ♥", g.lives) 131 | } else { 132 | for i := 0; i < g.lives; i++ { 133 | right += " ♥" 134 | } 135 | } 136 | right += " " 137 | 138 | g.sbar.SetRight(right, sbwarn) 139 | 140 | g.level.Draw(g.lview) 141 | g.sbar.Draw() 142 | g.screen.Show() 143 | g.Unlock() 144 | } 145 | 146 | func (g *Game) Run() error { 147 | 148 | go g.EventPoller() 149 | go g.Updater() 150 | loop: 151 | for { 152 | g.Draw() 153 | select { 154 | case <-g.quitq: 155 | break loop 156 | case <-time.After(time.Millisecond * 10): 157 | case ev := <-g.eventq: 158 | g.HandleEvent(ev) 159 | } 160 | } 161 | 162 | // Inject a wakeup interrupt 163 | iev := tcell.NewEventInterrupt(nil) 164 | g.screen.PostEvent(iev) 165 | 166 | g.screen.Fini() 167 | // wait for updaters to finish 168 | if g.errmsg != "" { 169 | return errors.New(g.errmsg) 170 | } 171 | return nil 172 | } 173 | 174 | func (g *Game) Error(msg string) { 175 | g.errmsg = msg 176 | g.Quit() 177 | } 178 | 179 | func (g *Game) HandleEvent(ev tcell.Event) bool { 180 | switch ev := ev.(type) { 181 | case *tcell.EventResize: 182 | g.lview.Resize(0, 1, -1, -1) 183 | g.sview.Resize(0, 0, -1, 1) 184 | g.level.HandleEvent(ev) 185 | case *tcell.EventKey: 186 | if ev.Key() == tcell.KeyEscape { 187 | g.Quit() 188 | return true 189 | } 190 | if !g.started { 191 | if ev.Key() == tcell.KeyEnter { 192 | if g.gameover { 193 | g.lives = 5 194 | } 195 | g.level.Reset() 196 | g.level.Start() 197 | g.started = true 198 | g.gameover = false 199 | } 200 | // eat all keys until level starts 201 | return true 202 | } 203 | case *EventPlayerDeath: 204 | g.lives-- 205 | g.started = false 206 | if g.lives == 0 { 207 | g.gameover = true 208 | g.level.HandleEvent(&EventGameOver{}) 209 | } 210 | return true 211 | 212 | case *EventLevelComplete: 213 | g.lives++ // bonus life (for now) 214 | g.started = false 215 | return true 216 | } 217 | if !g.level.HandleEvent(ev) { 218 | return true 219 | } 220 | return true 221 | } 222 | 223 | func (g *Game) Updater() { 224 | for { 225 | select { 226 | case <-g.quitq: 227 | return 228 | case <-time.After(time.Millisecond * 10): 229 | g.Lock() 230 | g.level.Update(time.Now()) 231 | g.Unlock() 232 | } 233 | } 234 | } 235 | 236 | func (g *Game) EventPoller() { 237 | for { 238 | select { 239 | case <-g.quitq: 240 | return 241 | default: 242 | } 243 | ev := g.screen.PollEvent() 244 | if ev == nil { 245 | return 246 | } 247 | select { 248 | case <-g.quitq: 249 | return 250 | case g.eventq <- ev: 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /s-explosion.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | # Thanks to Luis Zuno for dedicating his explosions pack to the 17 | # public domain. Those were PNGs, and not used precisely identically, 18 | # but his work inspired this. 19 | 20 | name: Explosion 21 | width: 29 22 | height: 13 23 | originx: 14 24 | originy: 6 25 | layer: 7 26 | glyphs: 27 | A: 28 | background: yellow 29 | foreground: yellow 30 | display: "▒" 31 | 32 | B: 33 | background: gold 34 | foreground: yellow 35 | display: "▒" 36 | 37 | C: 38 | background: orange 39 | foreground: red 40 | display: "▒" 41 | D: 42 | background: red 43 | foreground: red 44 | display: "▒" 45 | 46 | frames: 47 | - names: [ F1 ] 48 | next: F2 49 | time: 70 50 | data: 51 | - " " 52 | - " " 53 | - " " 54 | - " AAAAA " 55 | - " AAAAAAAAAAA " 56 | - " AAAAAAAAAAAAA " 57 | - " AAAAAAAAAAAAA " 58 | - " AAAAAAAAAAA " 59 | - " AAAAA " 60 | - " " 61 | - " " 62 | - " " 63 | - " " 64 | 65 | - names: [ F2 ] 66 | next: F3 67 | time: 70 68 | data: 69 | - " " 70 | - " " 71 | - " AAAAAAAAA " 72 | - " AAAAAAAAAAAAAAA " 73 | - " AAAAAAAAAAAAAAAAAAA " 74 | - " AAAAAAAAAAAAAAAAAAAAA " 75 | - " AAAAAAAAAAAAAAAAAAAAA " 76 | - " AAAAAAAAAAAAAAAAAAAAA " 77 | - " AAAAAAAAAAAAAAAAAAA " 78 | - " AAAAAAAAAAAAAAA " 79 | - " AAAAAAAAA " 80 | - " " 81 | - " " 82 | 83 | - names: [ F3 ] 84 | next: F4 85 | time: 70 86 | data: 87 | - " BBBBBBBBBBB " 88 | - " BBBBBBBBBBBBBBBBBBB " 89 | - " BBBBBBBBBBBBBBBBBBBBBBBBB " 90 | - " BBBBBBBBBBBBBBBBBBBBBBBBBBB " 91 | - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 92 | - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 93 | - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 94 | - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 95 | - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 96 | - " BBBBBBBBBBBBBBBBBBBBBBBBBBB " 97 | - " BBBBBBBBBBBBBBBBBBBBBBBBB " 98 | - " BBBBBBBBBBBBBBBBBBB " 99 | - " BBBBBBBBBBB " 100 | 101 | - names: [ F4 ] 102 | next: F5 103 | time: 100 104 | data: 105 | - " DDDDDDDDDDD " 106 | - " DDDDDDCCCCCCCCDDDDD " 107 | - " DDDDDDDCCCCCCCCCCCCCCDDDD " 108 | - " DDDDDCCCCCCCCCCCCCCCCCCCCDD " 109 | - "DDDDCCCCCCCCCCCCCCCCCCCCCCCCC" 110 | - "DDDCCCCCCCCCCCCC CCC" 111 | - "DDCCCCCCCCCCC C" 112 | - "DDCCCCCCCCC " 113 | - "DDCCCCCCCC " 114 | - " DCCCCCCCC " 115 | - " DCCCCCCC " 116 | - " CCCCCC " 117 | - " CCC " 118 | 119 | - names: [ F5 ] 120 | next: "F6" 121 | time: 110 122 | data: 123 | - " DDDDDDDDDDD " 124 | - " DDDDD DD DD " 125 | - " DDDD DD " 126 | - " DD D " 127 | - "DD " 128 | - "DD " 129 | - "DD " 130 | - "DD " 131 | - "DD " 132 | - " D " 133 | - " DD " 134 | - " D D " 135 | - " DDD " 136 | 137 | - names: [ F6 ] 138 | next: "F7" 139 | time: 120 140 | data: 141 | - " DDDDDDDDDDD " 142 | - " DDDD D " 143 | - " DDD D " 144 | - " D " 145 | - "D " 146 | - "D " 147 | - "D " 148 | - "D " 149 | - "D " 150 | - " D " 151 | - " D " 152 | - " " 153 | - " " 154 | 155 | - names: [ F7 ] 156 | next: "" 157 | time: 120 158 | data: 159 | - " DDDD DD D " 160 | - " DDD D " 161 | - " D D " 162 | - " D " 163 | - "D " 164 | - "D " 165 | - " " 166 | - " " 167 | - " " 168 | - " " 169 | - " " 170 | - " " 171 | - " " 172 | -------------------------------------------------------------------------------- /s-gameover.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Garrett D'Amore 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use file except in compliance with the License. 5 | # You may obtain a copy of the license at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | name: GameOver 17 | width: 23 18 | height: 1 19 | originx: 11 20 | originy: 1 21 | layer: 9 22 | glyphs: 23 | "_": 24 | background: color1 25 | foreground: color2 26 | display: " " 27 | 28 | "G": 29 | background: color1 30 | foreground: color2 31 | display: "G" 32 | 33 | "A": 34 | background: color1 35 | foreground: color2 36 | display: "A" 37 | 38 | "M": 39 | background: color1 40 | foreground: color2 41 | display: "M" 42 | 43 | "E": 44 | background: color1 45 | foreground: color2 46 | display: "E" 47 | 48 | "O": 49 | background: color1 50 | foreground: color2 51 | display: "O" 52 | 53 | "V": 54 | background: color1 55 | foreground: color2 56 | display: "V" 57 | 58 | "R": 59 | background: color1 60 | foreground: color2 61 | display: "R" 62 | 63 | "g": 64 | background: color1 65 | foreground: color3 66 | display: "G" 67 | 68 | "a": 69 | background: color1 70 | foreground: color3 71 | display: "A" 72 | 73 | "m": 74 | background: color1 75 | foreground: color3 76 | display: "M" 77 | 78 | "e": 79 | background: color1 80 | foreground: color3 81 | display: "E" 82 | 83 | "o": 84 | background: color1 85 | foreground: color3 86 | display: "O" 87 | 88 | "v": 89 | background: color1 90 | foreground: color3 91 | display: "V" 92 | 93 | "r": 94 | background: color1 95 | foreground: color3 96 | display: "R" 97 | 98 | "H": 99 | background: color1 100 | foreground: color4 101 | display: "G" 102 | 103 | "B": 104 | background: color1 105 | foreground: color4 106 | display: "A" 107 | 108 | "N": 109 | background: color1 110 | foreground: color4 111 | display: "M" 112 | 113 | "F": 114 | background: color1 115 | foreground: color4 116 | display: "E" 117 | 118 | "P": 119 | background: color1 120 | foreground: color4 121 | display: "O" 122 | 123 | "W": 124 | background: color1 125 | foreground: color4 126 | display: "V" 127 | 128 | "S": 129 | background: color1 130 | foreground: color4 131 | display: "R" 132 | 133 | "h": 134 | background: color1 135 | foreground: color4 136 | display: "G" 137 | 138 | "b": 139 | background: color1 140 | foreground: color4 141 | display: "A" 142 | 143 | "n": 144 | background: color1 145 | foreground: color4 146 | display: "M" 147 | 148 | "f": 149 | background: color1 150 | foreground: color4 151 | display: "E" 152 | 153 | "p": 154 | background: color1 155 | foreground: color4 156 | display: "O" 157 | 158 | "w": 159 | background: color1 160 | foreground: color4 161 | display: "V" 162 | 163 | "s": 164 | background: color1 165 | foreground: color4 166 | display: "R" 167 | 168 | "I": 169 | background: color1 170 | foreground: color5 171 | display: "G" 172 | 173 | "C": 174 | background: color1 175 | foreground: color5 176 | display: "A" 177 | 178 | "0": 179 | background: color1 180 | foreground: color5 181 | display: "M" 182 | 183 | "1": 184 | background: color1 185 | foreground: color5 186 | display: "E" 187 | 188 | "Q": 189 | background: color1 190 | foreground: color5 191 | display: "O" 192 | 193 | "X": 194 | background: color1 195 | foreground: color5 196 | display: "V" 197 | 198 | "T": 199 | background: color1 200 | foreground: color5 201 | display: "R" 202 | 203 | 204 | "i": 205 | background: color1 206 | foreground: color6 207 | display: "G" 208 | 209 | "c": 210 | background: color1 211 | foreground: color6 212 | display: "A" 213 | 214 | "2": 215 | background: color1 216 | foreground: color6 217 | display: "M" 218 | 219 | "3": 220 | background: color1 221 | foreground: color6 222 | display: "E" 223 | 224 | "q": 225 | background: color1 226 | foreground: color6 227 | display: "O" 228 | 229 | "x": 230 | background: color1 231 | foreground: color6 232 | display: "V" 233 | 234 | "t": 235 | background: color1 236 | foreground: color6 237 | display: "R" 238 | 239 | palette: 240 | color1: black 241 | color2: red 242 | color3: blue 243 | color4: lime 244 | color5: fuchsia 245 | color6: white 246 | 247 | frames: 248 | - names: [ F0 ] 249 | next: F1 250 | time: 50 251 | data: [ "___G_A_M_E___O_V_E_R___" ] 252 | 253 | - names: [ F2 ] 254 | next: F3 255 | time: 50 256 | data: [ "___H_B_N_F___P_W_F_S___" ] 257 | 258 | - names: [ F1 ] 259 | next: F2 260 | time: 50 261 | data: [ "___g_a_m_e___o_v_e_r___" ] 262 | 263 | - names: [ F3 ] 264 | next: F4 265 | time: 50 266 | data: [ "___h_b_n_f___p_w_f_s___" ] 267 | 268 | - names: [ F4 ] 269 | next: F5 270 | time: 50 271 | data: [ "___I_C_0_1___Q_X_1_T___" ] 272 | 273 | - names: [ F5 ] 274 | next: F0 275 | time: 50 276 | data: [ "___i_c_2_3___q_x_3_t___" ] 277 | -------------------------------------------------------------------------------- /level.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "math/rand" 19 | "time" 20 | 21 | "github.com/gdamore/tcell" 22 | "github.com/gdamore/tcell/views" 23 | ) 24 | 25 | type Level struct { 26 | name string 27 | title string 28 | width int 29 | height int 30 | startx int 31 | starty int 32 | game *Game 33 | layer int 34 | maxlayer int 35 | view *views.ViewPort 36 | maxtime time.Duration 37 | begin time.Time 38 | expired bool 39 | gravity float64 40 | clock AlarmClock 41 | stopped bool 42 | manager *SpriteManager 43 | terrain *Sprite 44 | data *LevelData 45 | win bool 46 | started bool 47 | } 48 | 49 | func NewLevelFromData(d *LevelData) *Level { 50 | lvl := &Level{data: d} 51 | 52 | lvl.name = d.Name 53 | lvl.title = d.Properties.PropString("title", lvl.name) 54 | 55 | timer := d.Properties.PropInt("timer", 300) 56 | lvl.gravity = d.Properties.PropFloat64("gravity", 0.0) 57 | lvl.width = d.Properties.PropInt("width", 0) 58 | lvl.height = d.Properties.PropInt("height", 0) 59 | if lvl.width == 0 || lvl.height == 0 { 60 | panic("level has no dimensions!") 61 | } 62 | lvl.maxtime = time.Duration(timer) * time.Second 63 | lvl.manager = NewSpriteManager(lvl.width, lvl.height) 64 | lvl.clock = NewAlarmClock() 65 | 66 | return lvl 67 | } 68 | 69 | func (l *Level) Name() string { 70 | return l.name 71 | } 72 | 73 | func (l *Level) Title() string { 74 | return l.title 75 | } 76 | 77 | func (l *Level) randomViewXY() (int, int) { 78 | x1, y1, x2, y2 := l.view.GetVisible() 79 | return rand.Intn(x2-x1+1) + x1, rand.Intn(y2-y1+1) + y1 80 | } 81 | 82 | func (l *Level) Update(now time.Time) { 83 | 84 | l.clock.Tick(now) 85 | l.manager.Update(now) 86 | 87 | if l.gravity != 0 { 88 | l.HandleEvent(&EventGravity{when: now, accel: l.gravity}) 89 | } 90 | 91 | if l.GetTimer() == 0 && !l.expired && !l.win && l.started { 92 | l.HandleEvent(&EventTimesUp{}) 93 | l.expired = true 94 | } 95 | } 96 | 97 | func (l *Level) SetView(v *views.ViewPort) { 98 | l.view = v 99 | v.SetContentSize(l.width, l.height, true) 100 | l.manager.SetView(v) 101 | } 102 | 103 | func (l *Level) Draw(v *views.ViewPort) { 104 | 105 | l.manager.Draw(v) 106 | } 107 | 108 | func (l *Level) Reset() { 109 | 110 | l.manager.Reset() 111 | l.clock.Reset() 112 | 113 | for name, plist := range l.data.Objects { 114 | props := GameObjectProps{} 115 | props["label"] = name 116 | for k, v := range plist { 117 | props[k] = v 118 | } 119 | MakeGameObject(l, props["class"], props) 120 | } 121 | l.begin = time.Now() 122 | l.stopped = false 123 | l.started = false 124 | l.win = false 125 | l.expired = false 126 | } 127 | 128 | func (l *Level) GetTimer() time.Duration { 129 | if l.win || !l.started { 130 | return l.maxtime 131 | } 132 | d := time.Now().Sub(l.begin) 133 | d = l.maxtime - d 134 | if d < 0 { 135 | d = 0 136 | } 137 | return d 138 | } 139 | 140 | func (l *Level) SetGame(g *Game) { 141 | l.game = g 142 | } 143 | 144 | func (l *Level) Layer() int { 145 | return LayerTerrain 146 | } 147 | 148 | func (l *Level) Size() (int, int) { 149 | return l.width, l.height 150 | } 151 | 152 | func (l *Level) MakeVisible(x, y int) { 153 | if l.view != nil { 154 | l.view.MakeVisible(x, y) 155 | } 156 | } 157 | 158 | func (l *Level) Center(x, y int) { 159 | if l.view != nil { 160 | l.view.Center(x, y) 161 | } 162 | } 163 | 164 | func (l *Level) Start() { 165 | l.started = true 166 | l.begin = time.Now() 167 | l.HandleEvent(&EventLevelStart{}) 168 | } 169 | 170 | func (l *Level) ShowPress() { 171 | x1, y1, x2, y2 := l.view.GetVisible() 172 | sprite := GetSprite("PressSpace") 173 | sprite.SetLayer(LayerDialog) 174 | sprite.SetFrame("F0") 175 | sprite.SetPosition(x1+(x2-x1)/2, y1+(y2-y1)/2+2) 176 | l.manager.AddSprite(sprite) 177 | } 178 | 179 | func (l *Level) ShowComplete() { 180 | x1, y1, x2, y2 := l.view.GetVisible() 181 | sprite := GetSprite("LevelComplete") 182 | sprite.SetLayer(LayerDialog) 183 | sprite.SetFrame("F0") 184 | sprite.SetPosition(x1+(x2-x1)/2, y1+(y2-y1)/2) 185 | l.manager.AddSprite(sprite) 186 | } 187 | 188 | func (l *Level) HandleEvent(ev tcell.Event) bool { 189 | switch ev := ev.(type) { 190 | case *tcell.EventKey: 191 | switch ev.Key() { 192 | case tcell.KeyCtrlR: 193 | // secret reset button 194 | l.Reset() 195 | } 196 | case *tcell.EventMouse: 197 | 198 | offx, offy, _, _ := l.view.GetVisible() 199 | px1, py1, px2, py2 := l.view.GetPhysical() 200 | mx, my := ev.Position() 201 | if mx < px1 || mx > px2 || my < py1 || my > py2 { 202 | // outside our view 203 | return false 204 | } 205 | if ev.Buttons()&tcell.Button1 != 0 { 206 | l.stopped = true 207 | l.view.Center(offx+mx-px1, offy+my-py1) 208 | } else if ev.Buttons()&tcell.Button2 != 0 { 209 | l.Reset() 210 | } else if ev.Buttons()&tcell.Button3 != 0 { 211 | l.Reset() 212 | } 213 | return true 214 | 215 | case *EventPlayerDeath: 216 | l.game.HandleEvent(ev) 217 | l.ShowPress() 218 | return true 219 | 220 | case *EventLevelComplete: 221 | l.win = true 222 | l.game.HandleEvent(ev) 223 | l.ShowComplete() 224 | l.ShowPress() 225 | return true 226 | 227 | case *EventGameOver: 228 | x1, y1, x2, y2 := l.view.GetVisible() 229 | sprite := GetSprite("GameOver") 230 | sprite.SetLayer(LayerDialog) 231 | sprite.SetFrame("F0") 232 | sprite.SetPosition(x1+(x2-x1)/2, y1+(y2-y1)/2) 233 | l.manager.AddSprite(sprite) 234 | 235 | case *EventTimesUp: 236 | bw := GetSprite("Blastwave") 237 | bw.Resize(l.width, l.height) 238 | bw.ScheduleFrame("0", time.Now().Add(2*time.Second)) 239 | l.AddSprite(bw) 240 | 241 | dur := time.Duration(0) 242 | for i := 0; i < 100; i++ { 243 | x, y := l.randomViewXY() 244 | sprite := GetSprite("Explosion") 245 | sprite.ScheduleFrame("0", time.Now().Add(dur)) 246 | sprite.SetPosition(x, y) 247 | l.AddSprite(sprite) 248 | dur += time.Millisecond * 5 249 | 250 | for j := 0; j < 4; j++ { 251 | x, y = l.randomViewXY() 252 | sprite = GetSprite("SmallExplosion") 253 | sprite.SetPosition(x, y) 254 | sprite.ScheduleFrame("0", time.Now().Add(dur)) 255 | l.AddSprite(sprite) 256 | dur += time.Millisecond * 50 257 | } 258 | } 259 | } 260 | if l.stopped { 261 | return false 262 | } 263 | 264 | return l.manager.HandleEvent(ev) 265 | } 266 | 267 | func (l *Level) AddSprite(sprite *Sprite) { 268 | l.manager.AddSprite(sprite) 269 | } 270 | 271 | func (l *Level) RemoveSprite(sprite *Sprite) { 272 | l.manager.RemoveSprite(sprite) 273 | } 274 | 275 | func (l *Level) AddAlarm(d time.Duration, h EventHandler) *Alarm { 276 | return l.clock.Schedule(d, h) 277 | } 278 | 279 | func (l *Level) RemoveAlarm(a *Alarm) { 280 | l.clock.Cancel(a) 281 | } 282 | -------------------------------------------------------------------------------- /f-ship.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/gdamore/tcell" 21 | ) 22 | 23 | type ship struct { 24 | ship *Sprite 25 | xthrust *Sprite 26 | ythrust *Sprite 27 | level *Level 28 | lastx int 29 | lasty int 30 | ximpulse float64 31 | yimpulse float64 32 | bspeed float64 33 | lastgrav time.Time 34 | launched bool 35 | dead bool 36 | } 37 | 38 | func (o *ship) shoot() { 39 | // TBD: shot limiter 40 | x1, y, x2, _ := o.ship.Bounds() 41 | _, vy := o.ship.Velocity() 42 | props := GameObjectProps{} 43 | props.PropSetInt("x", x1+(x2-x1)/2) 44 | props.PropSetInt("y", y-1) 45 | props.PropSetFloat64("vy", vy-o.bspeed) 46 | props.PropSetInt("delay", 10) 47 | MakeGameObject(o.level, "bullet", props) 48 | } 49 | 50 | func (o *ship) thrustUp() { 51 | vx, vy := o.ship.Velocity() 52 | vy -= o.yimpulse 53 | o.ship.SetVelocity(vx, vy) 54 | o.ythrust.SetFrame("UP") 55 | if !o.launched { 56 | if sprite := GetSprite("Exhaust"); sprite != nil { 57 | x, y := o.ship.Position() 58 | sprite.SetFrame("LIFTOFF") 59 | sprite.SetPosition(x, y) 60 | sprite.SetLayer(LayerThrust) 61 | o.level.AddSprite(sprite) 62 | } 63 | o.launched = true 64 | } 65 | } 66 | 67 | func (o *ship) thrustLeft() { 68 | vx, vy := o.ship.Velocity() 69 | vx -= o.ximpulse 70 | o.ship.SetVelocity(vx, vy) 71 | o.xthrust.SetFrame("LEFT") 72 | } 73 | 74 | func (o *ship) thrustRight() { 75 | vx, vy := o.ship.Velocity() 76 | vx += o.ximpulse 77 | o.ship.SetVelocity(vx, vy) 78 | o.xthrust.SetFrame("RIGHT") 79 | } 80 | 81 | func (o *ship) thrustDown() { 82 | vx, vy := o.ship.Velocity() 83 | vy += o.yimpulse 84 | o.ship.SetVelocity(vx, vy) 85 | o.ythrust.SetFrame("DOWN") 86 | } 87 | 88 | func (o *ship) destroy() { 89 | o.dead = true 90 | o.ship.Hide() 91 | 92 | x, y, _, _ := o.ship.Bounds() 93 | props := GameObjectProps{} 94 | props.PropSetInt("x", x) 95 | props.PropSetInt("y", y) 96 | props.PropSetInt("count", 3) 97 | MakeGameObject(o.level, "explosion", props) 98 | props = GameObjectProps{} 99 | props.PropSetInt("x", x) 100 | props.PropSetInt("y", y) 101 | props.PropSetInt("count", 3) 102 | MakeGameObject(o.level, "smexplosion", props) 103 | o.level.RemoveSprite(o.ship) 104 | o.level.HandleEvent(&EventPlayerDeath{}) 105 | } 106 | 107 | func (o *ship) adjustView() { 108 | x1, y1, x2, y2 := o.ship.Bounds() 109 | o.level.MakeVisible(x2+20, y2+8) 110 | o.level.MakeVisible(x1-20, y1-12) 111 | if o.ythrust != nil { 112 | o.ythrust.SetPosition(x1+(x2-x1)/2, y1) 113 | } 114 | if o.xthrust != nil { 115 | o.xthrust.SetPosition(x1+(x2-x1)/2, y1) 116 | } 117 | } 118 | 119 | func (o *ship) HandleEvent(ev tcell.Event) bool { 120 | if o.dead { 121 | return false 122 | } 123 | switch ev := ev.(type) { 124 | case *EventSpriteAccelerate: 125 | if ev.s != o.ship { 126 | return false 127 | } 128 | vx, _ := o.ship.Velocity() 129 | if vx >= 1.0 { 130 | o.ship.SetFrame("RIGHT") 131 | } else if vx <= -1.0 { 132 | o.ship.SetFrame("LEFT") 133 | } else { 134 | o.ship.SetFrame("FWD") 135 | } 136 | case *EventSpriteMove: 137 | // We don't let ship leave the map 138 | x, y := o.ship.Position() 139 | ox, oy := x, y 140 | vx, vy := o.ship.Velocity() 141 | w, h := o.level.Size() 142 | if x < 0 { 143 | x = 0 144 | if vx < 0 { 145 | vx = 0 146 | } 147 | } else if x >= w { 148 | x = w - 1 149 | if vx > 0 { 150 | vx = 0 151 | } 152 | } 153 | if y < 0 { 154 | y = 0 155 | if vy < 0 { 156 | vy = 0 157 | } 158 | } else if y >= h { 159 | y = h - 1 160 | if vy > 0 { 161 | vy = 0 162 | } 163 | } 164 | if ox != x || oy != y { 165 | o.ship.SetPosition(x, y) 166 | o.ship.SetVelocity(vx, vy) 167 | } 168 | 169 | if y == 0 { 170 | o.dead = true 171 | o.level.HandleEvent(&EventLevelComplete{}) 172 | } 173 | o.adjustView() 174 | case *EventGravity: 175 | now := ev.When() 176 | if !o.lastgrav.IsZero() { 177 | vx, vy := o.ship.Velocity() 178 | frac := float64(now.Sub(o.lastgrav)) 179 | frac /= float64(time.Second) 180 | vy += ev.Accel() * frac 181 | o.ship.SetVelocity(vx, vy) 182 | } 183 | o.lastgrav = now 184 | 185 | case *EventCollision: 186 | switch ev.Collider().Layer() { 187 | case LayerTerrain, LayerHazard, LayerShot: 188 | o.destroy() 189 | case LayerPad: 190 | // if we're on the pad, and not too 191 | // fast, then stay on the pad. 192 | // TODO: probably the max velocity (4.0) 193 | // should be tunable. 194 | vx, vy := o.ship.Velocity() 195 | x, y := o.ship.Position() 196 | if vx == 0 && vy > 0 && vy < 4.0 { 197 | y-- 198 | vy = 0 199 | o.ship.SetPosition(x, y) 200 | o.ship.SetVelocity(vx, vy) 201 | o.launched = false 202 | } else { 203 | o.destroy() 204 | } 205 | } 206 | 207 | case *EventTimesUp: 208 | o.destroy() 209 | 210 | case *tcell.EventKey: 211 | switch ev.Key() { 212 | 213 | case tcell.KeyLeft: 214 | o.thrustLeft() 215 | return true 216 | 217 | case tcell.KeyRight: 218 | o.thrustRight() 219 | return true 220 | 221 | case tcell.KeyUp: 222 | o.thrustUp() 223 | return true 224 | 225 | case tcell.KeyDown: 226 | o.thrustDown() 227 | return true 228 | 229 | case tcell.KeyRune: 230 | switch ev.Rune() { 231 | case ' ': 232 | o.shoot() 233 | return true 234 | case 'j', 'J': 235 | o.thrustLeft() 236 | return true 237 | case 'k', 'K': 238 | o.thrustRight() 239 | return true 240 | case 'i', 'I': 241 | o.thrustUp() 242 | return true 243 | case 'm', 'M': 244 | o.thrustDown() 245 | return true 246 | } 247 | } 248 | case *tcell.EventResize: 249 | x, y := o.ship.Position() 250 | o.level.Center(x, y) 251 | o.adjustView() 252 | } 253 | return false 254 | } 255 | 256 | func makeShip(level *Level, props GameObjectProps) error { 257 | 258 | x := props.PropInt("x", 0) 259 | y := props.PropInt("y", 0) 260 | velx := props.PropFloat64("vx", 0) 261 | vely := props.PropFloat64("vy", 0) 262 | frame := props.PropString("frame", "FWD") 263 | sname := props.PropString("sprite", "Ship") 264 | xthrust := props.PropString("xthrust", "Thrust") 265 | ythrust := props.PropString("ythrust", "Thrust") 266 | 267 | sprite := GetSprite(sname) 268 | o := &ship{ship: sprite, level: level} 269 | 270 | // How much impulse does each engine fire give us? 271 | o.yimpulse = props.PropFloat64("yimpulse", 3.5) 272 | o.ximpulse = props.PropFloat64("ximpulse", 4.5) 273 | o.bspeed = props.PropFloat64("vshot", 20.0) 274 | 275 | sprite.SetLayer(LayerPlayer) 276 | sprite.Watch(o) 277 | sprite.SetFrame(frame) 278 | sprite.SetPosition(x, y) 279 | sprite.SetVelocity(velx, vely) 280 | level.AddSprite(sprite) 281 | 282 | o.xthrust = GetSprite(xthrust) 283 | o.ythrust = GetSprite(ythrust) 284 | level.AddSprite(o.xthrust) 285 | level.AddSprite(o.ythrust) 286 | o.xthrust.SetLayer(LayerThrust) 287 | o.ythrust.SetLayer(LayerThrust) 288 | o.level.Center(x, y) 289 | o.adjustView() 290 | 291 | return nil 292 | } 293 | 294 | func init() { 295 | RegisterGameObjectMaker("ship", makeShip) 296 | } 297 | -------------------------------------------------------------------------------- /story.txt: -------------------------------------------------------------------------------- 1 | 2 | ESCAPE FROM PROXIMA 5 3 | --------------------- 4 | 5 | [ BACK STORY ] 6 | 7 | Three centuries ago, while making their first interstellar exploratory steps 8 | in slow generation ships, our people discovered Proxima 5, dozens of light 9 | years from our home world. 10 | 11 | Proxima 5 was not like other planets our people had discovered. Orbiting a 12 | red dwarf star, 4.24 light-years from the nearest life-bearing planet, 13 | Proxima 5 was found to be unique for several reasons. 14 | 15 | Proxima 5 is not the closest planet in its system to its primary; but it is the 16 | closest terrestrial planet in the system. (Gas giant Proxima 3 orbits closer 17 | at 2.3 AU. There is evidence of two other rocky inner planets that existed, 18 | once, perhaps millions or billions of years ago, but they are long gone, their 19 | disappearance a mystery. Today only two sparse belts of asteroids populate 20 | those orbits.) 21 | 22 | The fourth planet, Proxima 4, has a special relationship with Proxima 5. 23 | Proxima 4 is the more massive, massing about six times Proxima 5, and they 24 | whirl about each other at an average distance of a mere 30,000 km, though 25 | Proxima 5 swings around the center of gravity on a very elliptcal path. 26 | This is an unusual planetary configuration, and one that imparts considerable 27 | energy to both partners. If either planet had oceans, the tides would swing by 28 | hundreds of meters. 29 | 30 | At approximately 3 AUs from the diminutive primary, both Proxima 4 and 5 31 | receive far too little solar radiation to sustain life. However, the energy 32 | resulting from their whirling dance is sufficient to heat the insides of 33 | both planets quite substantially. 34 | 35 | On Proxima 5, this has fueled the evolution of life in the caverns and 36 | ancient lava tubes that run deep through the planet's crust and mantle. 37 | 38 | Amongst all this, our people also found artifacts, some dating back apparently 39 | many millions of years, indicating a star-faring culture. Not much remained, 40 | the youngest remains being no more recent than 27.2 million years, and 41 | Proxima 5's vigorous geology doing what it can to erase any long term 42 | evidence from the planet. 43 | 44 | It is unclear whether intelligent life evolved here, or merely was visiting, 45 | there is simply not enough left in the record. Perhaps another culture found 46 | this planet as interesting as we did. Much of those people, including their 47 | ultimate fate, remains a mystery to this day. 48 | 49 | When our people discovered Proxima 5, and the unique conditions of life here, 50 | it was determined that further study of the system was warranted, although 51 | the conditions were not deemed suitable for full-scale colonization. So 52 | a contingent a few dozen scientists and their families, and some support 53 | personnel were left behind, while the generation ship continued on to look 54 | for more suitable colonization prospects. 55 | 56 | Over a period of two centuries, the research colony here on Proxima 5 grew 57 | from a couple of dozen people to nearly a million scientists, their families, 58 | and supporting staff. Periodic communication with the home world and our 59 | other colonies occurred over light speed transmissions. Visits by star ship 60 | were rare. 61 | 62 | Then, 123 years ago, Proxima 5 lost contact with everyone else, including 63 | the home world. All of the transmisions we had been receiving just stopped. 64 | Theories abound -- was it an attack by hostile forces? Did our people exprience 65 | some sort of technological singularity? Was there some other disaster of 66 | interstellar proportions. We simply do not know, even to this day. 67 | 68 | After the shock of losing contact, and the initial social disruptions that 69 | resulted, our people on Proxima 5 colony hunkered down, and re-founded our 70 | culture, valuing scientific discovery and the arts above other endeavors. 71 | Ours became an enlightened society, and Proxima (for we had long ago dropped 72 | the “5”) became our true home. 73 | 74 | Fearing whatever fate befell the rest of our culture might happen to us as 75 | well, we kept to ourselves, and retreated ever deeper into the core of 76 | Proxima 5, seeking its secrets rather than looking outward. 77 | 78 | It was 27 years ago, when Xyxaz Zhovu cracked the secret of super-luminal 79 | travel. Initial short range explorations have been made, and the technology 80 | is sound. Since then, we have been producing a fleet of small craft, far 81 | underground, ready to take teams of our people back to the home world, 82 | to either re-establish contact, or to recolonize if necessary. 83 | 84 | Still our people have debated about the wisdom of wider exploration, fearing 85 | that whatever happened to our 123 years ago might befall us. Even unmanned 86 | probes represent a risk, in that they might give away our location to 87 | an unknown enemy. There is safety in hiding, and ours is not a culture of 88 | bravery. 89 | 90 | [ NOW ] 91 | 92 | Perhaps the mysterious disappearance of our people, or even the ancient 93 | culture that was here millions of years ago, is now understood. 94 | 95 | A black hole, massing some hundred stellar masses, traveling at a extrem 96 | velocities, has just entered our system. 97 | 98 | Our scientists have analyzed its path. 99 | 100 | Once every few million years, it's path brings it close to the unfortunately 101 | named Proxima system. Once every 27.2 of those millions of years, the path is 102 | close enough to cause local devastation. 103 | 104 | Very soon, this black hole will rip through the inner system, perhaps 105 | destroying more planets including Proxima 5 just as it apparently destroyed 106 | Proxima 1 and 2 so many millions of years ago. 107 | 108 | Commander, your starship training is of immediate utility. We no longer 109 | need concern ourselves with the risks of discovery, for our very existence 110 | is threatened by this Nemesis black hole. 111 | 112 | You need to evacuate as many people as you can from the planet's interior. 113 | They may be all that remains of our species, and our culture. 114 | 115 | Time is running out. The black hole's approach is rapid, and its 116 | gravitational effects are already causing disruptions throughout the system. 117 | The primary is undergoing giant fluctuations in its output, and the resulting 118 | flares have knocked out many of our systems, and confused others. Multiple 119 | blast waves have been detected leaving it, and they are headed here. 120 | 121 | The native fauna are running rampant, and defense systems intended to protect 122 | our people from the most dangerous of those are either offline or 123 | malfunctioning. Our own defense systems are now as much a threat to us as they 124 | are to anything else. You'll need to be wary of both the defenses and the 125 | critters. 126 | 127 | Your craft is waiting in the launch bay, in the deep caverns. The most 128 | important first evacuees are already enroute and will have boarded before 129 | you get there. You have only limited armament, a short range star drive, 130 | and limited time. 131 | 132 | Your journey to the surface, and ultimately out of the system, will be 133 | treacherous, but we are out of choices, and out of time. 134 | 135 | If you make it to the surface, take your squadron, and make for Sol, 136 | the nearest system. The conditions on the third planet were habitable 137 | when last surveyed 203 years ago. Hopefully the natives there, a primate 138 | species who call themselves "human", will be welcoming. They were just 139 | entering their industrial revolution at the time of the last survey. There 140 | are no other options within range. 141 | 142 | [ Blaring alert: BLASTWAVE APPROACHING -- TIME TO IMPACT 24 SENARI ] 143 | [ Blaring alert: PILOTS AND CREWS REPORT TO STARSHIPS IMMEDIATELY ] 144 | 145 | Oh, fozzle! I thought we had at least 200 senari before the first 146 | blast waves would hit; things are happening even faster than my worst fears. 147 | 148 | Get to your ship commander, our people are counting on you! Xevaru protect 149 | us all, I have to get to my family, and you have to save the few of us that 150 | you can. 151 | -------------------------------------------------------------------------------- /sprite.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Garrett D'Amore 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use file except in compliance with the License. 5 | // You may obtain a copy of the license at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/gdamore/tcell" 19 | "github.com/gdamore/tcell/views" 20 | "time" 21 | ) 22 | 23 | type spriteFrame struct { 24 | runes []rune 25 | styles []tcell.Style 26 | nextFrame string 27 | timer time.Duration 28 | } 29 | 30 | type Sprite struct { 31 | originx int 32 | originy int 33 | posx int 34 | posy int 35 | fposx float64 36 | fposy float64 37 | velx float64 38 | vely float64 39 | ptime time.Time 40 | width int 41 | height int 42 | frame string 43 | ftime time.Time 44 | layer int 45 | handlers map[EventHandler]struct{} 46 | sched time.Time 47 | schedFrame string 48 | 49 | // allow for multiple frames 50 | frames map[string]*spriteFrame 51 | } 52 | 53 | func NewSprite(w, h int) *Sprite { 54 | s := &Sprite{} 55 | // no display! 56 | s.frame = "" 57 | s.width = w 58 | s.height = h 59 | s.handlers = make(map[EventHandler]struct{}) 60 | s.frames = make(map[string]*spriteFrame) 61 | 62 | // default is to exist at layer 1 63 | s.layer = 1 64 | return s 65 | } 66 | 67 | func (s *Sprite) SetOrigin(x, y int) { 68 | // It is possible to set the origin outside of the 69 | // image. Don't do that! 70 | s.originx, s.originy = x, y 71 | } 72 | 73 | // AddFrame adds a frame, and return the frame number. 74 | func (s *Sprite) AddFrame(name string, runes []rune, styles []tcell.Style) { 75 | frame := &spriteFrame{} 76 | frame.runes = make([]rune, 0, s.width*s.height) 77 | frame.styles = make([]tcell.Style, 0, s.width*s.height) 78 | for _, r := range runes { 79 | frame.runes = append(frame.runes, r) 80 | } 81 | for _, st := range styles { 82 | frame.styles = append(frame.styles, st) 83 | } 84 | s.frames[name] = frame 85 | } 86 | 87 | // SetNextFrame is used to arrange for automatic frame transitions. 88 | // At the specified duration after this frame is made active, the frame 89 | // will advance to the frame specified by next. If next is -1, then 90 | // the sprite will be made invisible. If the duration is is zero, then 91 | // no automatic transition will occur. 92 | func (s *Sprite) SetNextFrame(name string, d time.Duration, next string) { 93 | if frame, ok := s.frames[name]; ok { 94 | frame.nextFrame = next 95 | frame.timer = d 96 | } 97 | } 98 | 99 | func (s *Sprite) SetFrame(frame string) { 100 | s.frame = frame 101 | s.ftime = time.Now() 102 | ev := &EventSpriteFrame{when: s.ftime, frame: frame, s: s} 103 | s.HandleEvent(ev) 104 | } 105 | 106 | // ScheduleFrame arranges to change the frame to the named frame 107 | // at some point near the given time. The purpose is to allow 108 | // deferring the initial display of a sprite. Only a single 109 | // frame change may be scheduled this way. 110 | func (s *Sprite) ScheduleFrame(frame string, when time.Time) { 111 | s.sched = when 112 | s.schedFrame = frame 113 | } 114 | 115 | // UnscheduleFrame cancels the scheduled change. 116 | func (s *Sprite) UnscheduleFrame() { 117 | s.ScheduleFrame("", time.Time{}) 118 | } 119 | 120 | func (s *Sprite) Frame() string { 121 | return s.frame 122 | } 123 | 124 | func (s *Sprite) Position() (int, int) { 125 | return s.posx, s.posy 126 | } 127 | 128 | func (s *Sprite) SetPosition(x, y int) { 129 | s.posx, s.posy = x, y 130 | s.fposx, s.fposy = float64(x), float64(y) 131 | s.ptime = time.Now() 132 | ev := &EventSpriteMove{when: s.ptime, s: s, x: x, y: y} 133 | s.HandleEvent(ev) 134 | } 135 | 136 | // Set the velocity of the sprite. The units are cells per second. 137 | // This cancels any previous but uncalculated motion, so don't call 138 | // this unless the values are actually different, otherwise the motion 139 | // may appear choppier than it should. 140 | func (s *Sprite) SetVelocity(velx, vely float64) { 141 | s.velx = velx 142 | s.vely = vely 143 | s.ptime = time.Now() 144 | ev := &EventSpriteAccelerate{when: s.ptime, s: s, x: velx, y: vely} 145 | s.HandleEvent(ev) 146 | } 147 | 148 | // Velocity returns the velocity of the sprite in cells per second. 149 | func (s *Sprite) Velocity() (float64, float64) { 150 | return s.velx, s.vely 151 | } 152 | 153 | func (s *Sprite) Update(now time.Time) { 154 | if !s.sched.IsZero() && s.sched.Before(now) { 155 | s.sched = time.Time{} 156 | s.frame = s.schedFrame 157 | s.ftime = now 158 | ev := &EventSpriteFrame{s: s, when: now, frame: s.frame} 159 | s.HandleEvent(ev) 160 | } else if frame, ok := s.frames[s.frame]; ok { 161 | if frame.timer != 0 && now.Sub(s.ftime) > frame.timer { 162 | s.frame = frame.nextFrame 163 | s.ftime = now 164 | ev := &EventSpriteFrame{s: s, when: now, frame: s.frame} 165 | s.HandleEvent(ev) 166 | } 167 | } 168 | if s.velx != 0 || s.vely != 0 { 169 | fac := float64(now.Sub(s.ptime)) / float64(time.Second) 170 | s.ptime = now 171 | 172 | s.fposx += s.velx * fac 173 | s.fposy += s.vely * fac 174 | ox, oy := s.posx, s.posy 175 | s.posx = int(s.fposx) 176 | s.posy = int(s.fposy) 177 | 178 | if ox != s.posx || oy != s.posy { 179 | ev := &EventSpriteMove{s: s, 180 | when: now, x: s.posx, y: s.posy} 181 | s.HandleEvent(ev) 182 | } 183 | } 184 | } 185 | 186 | func (s *Sprite) Draw(view views.View) { 187 | 188 | frame, ok := s.frames[s.frame] 189 | if !ok { 190 | return 191 | } 192 | styles := frame.styles 193 | runes := frame.runes 194 | if runes == nil { 195 | return 196 | } 197 | offx, offy := s.posx-s.originx, s.posy-s.originy 198 | for y := 0; y < s.height; y++ { 199 | for x := 0; x < s.width; x++ { 200 | i := y*s.width + x 201 | if len(runes) < i { 202 | return 203 | } 204 | if runes[i] == 0 { 205 | continue 206 | } 207 | style := tcell.StyleDefault 208 | if len(styles) >= i { 209 | style = styles[i] 210 | } 211 | view.SetContent(x+offx, y+offy, 212 | runes[i], nil, style) 213 | } 214 | } 215 | } 216 | 217 | // Resize resizes a sprite. If the sprite shrinks then the cells 218 | // are clipped from the end. If the sprite grows, then new cells 219 | // are added by copying to the last to the end. 220 | func (s *Sprite) Resize(w, h int) { 221 | ow, oh := s.width, s.height 222 | for _, f := range s.frames { 223 | nr := make([]rune, w*h) 224 | ns := make([]tcell.Style, w*h) 225 | if ow == 0 || oh == 0 { 226 | f.runes = nr 227 | f.styles = ns 228 | continue 229 | } 230 | for y := 0; y < h; y++ { 231 | for x := 0; x < w; x++ { 232 | if y >= oh { 233 | nr[y*w+x] = nr[(y-1)*w+x] 234 | ns[y*w+x] = ns[(y-1)*w+x] 235 | } else if x >= ow { 236 | nr[y*w+x] = nr[y*w+(x-1)] 237 | ns[y*w+x] = ns[y*w+(x-1)] 238 | } else { 239 | nr[y*w+x] = f.runes[y*ow+x] 240 | ns[y*w+x] = f.styles[y*ow+x] 241 | } 242 | } 243 | } 244 | f.runes = nr 245 | f.styles = ns 246 | } 247 | s.width = w 248 | s.height = h 249 | 250 | } 251 | 252 | // Collides checks if a collision at the given coordinates occurs. 253 | func (s *Sprite) Collides(x, y int) bool { 254 | x -= s.posx 255 | x += s.originx 256 | y -= s.posy 257 | y += s.originy 258 | if x < 0 || x >= s.width || y < 0 || y >= s.height { 259 | return false 260 | } 261 | if frame, ok := s.frames[s.frame]; ok { 262 | runes := frame.runes 263 | i := y*s.width + x 264 | if i >= len(runes) { 265 | return false 266 | } 267 | if runes[i] == 0 { 268 | return false 269 | } 270 | return true 271 | } 272 | return false 273 | } 274 | 275 | // Bounds returns the bounds of the sprite, for collision 276 | // detection purposes (See Collider interface) 277 | func (s *Sprite) Bounds() (x1, y1, x2, y2 int) { 278 | 279 | x1, y1 = s.posx-s.originx, s.posy-s.originy 280 | x2, y2 = x1+s.width-1, y1+s.height-1 281 | return x1, y1, x2, y2 282 | } 283 | 284 | // SetLayer sets the layer. This determines when the object is drawn, 285 | // higher layers are drawn after lower ones. This is also reported via 286 | // Layer() and may be used in collision event handlers. 287 | func (s *Sprite) SetLayer(layer int) { 288 | s.layer = layer 289 | } 290 | 291 | // Layer returns the layer at which the sprite exists. 292 | func (s *Sprite) Layer() int { 293 | return s.layer 294 | } 295 | 296 | func (s *Sprite) Visible() bool { 297 | _, ok := s.frames[s.frame] 298 | return ok 299 | } 300 | 301 | func (s *Sprite) Hide() { 302 | s.SetFrame("") 303 | } 304 | 305 | func (s *Sprite) HandleEvent(ev tcell.Event) bool { 306 | for h := range s.handlers { 307 | if h.HandleEvent(ev) { 308 | return true 309 | } 310 | } 311 | return false 312 | } 313 | 314 | // Watch adds the handler to the list that will be notified for sprite events. 315 | // Note that handlers may be called in any order. However, a handler that 316 | // returns true will consume the event, and no other handlers will receive 317 | // it afterwards. For most events, it probably is best to return false, 318 | // so that other game consumers can see the event as well. 319 | func (s *Sprite) Watch(h EventHandler) { 320 | s.handlers[h] = struct{}{} 321 | } 322 | 323 | // Unwatch removes the handler from the notify list for sprite events. 324 | func (s *Sprite) Unwatch(h EventHandler) { 325 | delete(s.handlers, h) 326 | } 327 | 328 | // In addition to EventCollision, we have these. 329 | 330 | // EventSpriteFrame is delivered to the watchers when the frame for 331 | // the sprite changes. If the frame is -1, then the sprite is no longer 332 | // visible. 333 | type EventSpriteFrame struct { 334 | when time.Time 335 | s *Sprite 336 | frame string 337 | } 338 | 339 | // When returns the time of the event. 340 | func (ev *EventSpriteFrame) When() time.Time { 341 | return ev.when 342 | } 343 | 344 | // Sprite returns the sprite that changed frames. 345 | func (ev *EventSpriteFrame) Sprite() *Sprite { 346 | return ev.s 347 | } 348 | 349 | // Frame returns the new frame number. It will be -1 if the 350 | // sprite is no longer visible. The caller is presumed to know 351 | // the details of the individual frames. 352 | func (ev *EventSpriteFrame) Frame() string { 353 | return ev.frame 354 | } 355 | 356 | // EventSpriteMove is delivered ot the watchers when the sprite 357 | // position changes. 358 | type EventSpriteMove struct { 359 | when time.Time 360 | s *Sprite 361 | x, y int 362 | } 363 | 364 | // When returns the time of the event. 365 | func (ev *EventSpriteMove) When() time.Time { 366 | return ev.when 367 | } 368 | 369 | // Sprite returns the sprite that moved. 370 | func (ev *EventSpriteMove) Sprite() *Sprite { 371 | return ev.s 372 | } 373 | 374 | // Position returns the new sprite position (X,Y). 375 | func (ev *EventSpriteMove) Position() (int, int) { 376 | return ev.x, ev.y 377 | } 378 | 379 | // EventSpriteAccelerate is delivered ot the watchers when the sprite 380 | // velocity changes. 381 | type EventSpriteAccelerate struct { 382 | when time.Time 383 | s *Sprite 384 | x, y float64 385 | } 386 | 387 | // When returns the time of the event. 388 | func (ev *EventSpriteAccelerate) When() time.Time { 389 | return ev.when 390 | } 391 | 392 | // Sprite returns the sprite that accelerated. 393 | func (ev *EventSpriteAccelerate) Sprite() *Sprite { 394 | return ev.s 395 | } 396 | 397 | // Position returns the new sprite velocity (X,Y). 398 | func (ev *EventSpriteAccelerate) Velocity() (float64, float64) { 399 | return ev.x, ev.y 400 | } 401 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /s-cavern1.go: -------------------------------------------------------------------------------- 1 | // Sprite data file 2 | // Generated automatically, do not edit! 3 | package main 4 | 5 | func init() { 6 | RegisterSpriteGobZ([]byte{ 7 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xdd, 0x4f, 0x8f, 0x1b, 0x35, 8 | 0x14, 0x00, 0x70, 0xbf, 0xcd, 0xa4, 0x5b, 0xaa, 0x4a, 0x88, 0x4b, 0x2f, 0x5c, 0x06, 0x8d, 0xb8, 9 | 0x21, 0x24, 0x2e, 0x9c, 0x0b, 0x94, 0x02, 0x12, 0x5a, 0x50, 0xa9, 0x04, 0xa8, 0x42, 0xab, 0x69, 10 | 0x3b, 0x4a, 0xa3, 0x6e, 0xb2, 0xcb, 0x24, 0x35, 0xb6, 0x25, 0x2a, 0x28, 0xe5, 0x5f, 0x69, 0x7b, 11 | 0xe0, 0x80, 0x84, 0x40, 0xe2, 0x6b, 0xf0, 0x61, 0x38, 0xf1, 0x3d, 0xd8, 0x41, 0x49, 0xd0, 0xd2, 12 | 0x6e, 0xff, 0x4d, 0x62, 0x3f, 0xfb, 0xd9, 0x7e, 0x73, 0xd9, 0xdd, 0xe9, 0x28, 0xf1, 0xfc, 0x3a, 13 | 0x93, 0x79, 0x7e, 0x7e, 0xb1, 0xbf, 0xec, 0xbe, 0x1e, 0x00, 0x9c, 0xfa, 0xe8, 0xa0, 0x1d, 0xcf, 14 | 0x9b, 0x73, 0xf5, 0xbc, 0x86, 0xee, 0x96, 0x80, 0xe7, 0xa0, 0xd8, 0xa9, 0x27, 0x0d, 0x9c, 0x16, 15 | 0x30, 0xfc, 0x78, 0x7c, 0x75, 0x7e, 0x0d, 0x0a, 0x01, 0x27, 0xde, 0x6d, 0xc6, 0xa3, 0x6b, 0xf3, 16 | 0xc5, 0xaf, 0xdb, 0x1f, 0xb4, 0xe3, 0xd1, 0x78, 0xfa, 0xc9, 0x03, 0xbf, 0x7f, 0xba, 0xf8, 0x7d, 17 | 0xf8, 0x7e, 0xad, 0x9b, 0x76, 0x79, 0xf0, 0x3b, 0x7b, 0xfa, 0xe0, 0xda, 0x0c, 0xba, 0xef, 0x04, 18 | 0x6c, 0x7f, 0x58, 0xef, 0x35, 0xf3, 0x79, 0x03, 0xdd, 0x0f, 0x02, 0x4e, 0x9c, 0x6f, 0xeb, 0x49, 19 | 0x33, 0x83, 0xee, 0xbe, 0x10, 0xe2, 0x95, 0xee, 0xdb, 0x02, 0xe0, 0xc5, 0x49, 0x7d, 0x70, 0x69, 20 | 0x36, 0x6f, 0xc7, 0xd3, 0xd1, 0x67, 0x93, 0x7a, 0x3c, 0x7d, 0x75, 0xd5, 0x9a, 0xe5, 0x2b, 0x2c, 21 | 0x5f, 0xe0, 0x34, 0x74, 0xb7, 0x85, 0x78, 0xbd, 0xfb, 0x66, 0x00, 0x5b, 0xdd, 0x6d, 0x01, 0x03, 22 | 0xd8, 0x3e, 0x37, 0x9e, 0x1d, 0xec, 0xd5, 0x7a, 0xd1, 0xc2, 0x53, 0xe7, 0xf7, 0xdb, 0x66, 0xd4, 23 | 0xee, 0xdf, 0x98, 0x5e, 0x5d, 0xfe, 0xf9, 0x66, 0x7d, 0xe5, 0xfa, 0xd1, 0x9f, 0x42, 0xbc, 0xd4, 24 | 0x7d, 0x5f, 0x00, 0xbc, 0xf0, 0xc0, 0x7b, 0xac, 0x7e, 0x2c, 0x5b, 0x73, 0x7a, 0x71, 0xc8, 0xcb, 25 | 0xdd, 0xbd, 0x2d, 0x80, 0x33, 0x97, 0x1e, 0x7c, 0xf3, 0x65, 0x2b, 0x57, 0x1e, 0xf7, 0x05, 0x74, 26 | 0x77, 0x84, 0x38, 0xd7, 0xfd, 0x38, 0x00, 0x78, 0xfe, 0x91, 0x7f, 0xbe, 0x23, 0xa0, 0x80, 0xe1, 27 | 0xce, 0xea, 0xa4, 0xee, 0x0a, 0x28, 0x76, 0x1a, 0x35, 0x5f, 0x34, 0xa4, 0xb8, 0x38, 0x9e, 0x34, 28 | 0x0b, 0x8b, 0x62, 0x75, 0xe0, 0x5d, 0x21, 0xc4, 0x99, 0xee, 0xa7, 0x2d, 0x80, 0x93, 0x97, 0x8e, 29 | 0xda, 0x70, 0x57, 0x2c, 0x5a, 0xf0, 0x0f, 0x9c, 0xb9, 0xd7, 0xdd, 0x82, 0xed, 0xb7, 0x6a, 0xd9, 30 | 0xb4, 0xd3, 0xd7, 0xe0, 0x70, 0xeb, 0x2b, 0x38, 0x84, 0x3f, 0x07, 0x27, 0x61, 0x1b, 0x2a, 0x18, 31 | 0xfc, 0xfd, 0xcb, 0xcf, 0x50, 0x4c, 0xf6, 0x67, 0x33, 0x28, 0xae, 0x8e, 0xdb, 0xb9, 0x80, 0x5d, 32 | 0x80, 0x5d, 0x18, 0x8e, 0xda, 0x7a, 0x36, 0x83, 0xe1, 0xe5, 0xbd, 0xfa, 0xca, 0x75, 0x01, 0x6a, 33 | 0x71, 0xdc, 0x6f, 0xab, 0x23, 0x8e, 0x76, 0x9a, 0xc5, 0xce, 0xdf, 0x8f, 0xed, 0xd4, 0x8b, 0x9d, 34 | 0xbf, 0x1e, 0xdb, 0x29, 0x17, 0x3b, 0xff, 0x38, 0xb6, 0xf3, 0x26, 0xc0, 0x4d, 0x18, 0x7e, 0x51, 35 | 0xcb, 0x66, 0xb6, 0xf8, 0x31, 0x6f, 0x5a, 0x01, 0x27, 0x8a, 0x76, 0xff, 0xca, 0xf5, 0x62, 0xd4, 36 | 0x36, 0x7a, 0xb5, 0xab, 0xb8, 0xbc, 0x77, 0xa3, 0x59, 0x1d, 0x54, 0xd4, 0x9f, 0xdf, 0xa8, 0x97, 37 | 0x2f, 0x31, 0x98, 0xd7, 0xd3, 0x55, 0x03, 0x8b, 0xbd, 0xf1, 0xa4, 0x59, 0xb6, 0x7e, 0x38, 0x6a, 38 | 0x9b, 0x66, 0x0a, 0x00, 0xb0, 0x7d, 0xf1, 0xed, 0x0b, 0x17, 0xde, 0x78, 0x6f, 0x67, 0xd0, 0xfd, 39 | 0x75, 0x08, 0x67, 0x4b, 0xde, 0xac, 0x36, 0x26, 0x64, 0x42, 0x26, 0x64, 0x42, 0xde, 0x98, 0x90, 40 | 0x09, 0x89, 0x10, 0xee, 0x12, 0xde, 0xfa, 0x9c, 0x42, 0xe8, 0x36, 0x1e, 0xc2, 0xd9, 0x8a, 0xee, 41 | 0xa6, 0xfa, 0x10, 0x86, 0x6e, 0x24, 0x69, 0xc2, 0x5e, 0x82, 0x4c, 0x68, 0x2d, 0xc8, 0x84, 0xd6, 42 | 0x82, 0x4c, 0x68, 0x2d, 0xc8, 0x84, 0xd6, 0x82, 0x4c, 0x68, 0x2d, 0xc8, 0x84, 0xd6, 0x82, 0x4c, 43 | 0xf8, 0xf8, 0x6d, 0x0d, 0x41, 0xc9, 0x84, 0x96, 0x84, 0xc1, 0x05, 0xa3, 0x27, 0x0c, 0x2f, 0x18, 44 | 0x3b, 0x21, 0x01, 0x41, 0xaa, 0x84, 0xba, 0x27, 0xa1, 0x61, 0xc2, 0x14, 0x0c, 0xc9, 0x86, 0xd6, 45 | 0xf1, 0x18, 0xd2, 0xed, 0xe0, 0x45, 0x63, 0x48, 0x38, 0xcd, 0xa0, 0x39, 0xb4, 0xf6, 0x65, 0xc8, 46 | 0x84, 0xd6, 0x86, 0x4c, 0x68, 0x6d, 0xc8, 0x84, 0xd6, 0x21, 0xb6, 0x61, 0x42, 0xeb, 0x5e, 0x8a, 47 | 0x61, 0x42, 0xeb, 0x8e, 0x9e, 0x61, 0x42, 0xeb, 0xbe, 0xb2, 0xc9, 0x87, 0x50, 0x51, 0xad, 0x48, 48 | 0x30, 0xb1, 0x10, 0x2a, 0xba, 0x55, 0x1d, 0x26, 0x0e, 0x42, 0x45, 0xb9, 0x32, 0xc6, 0xc4, 0x40, 49 | 0xa8, 0x68, 0x57, 0x17, 0x19, 0xfa, 0x84, 0x8a, 0x7a, 0x85, 0x16, 0x79, 0x42, 0xf2, 0x82, 0xe4, 50 | 0x09, 0xe9, 0x0b, 0x52, 0x27, 0x8c, 0x40, 0x90, 0x28, 0xa1, 0x8a, 0xa5, 0x52, 0x95, 0x6c, 0x68, 51 | 0x1d, 0x8d, 0x20, 0x59, 0xc2, 0x78, 0x04, 0xa9, 0x12, 0x46, 0x24, 0x48, 0x91, 0x50, 0x47, 0x56, 52 | 0xb5, 0x4f, 0xb1, 0x8f, 0x9c, 0x91, 0x21, 0xda, 0x8d, 0x9c, 0x8f, 0x21, 0xde, 0x67, 0x61, 0x36, 53 | 0x86, 0x1c, 0x17, 0x5a, 0x5b, 0x72, 0x5c, 0x68, 0x6d, 0xc8, 0x71, 0xa1, 0xb5, 0x21, 0xc7, 0x85, 54 | 0xd6, 0x86, 0x88, 0x84, 0x9b, 0x56, 0x54, 0x6a, 0xcb, 0x6a, 0x4c, 0xed, 0xd9, 0xd4, 0x2b, 0xa1, 55 | 0xb4, 0x30, 0x90, 0x61, 0x63, 0x01, 0x43, 0x83, 0xd0, 0xe6, 0x3a, 0x92, 0xa1, 0xe3, 0x29, 0x43, 56 | 0x81, 0xd0, 0xe6, 0x3a, 0x92, 0xe1, 0x63, 0x52, 0xe3, 0x9f, 0x50, 0xd9, 0x74, 0xe5, 0x5d, 0xa6, 57 | 0x01, 0x1c, 0x79, 0x4a, 0xef, 0x84, 0xaa, 0xa4, 0x42, 0xe8, 0xc6, 0x50, 0x7a, 0xbf, 0x91, 0x55, 58 | 0x49, 0x87, 0xd0, 0x85, 0xa1, 0xf4, 0xfe, 0x38, 0x51, 0x25, 0x25, 0x42, 0x7b, 0x43, 0x89, 0x1a, 59 | 0xd4, 0x28, 0x07, 0xad, 0xc0, 0x26, 0xac, 0x90, 0xf4, 0xdc, 0x10, 0xa2, 0x08, 0x12, 0x22, 0x94, 60 | 0xe8, 0xbd, 0x13, 0x1c, 0x41, 0x3a, 0x84, 0x12, 0xbd, 0x83, 0x87, 0x24, 0x48, 0x86, 0x50, 0xa2, 61 | 0xf4, 0x91, 0x15, 0x52, 0x4b, 0x9e, 0xfa, 0x1e, 0xc6, 0x77, 0xc6, 0x43, 0x22, 0xa6, 0x19, 0x82, 62 | 0x08, 0x5a, 0x1a, 0xa2, 0x0a, 0xae, 0x4d, 0x18, 0x48, 0xd0, 0xca, 0x50, 0xe1, 0x0e, 0x8a, 0xae, 63 | 0x49, 0x18, 0x4c, 0xd0, 0xc2, 0x50, 0x21, 0x27, 0x0c, 0xfb, 0x13, 0x2a, 0x44, 0xb5, 0x5e, 0xaf, 64 | 0x4f, 0x20, 0xf3, 0x6b, 0xac, 0x08, 0x83, 0x0b, 0x96, 0x14, 0x72, 0xe7, 0x36, 0x84, 0xe1, 0x05, 65 | 0x4b, 0x0a, 0xa3, 0x0f, 0x16, 0x84, 0x04, 0x04, 0x4b, 0x02, 0x82, 0x72, 0x53, 0x42, 0x85, 0xaa, 66 | 0xd7, 0xf7, 0x1d, 0x64, 0x00, 0x37, 0xe9, 0xea, 0x71, 0xa2, 0x59, 0xd0, 0xfa, 0x89, 0xac, 0x63, 67 | 0x12, 0x74, 0x97, 0xf5, 0x97, 0x2e, 0x83, 0x1a, 0x1d, 0x91, 0x20, 0x7e, 0xa6, 0x7f, 0xb3, 0xc7, 68 | 0x89, 0x46, 0xe2, 0xeb, 0x79, 0xc6, 0x32, 0x44, 0xae, 0xd5, 0x75, 0x68, 0xad, 0x51, 0xc2, 0xde, 69 | 0x9e, 0xe7, 0x29, 0x83, 0xe4, 0xab, 0x9d, 0xf7, 0x4e, 0x34, 0x1e, 0xe1, 0xb3, 0xce, 0x53, 0x86, 70 | 0xc9, 0xf9, 0xbb, 0xef, 0xe0, 0x69, 0x9c, 0x02, 0xc7, 0x67, 0xbf, 0xb6, 0xf4, 0x9c, 0xe7, 0xc2, 71 | 0x23, 0xc4, 0x66, 0xf4, 0x35, 0x90, 0xe8, 0x3c, 0xd5, 0xb0, 0x66, 0xa6, 0xa6, 0x8c, 0xcc, 0xd0, 72 | 0x47, 0x7d, 0x97, 0x4b, 0x42, 0x82, 0x86, 0x3e, 0x6a, 0xe4, 0xd6, 0x24, 0xd4, 0x91, 0x19, 0x6a, 73 | 0x0f, 0x86, 0x1b, 0x0e, 0x3f, 0x69, 0xf7, 0xf5, 0xb6, 0x44, 0x15, 0xf1, 0xea, 0x0b, 0x23, 0x32, 74 | 0x24, 0x5b, 0x5f, 0x18, 0x8f, 0x21, 0xdd, 0xfa, 0xc2, 0x68, 0x0c, 0x69, 0xd7, 0x17, 0x6a, 0x94, 75 | 0xfa, 0x6f, 0x8a, 0x1f, 0x86, 0x88, 0xf5, 0x85, 0x3a, 0x17, 0x43, 0xc4, 0xfa, 0xc2, 0x5c, 0xae, 76 | 0xc3, 0x0a, 0x8f, 0x30, 0x97, 0x7b, 0xd9, 0x5b, 0xb9, 0x3a, 0xf5, 0xfb, 0xfa, 0x49, 0x37, 0xa9, 77 | 0xcd, 0xf5, 0xe8, 0xba, 0xca, 0x95, 0xb8, 0xe1, 0x13, 0x4c, 0x14, 0x25, 0x42, 0xe2, 0x86, 0x8f, 78 | 0x37, 0x51, 0x25, 0x29, 0x42, 0xda, 0x86, 0x18, 0xc3, 0xf1, 0x88, 0x5f, 0xdd, 0x21, 0x64, 0xe9, 79 | 0x74, 0x44, 0xde, 0xf8, 0x23, 0xec, 0xad, 0x68, 0xa2, 0x12, 0x7c, 0xa4, 0xbd, 0xc8, 0x13, 0x4e, 80 | 0x91, 0x30, 0x74, 0xfe, 0xad, 0x5e, 0xe3, 0x93, 0x30, 0x68, 0x5f, 0x1a, 0xef, 0x0b, 0xd1, 0xc6, 81 | 0x2f, 0xe1, 0x5a, 0x71, 0xad, 0x79, 0x0a, 0x84, 0x09, 0x89, 0xf6, 0x94, 0xde, 0x9e, 0x8f, 0x99, 82 | 0xe3, 0xac, 0x0c, 0xd5, 0x1a, 0xd7, 0x6a, 0x10, 0x41, 0x3f, 0x93, 0xef, 0x59, 0x18, 0xaa, 0x35, 83 | 0xee, 0xf7, 0x30, 0x82, 0x9e, 0xe6, 0x2f, 0xdc, 0xd8, 0x50, 0xad, 0xf1, 0x99, 0x19, 0x48, 0xd0, 84 | 0xef, 0xa4, 0xcc, 0x1a, 0x33, 0xf7, 0xa9, 0x3c, 0x56, 0x22, 0x85, 0x23, 0x74, 0x24, 0x69, 0x48, 85 | 0x09, 0x86, 0x99, 0xa0, 0x1e, 0xc1, 0x30, 0x9c, 0x60, 0xa0, 0x39, 0xfe, 0x9d, 0x1b, 0x06, 0x14, 86 | 0x0c, 0xb5, 0x4c, 0x42, 0xa8, 0x39, 0xd1, 0x30, 0x96, 0x1e, 0x0c, 0xb5, 0xd2, 0x84, 0x4e, 0xc4, 87 | 0x2f, 0xe4, 0x62, 0x1d, 0x3a, 0x15, 0xc1, 0x80, 0xeb, 0x9d, 0xe8, 0x44, 0x04, 0x43, 0x2e, 0x19, 88 | 0x93, 0x88, 0x60, 0x36, 0x84, 0x88, 0xe7, 0xc1, 0x84, 0x11, 0x13, 0xaa, 0x34, 0x6e, 0xe3, 0x50, 89 | 0x84, 0x2a, 0x52, 0x2d, 0x3a, 0x84, 0x2a, 0x15, 0xbd, 0x60, 0x84, 0x69, 0x09, 0x86, 0x20, 0x4c, 90 | 0x4c, 0x30, 0x91, 0x75, 0xf0, 0x82, 0xae, 0xba, 0x9f, 0xc4, 0x3a, 0x78, 0x41, 0x05, 0x93, 0x58, 91 | 0x07, 0x2f, 0xac, 0x60, 0x9c, 0xeb, 0xe0, 0xc9, 0x87, 0x7a, 0xd8, 0x81, 0x05, 0x3d, 0x10, 0xaa, 92 | 0x48, 0x3a, 0x19, 0x74, 0x09, 0x15, 0x66, 0x39, 0x4b, 0x16, 0x84, 0x2a, 0x9e, 0xbe, 0x2e, 0x51, 93 | 0x42, 0xe4, 0x8a, 0xa0, 0xc4, 0x09, 0xbd, 0xa4, 0x11, 0x4c, 0xca, 0x84, 0x9e, 0x12, 0x31, 0x26, 94 | 0x5d, 0x42, 0x6f, 0xa9, 0x2c, 0x93, 0x2a, 0xa1, 0xc7, 0x64, 0xa0, 0x49, 0x93, 0x50, 0x25, 0x90, 95 | 0x8c, 0x0e, 0x4b, 0xa8, 0x4a, 0x26, 0x8c, 0xe6, 0x2e, 0x4e, 0x94, 0x50, 0x95, 0x4c, 0x18, 0x87, 96 | 0xa0, 0x4c, 0x36, 0xb4, 0xce, 0x4e, 0xd0, 0x39, 0x61, 0x7e, 0x82, 0xae, 0x09, 0x33, 0x14, 0x74, 97 | 0x4c, 0xa8, 0x32, 0x10, 0xc3, 0x25, 0xcc, 0x52, 0xd0, 0x29, 0x61, 0x9e, 0x82, 0x2e, 0x09, 0x33, 98 | 0x15, 0x74, 0x48, 0xa8, 0x32, 0xd4, 0x73, 0x4b, 0x98, 0xad, 0xa0, 0x33, 0xc2, 0x7c, 0x05, 0x5d, 99 | 0x11, 0x66, 0x2c, 0xe8, 0x8a, 0xb0, 0xaf, 0x48, 0xcc, 0xe3, 0xc5, 0xc1, 0x09, 0x65, 0xc5, 0x84, 100 | 0x76, 0x84, 0xb2, 0x62, 0x42, 0x3b, 0x42, 0x59, 0x31, 0xa1, 0xe5, 0xd3, 0xc4, 0xac, 0xf9, 0xd4, 101 | 0x31, 0xb9, 0x11, 0xaa, 0x9e, 0x26, 0x2a, 0xee, 0xe2, 0x19, 0x44, 0x42, 0xd5, 0xd3, 0x44, 0xc5, 102 | 0x5e, 0x80, 0x84, 0x46, 0xd8, 0x53, 0xc6, 0xa8, 0xf8, 0x8b, 0xb8, 0xb0, 0x08, 0x43, 0x7d, 0xb7, 103 | 0x98, 0x8a, 0xb5, 0x8b, 0x1b, 0x39, 0x73, 0x43, 0x27, 0x8f, 0x93, 0xbc, 0x0d, 0xdd, 0x04, 0x35, 104 | 0x59, 0x1b, 0x3a, 0x0a, 0xad, 0x73, 0x36, 0x74, 0x95, 0xec, 0xca, 0xd8, 0xd0, 0x59, 0xca, 0x35, 105 | 0x5f, 0x43, 0x77, 0x89, 0xff, 0x6c, 0x0d, 0x11, 0xca, 0x92, 0x74, 0x66, 0x86, 0x18, 0xc5, 0x71, 106 | 0x99, 0x19, 0xa2, 0x94, 0x68, 0xe6, 0x65, 0x88, 0x53, 0x28, 0x9c, 0x95, 0x21, 0x52, 0xb9, 0x7a, 107 | 0x08, 0xbf, 0x84, 0x1e, 0x27, 0x4c, 0xc8, 0x84, 0x4c, 0x98, 0x06, 0xa1, 0x4a, 0xb9, 0x3c, 0xdd, 108 | 0x0b, 0xa1, 0x2a, 0x99, 0x90, 0xaa, 0x60, 0x2e, 0x84, 0xaa, 0x64, 0x42, 0xbf, 0x2a, 0xd1, 0x8e, 109 | 0x99, 0xac, 0x45, 0x48, 0x52, 0x90, 0x8c, 0x61, 0x1f, 0x42, 0xa2, 0x82, 0x54, 0x0c, 0x7b, 0x10, 110 | 0x92, 0x15, 0x24, 0x62, 0xf8, 0x1f, 0xa1, 0x2e, 0x79, 0x33, 0x96, 0x57, 0x21, 0x1b, 0x6e, 0x6c, 111 | 0x78, 0x74, 0x23, 0xb3, 0xe1, 0xa6, 0x86, 0xff, 0x7f, 0x16, 0xb2, 0xe1, 0x86, 0x86, 0x16, 0xa1, 112 | 0x75, 0x5c, 0x3a, 0x24, 0xe7, 0xb5, 0xce, 0xe2, 0x59, 0xc1, 0x84, 0x3e, 0x0c, 0xb3, 0x21, 0xc4, 113 | 0x33, 0xec, 0x4f, 0xa8, 0x72, 0x8f, 0xff, 0xac, 0x09, 0x15, 0xc7, 0xd0, 0x96, 0x84, 0x8a, 0xe3, 114 | 0x3f, 0x4b, 0x42, 0xc5, 0xf1, 0xdf, 0xa6, 0x84, 0x89, 0xc6, 0xdb, 0xd2, 0xe7, 0x55, 0xa8, 0x59, 115 | 0xd0, 0xfa, 0x46, 0xd6, 0x2c, 0x68, 0xfd, 0x59, 0xa8, 0x59, 0x30, 0xc8, 0x50, 0xbc, 0x8e, 0xe3, 116 | 0x59, 0xa2, 0x5c, 0x0c, 0x6d, 0x61, 0x4d, 0x44, 0x1a, 0x85, 0xe1, 0xc3, 0x71, 0x06, 0x35, 0xc2, 117 | 0x18, 0x0c, 0x8f, 0x45, 0x6a, 0xe4, 0x08, 0xe9, 0x1b, 0x1e, 0x8f, 0x75, 0xe9, 0x11, 0xc6, 0xf6, 118 | 0x2c, 0x92, 0x44, 0x09, 0xe3, 0x31, 0x94, 0x15, 0x55, 0xc2, 0x58, 0x0c, 0x65, 0x45, 0x97, 0x30, 119 | 0x0e, 0x43, 0x59, 0x51, 0x26, 0x74, 0x6d, 0x28, 0x5d, 0xa6, 0x7b, 0x09, 0x87, 0xd6, 0x78, 0x86, 120 | 0xb2, 0xca, 0x91, 0xd0, 0xa5, 0xa1, 0xac, 0xf2, 0x24, 0x74, 0x67, 0x28, 0xab, 0x5c, 0x09, 0x9d, 121 | 0x65, 0x6e, 0x8d, 0xa3, 0xff, 0x2a, 0x49, 0x3f, 0xcd, 0x80, 0x96, 0xfb, 0x36, 0x6e, 0x46, 0x15, 122 | 0xab, 0x48, 0x09, 0x15, 0x7e, 0xce, 0x3e, 0x71, 0x42, 0xe5, 0x61, 0xdc, 0x23, 0x6d, 0x42, 0x3f, 123 | 0xb3, 0x44, 0x6a, 0x8a, 0x59, 0xeb, 0xa8, 0x04, 0xab, 0x4a, 0xdf, 0x7c, 0xd6, 0x16, 0x49, 0xd6, 124 | 0x3a, 0x98, 0x60, 0x0f, 0xc3, 0x2a, 0x4e, 0x42, 0x9f, 0x73, 0xbd, 0x26, 0x4a, 0xe8, 0x9a, 0xaf, 125 | 0x22, 0xb4, 0x31, 0x21, 0x13, 0x66, 0x43, 0xb8, 0xeb, 0x78, 0xa3, 0x44, 0x28, 0xc4, 0xbf, 0x01, 126 | 0x00, 0x00, 0xff, 0xff, 0x63, 0x60, 0xd7, 0x55, 0x17, 0x18, 0x01, 0x00, 127 | }) 128 | } 129 | --------------------------------------------------------------------------------