├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── doc
└── screenshots.gif
└── src
├── alien.cpp
├── alien.h
├── display.h
├── eadkpp.h
├── icon.png
├── life.cpp
├── life.h
├── main.cpp
├── palette.h
├── rocket.cpp
├── rocket.h
├── score.cpp
├── score.h
├── spaceship.cpp
└── spaceship.h
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on: [pull_request, push]
3 |
4 | jobs:
5 | build:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: Checkout code
9 | uses: actions/checkout@v3
10 | - name: Install C++ toolchain
11 | uses: numworks/setup-arm-toolchain@latest
12 | - name: Run make
13 | run: make
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2021-2022 NumWorks. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
7 | 3. The name of NumWorks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
8 |
9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | Q ?= @
2 | CC = arm-none-eabi-gcc
3 | CXX = arm-none-eabi-g++
4 | BUILD_DIR = target
5 | NWLINK = npx --yes -- nwlink@0.0.16
6 | LINK_GC = 1
7 | LTO = 1
8 |
9 | define object_for
10 | $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1))))
11 | endef
12 |
13 | src = $(addprefix src/,\
14 | alien.cpp \
15 | life.cpp \
16 | main.cpp \
17 | rocket.cpp \
18 | spaceship.cpp \
19 | score.cpp \
20 | )
21 |
22 | CPPFLAGS = -std=c++11 -fno-exceptions
23 | CPPFLAGS += -Os -Wall
24 | CPPFLAGS += $(shell $(NWLINK) eadk-cflags)
25 | LDFLAGS = -Wl,--relocatable
26 | LDFLAGS += -nostartfiles
27 | LDFLAGS += --specs=nano.specs
28 |
29 | ifeq ($(LINK_GC),1)
30 | CPPFLAGS += -fdata-sections -ffunction-sections
31 | LDFLAGS += -Wl,-e,main -Wl,-u,eadk_app_name -Wl,-u,eadk_app_icon -Wl,-u,eadk_api_level
32 | LDFLAGS += -Wl,--gc-sections
33 | endif
34 |
35 | ifeq ($(LTO),1)
36 | CPPFLAGS += -flto -fno-fat-lto-objects
37 | CPPFLAGS += -fwhole-program
38 | CPPFLAGS += -fvisibility=internal
39 | LDFLAGS += -flinker-output=nolto-rel
40 | endif
41 |
42 | .PHONY: build
43 | build: $(BUILD_DIR)/voord.bin
44 |
45 | .PHONY: run
46 | run: $(BUILD_DIR)/voord.nwa
47 | @echo "INSTALL $<"
48 | $(Q) $(NWLINK) install-nwa $<
49 |
50 | $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.nwa
51 | @echo "BIN $@"
52 | $(Q) $(NWLINK) nwa-bin $< $@
53 |
54 | $(BUILD_DIR)/voord.nwa: $(call object_for,$(src)) $(BUILD_DIR)/icon.o
55 | @echo "LD $@"
56 | $(Q) $(CC) $(CPPFLAGS) $(LDFLAGS) $^ -o $@
57 |
58 | $(addprefix $(BUILD_DIR)/,%.o): %.cpp | $(BUILD_DIR)
59 | @echo "CXX $^"
60 | $(Q) $(CXX) $(CPPFLAGS) $(SFLAGS) -c $^ -o $@
61 |
62 | $(BUILD_DIR)/icon.o: src/icon.png
63 | @echo "ICON $<"
64 | $(Q) $(NWLINK) png-icon-o $< $@
65 |
66 | .PRECIOUS: $(BUILD_DIR)
67 | $(BUILD_DIR):
68 | $(Q) mkdir -p $@/src
69 |
70 | .PHONY: clean
71 | clean:
72 | @echo "CLEAN"
73 | $(Q) rm -rf $(BUILD_DIR)
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sample C++ app for Epsilon
2 |
3 | [](https://github.com/numworks/epsilon-sample-app-cpp/actions/workflows/build.yml)
4 |
5 |
6 |
7 | This is a sample C++ app to use on a [NumWorks calculator](https://www.numworks.com).
8 |
9 | ```cpp
10 |
11 | using namespace EADK;
12 |
13 | void eadk_main() {
14 | Display::pushRectUniform(
15 | Display::Rect(0, 0, 320, 240),
16 | Display::Color(0x000000)
17 | );
18 | while (1) {
19 | Keyboard::State kbd = Keyboard::scan();
20 | if (kbd.keyDown(Keyboard::Key::OK)) {
21 | spaceship.createRockets();
22 | }
23 | if (kbd.keyDown(Keyboard::Key::Up)) {
24 | spaceship.move(0, -Spaceship::k_step);
25 | }
26 | refreshScene();
27 | }
28 | }
29 | ```
30 |
31 | ## Build the app
32 |
33 | To build this sample app, you will need to install the [embedded ARM toolchain](https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain) and [Node.js](https://nodejs.org/en/). The C SDK for Epsilon apps is shipped as an npm module called [nwlink](https://www.npmjs.com/package/nwlink) that will automatically be installed at compile time.
34 |
35 | ```shell
36 | brew install numworks/tap/arm-none-eabi-gcc node # Or equivalent on your OS
37 | make
38 | ```
39 |
40 | You should now have a `target/voord.nwa` file that you can distribute! Anyone can now install it on their calculator from the [NumWorks online uploader](https://my.numworks.com/apps).
41 |
42 | ## Run the app locally
43 |
44 | To run the app on your development machine, you can use the following command
45 |
46 | ```shell
47 | # Now connect your NumWorks calculator to your computer using the USB cable
48 | make run
49 | ```
50 |
51 | ## License
52 |
53 | This sample app is distributed under the terms of the BSD License. See LICENSE for details.
54 |
55 | ## Trademarks
56 |
57 | NumWorks is a registered trademark.
58 |
--------------------------------------------------------------------------------
/doc/screenshots.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/numworks/epsilon-sample-app-cpp/1bccbd1459c39171956bf809296b4f73dfdd070e/doc/screenshots.gif
--------------------------------------------------------------------------------
/src/alien.cpp:
--------------------------------------------------------------------------------
1 | #include "alien.h"
2 | #include "palette.h"
3 | #include "stdlib.h"
4 |
5 | Alien::Alien(int x) :
6 | m_x(x),
7 | m_y(2*Display::CommonVerticalMargin)
8 | {
9 | if (!isGhost()) {
10 | draw(Orange);
11 | }
12 | }
13 |
14 | void Alien::hide() const {
15 | EADK::Display::pushRectUniform(EADK::Rect(m_x - k_width/2, m_y - k_height/2, k_width, k_height), Black);
16 | }
17 |
18 | void Alien::draw(const EADK::Color c) const {
19 | int xMin = m_x - k_width/2;
20 | int xMax = xMin + k_width;
21 | int yMin = m_y - k_height/2;
22 | int yMax = yMin + k_height;
23 | EADK::Display::pushRectUniform(EADK::Rect(xMin, yMin, k_width, k_height - 4), c);
24 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 3, yMax - 4, 2, 4), c);
25 | EADK::Display::pushRectUniform(EADK::Rect(xMax - 4 - 2, yMax - 4, 2, 4), c);
26 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 2, yMin + 3, 3, 7), Black);
27 | EADK::Display::pushRectUniform(EADK::Rect(xMax - 2 - 3, yMin + 3, 3, 7), Black);
28 | }
29 |
30 | void Alien::step() {
31 | if (!isGhost()) {
32 | hide();
33 | m_y += k_step;
34 | if (m_y >= EADK::Screen::Height) {
35 | ghostify();
36 | }
37 | if (!isGhost()) {
38 | draw(Orange);
39 | }
40 | }
41 | }
42 |
43 | bool Alien::tryToHit(Spaceship * s) {
44 | if (isGhost()) {
45 | return false;
46 | }
47 | if (abs(m_x - s->x()) < Spaceship::k_width/2 + k_width/2 &&
48 | abs(m_y - s->y()) < Spaceship::k_height/2 + k_height/2) {
49 | hide();
50 | ghostify();
51 | return s->hit();
52 | }
53 | return false;
54 | }
55 |
56 | void Alien::killed() {
57 | for (int i = 0; i < 5; i++) {
58 | draw(Green);
59 | EADK::Timing::msleep(10);
60 | draw(Orange);
61 | EADK::Timing::msleep(10);
62 | }
63 | hide();
64 | ghostify();
65 | }
66 |
--------------------------------------------------------------------------------
/src/alien.h:
--------------------------------------------------------------------------------
1 | #ifndef ALIEN_H
2 | #define ALIEN_H
3 |
4 | #include "eadkpp.h"
5 | #include "spaceship.h"
6 |
7 | class Alien {
8 | public:
9 | Alien(int x = -1);
10 | int x() const { return m_x; }
11 | int y() const { return m_y; }
12 | void step();
13 | bool tryToHit(Spaceship * s);
14 | bool isGhost() const { return m_x == -1; }
15 | void killed();
16 | static constexpr int k_stepPeriod = 10;
17 | static constexpr int k_materializationPeriod = 20;
18 | static constexpr int k_width = 15;
19 | static constexpr int k_height = 18;
20 | private:
21 | void draw(const EADK::Color c) const;
22 | void hide() const;
23 | void ghostify() { m_x = -1; }
24 | static constexpr int k_step = 10;
25 | int m_x;
26 | int m_y;
27 | };
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/src/display.h:
--------------------------------------------------------------------------------
1 | #ifndef DISPLAY_H
2 | #define DISPLAY_H
3 |
4 | class Display {
5 | public:
6 | constexpr static int CommonVerticalMargin = 20;
7 | constexpr static int CommonHorizontalMargin = 20;
8 | };
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/src/eadkpp.h:
--------------------------------------------------------------------------------
1 | #ifndef EADKPP_H
2 | #define EADKPP_H
3 |
4 | extern "C" {
5 | #include
6 | }
7 |
8 | namespace EADK {
9 |
10 | class Color {
11 | public:
12 | constexpr Color(uint32_t rgb) : m_value(((rgb&0xF80000)>>8)|((rgb&0x00FC00)>>5)|((rgb&0x0000F8)>>3)) {}
13 | constexpr operator eadk_color_t() const { return (eadk_color_t)m_value; }
14 | private:
15 | uint16_t m_value;
16 | };
17 | static_assert(sizeof(EADK::Color) == sizeof(eadk_color_t), "EADK::Color should match eadk_color_t");
18 |
19 | class Point {
20 | public:
21 | constexpr Point(int x, int y) :
22 | m_x(x), m_y(y) {}
23 | uint16_t x() const { return m_x; }
24 | uint16_t y() const { return m_y; }
25 | constexpr operator eadk_point_t() const { return *reinterpret_cast(this); }
26 | private:
27 | uint16_t m_x;
28 | uint16_t m_y;
29 | };
30 | static_assert(sizeof(EADK::Point) == sizeof(eadk_point_t), "EADK::Point should match eadk_point_t");
31 |
32 | class Rect {
33 | public:
34 | constexpr Rect(int x, int y, int width, int height) :
35 | m_x(x), m_y(y), m_width(width), m_height(height) {}
36 | uint16_t x() const { return m_x; }
37 | uint16_t y() const { return m_y; }
38 | uint16_t width() const { return m_width; }
39 | uint16_t height() const { return m_height; }
40 | constexpr operator eadk_rect_t() const { return *reinterpret_cast(this); }
41 | private:
42 | uint16_t m_x;
43 | uint16_t m_y;
44 | uint16_t m_width;
45 | uint16_t m_height;
46 | };
47 | static_assert(sizeof(EADK::Rect) == sizeof(eadk_rect_t), "EADK::Rect should match eadk_rect_t");
48 |
49 | namespace Screen {
50 | constexpr uint16_t Width = EADK_SCREEN_WIDTH;
51 | constexpr uint16_t Height = EADK_SCREEN_HEIGHT;
52 | constexpr Rect Rect(0, 0, Width, Height);
53 | }
54 |
55 | namespace Display {
56 |
57 | static inline void pushRect(Rect rect, const Color * pixels) {
58 | eadk_display_push_rect(rect, reinterpret_cast(pixels));
59 | }
60 |
61 | static inline void pushRectUniform(Rect rect, Color color) {
62 | eadk_display_push_rect_uniform(rect, color);
63 | }
64 | static inline void drawString(const char * text, Point point, bool largeFont, Color textColor, Color backgroundColor) {
65 | eadk_display_draw_string(text, point, largeFont, textColor, backgroundColor);
66 | }
67 |
68 | }
69 |
70 | namespace Keyboard {
71 |
72 | enum class Key : uint8_t {
73 | Left = 0,
74 | Up = 1,
75 | Down = 2,
76 | Right = 3,
77 | OK = 4,
78 | Back = 5,
79 | Home = 6,
80 | Shift = 12,
81 | Alpha = 13,
82 | XNT = 14,
83 | Var = 15,
84 | Toolbox = 16,
85 | Backspace = 17,
86 | Exp = 18,
87 | Ln = 19,
88 | Log = 20,
89 | Imaginary = 21,
90 | Comma = 22,
91 | Power = 23,
92 | Sine = 24,
93 | Cosine = 25,
94 | Tangent = 26,
95 | Pi = 27,
96 | Sqrt = 28,
97 | Square = 29,
98 | Seven = 30,
99 | Eight = 31,
100 | Nine = 32,
101 | LeftParenthesis = 33,
102 | RightParenthesis = 34,
103 | Four = 36,
104 | Five = 37,
105 | Six = 38,
106 | Multiplication = 39,
107 | Division = 40,
108 | One = 42,
109 | Two = 43,
110 | Three = 44,
111 | Plus = 45,
112 | Minus = 46,
113 | Zero = 48,
114 | Dot = 49,
115 | EE = 50,
116 | Ans = 51,
117 | EXE = 52,
118 | };
119 |
120 | class State {
121 | public:
122 | constexpr State(uint64_t s = 0) : m_bitField(s) { }
123 | inline bool keyDown(Key k) const {
124 | return eadk_keyboard_key_down(*this, (eadk_key_t)k);
125 | //return (m_bitField>>(uint8_t)k) & 1;
126 | }
127 | constexpr operator eadk_keyboard_state_t() const { return *reinterpret_cast(this); }
128 | private:
129 | uint64_t m_bitField;
130 | };
131 | static_assert(sizeof(EADK::Keyboard::State) == sizeof(eadk_keyboard_state_t), "EADK::Keyboard::State should match eadk_keyboard_state_t");
132 |
133 |
134 | static inline State scan() {
135 | return State(eadk_keyboard_scan());
136 | }
137 |
138 | }
139 |
140 | namespace Timing {
141 |
142 | static inline void msleep(uint32_t ms) {
143 | return eadk_timing_msleep(ms);
144 | }
145 |
146 | }
147 |
148 | static inline uint32_t random() {
149 | return eadk_random();
150 | }
151 |
152 | }
153 |
154 | #endif
155 |
--------------------------------------------------------------------------------
/src/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/numworks/epsilon-sample-app-cpp/1bccbd1459c39171956bf809296b4f73dfdd070e/src/icon.png
--------------------------------------------------------------------------------
/src/life.cpp:
--------------------------------------------------------------------------------
1 | #include "life.h"
2 | #include "palette.h"
3 |
4 | void Life::setIndex(int index) {
5 | m_x = EADK::Screen::Width - (index + 1) * Display::CommonHorizontalMargin;
6 | }
7 |
8 | constexpr EADK::Color k_heart[Life::k_height*Life::k_width] = {
9 | EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000),
10 | EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81),
11 | EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81),
12 | EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81),
13 | EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000),
14 | EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000), EADK::Color(0x000000),
15 | EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000),
16 | EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0xFC7F81), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000),
17 | EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0xFC7F81), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000), EADK::Color(0x000000)
18 | };
19 |
20 | void Life::draw() const {
21 | if (m_broken) {
22 | EADK::Display::pushRectUniform(EADK::Rect(m_x - k_width/2, m_y - k_height/2, k_width, k_height), Black);
23 | } else {
24 | EADK::Display::pushRect(EADK::Rect(m_x - k_width/2, m_y - k_height/2, k_width, k_height), k_heart);
25 | }
26 | }
27 |
28 | void Life::breaks() {
29 | m_broken = true;
30 | draw();
31 | }
32 |
--------------------------------------------------------------------------------
/src/life.h:
--------------------------------------------------------------------------------
1 | #ifndef LIFE_H
2 | #define LIFE_H
3 |
4 | #include "display.h"
5 | #include "eadkpp.h"
6 |
7 | class Life {
8 | public:
9 | Life() : m_x(0), m_y(Display::CommonVerticalMargin), m_broken(false) {}
10 | void setIndex(int index);
11 | void draw() const;
12 | void breaks();
13 | static constexpr int k_width = 11;
14 | static constexpr int k_height = 9;
15 | private:
16 | int m_x;
17 | int m_y;
18 | bool m_broken;
19 | };
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "alien.h"
2 | #include "display.h"
3 | #include "eadkpp.h"
4 | #include "palette.h"
5 | #include "spaceship.h"
6 |
7 | extern const char eadk_app_name[] __attribute__((section(".rodata.eadk_app_name"))) = "Voord";
8 | extern const uint32_t eadk_api_level __attribute__((section(".rodata.eadk_api_level"))) = 0;
9 |
10 | void checkForSpaceshipAlienCollisions(Alien aliens[], int numberOfAliens, Spaceship * spaceship) {
11 | for (int i = 0; i < numberOfAliens; i++) {
12 | if (aliens[i].tryToHit(spaceship)) {
13 | EADK::Display::pushRectUniform(EADK::Screen::Rect, Red);
14 | while (1) {}
15 | }
16 | }
17 | }
18 |
19 | int main(int argc, char * argv[]) {
20 | EADK::Display::pushRectUniform(EADK::Screen::Rect, Black);
21 |
22 | constexpr int k_maxNumberOfAliens = 10;
23 | Alien aliens[k_maxNumberOfAliens];
24 |
25 | Spaceship spaceship;
26 |
27 | int rocketTimer = 0;
28 | int alienStepTimer = 0;
29 | int alienMaterializationTimer = 0;
30 | while (1) {
31 | EADK::Keyboard::State keyboardState = EADK::Keyboard::scan();
32 | if (keyboardState.keyDown(EADK::Keyboard::Key::OK)) {
33 | spaceship.createRockets();
34 | }
35 | if (keyboardState.keyDown(EADK::Keyboard::Key::Up)) {
36 | spaceship.move(0, -Spaceship::k_step);
37 | }
38 | if (keyboardState.keyDown(EADK::Keyboard::Key::Down)) {
39 | spaceship.move(0, Spaceship::k_step);
40 | }
41 | if (keyboardState.keyDown(EADK::Keyboard::Key::Left)) {
42 | spaceship.move(-Spaceship::k_step, 0);
43 | }
44 | if (keyboardState.keyDown(EADK::Keyboard::Key::Right)) {
45 | spaceship.move(Spaceship::k_step, 0);
46 | }
47 |
48 | checkForSpaceshipAlienCollisions(aliens, k_maxNumberOfAliens, &spaceship);
49 |
50 | // Rockets move forward and potentially collide
51 | if (rocketTimer == Rocket::k_period) {
52 | rocketTimer = 0;
53 | spaceship.rocketsAction(aliens, k_maxNumberOfAliens);
54 | }
55 |
56 | // Aliens move forward and potentially collide with rockets or spaceship
57 | if (alienStepTimer == Alien::k_stepPeriod) {
58 | alienStepTimer = 0;
59 | for (int i = 0; i < k_maxNumberOfAliens; i++) {
60 | aliens[i].step();
61 | }
62 | checkForSpaceshipAlienCollisions(aliens, k_maxNumberOfAliens, &spaceship);
63 | spaceship.checkForRocketsAliensCollisions(aliens, k_maxNumberOfAliens);
64 | }
65 |
66 | EADK::Timing::msleep(20);
67 |
68 | // New alien
69 | if (alienMaterializationTimer == Alien::k_materializationPeriod) {
70 | alienMaterializationTimer = 0;
71 | for (int i = 0; i < k_maxNumberOfAliens; i++) {
72 | if (aliens[i].isGhost()) {
73 | aliens[i] = Alien(Display::CommonHorizontalMargin + (float)EADK::random()/(float)0xFFFFFFFF * (EADK::Screen::Width - 2*Display::CommonHorizontalMargin));
74 | break;
75 | }
76 | }
77 | }
78 |
79 | // Increment timers
80 | rocketTimer++;
81 | alienStepTimer++;
82 | alienMaterializationTimer++;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/palette.h:
--------------------------------------------------------------------------------
1 | #ifndef PALETTE_H
2 | #define PALETTE_H
3 |
4 | #include "eadkpp.h"
5 |
6 | constexpr static EADK::Color Black(0x000000);
7 | constexpr static EADK::Color Green(0x28F52E);
8 | constexpr static EADK::Color Grey(0xF7F7F7);
9 | constexpr static EADK::Color LightBlue(0XC9DBFD);
10 | constexpr static EADK::Color Orange(0xD55422);
11 | constexpr static EADK::Color Pink(0xCB6E79);
12 | constexpr static EADK::Color Red(0xFF0000);
13 | constexpr static EADK::Color White(0xFFFFFF);
14 | constexpr static EADK::Color Yellow(0xF3B443);
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/src/rocket.cpp:
--------------------------------------------------------------------------------
1 | #include "rocket.h"
2 | #include "alien.h"
3 | #include "palette.h"
4 | #include "stdlib.h"
5 |
6 | void Rocket::setLocation(int x, int y) {
7 | m_x = x;
8 | m_y = y;
9 | draw(LightBlue);
10 | }
11 |
12 | void Rocket::draw(const EADK::Color c) const {
13 | if (off()) {
14 | return;
15 | }
16 | EADK::Display::pushRectUniform(EADK::Rect(m_x, m_y - k_length/2, 1, 8), c);
17 | EADK::Display::pushRectUniform(EADK::Rect(m_x, m_y - k_length/2 + 8 + 1, 1, 2), c);
18 | EADK::Display::pushRectUniform(EADK::Rect(m_x, m_y + k_length/2, 1, 1), c);
19 | }
20 |
21 | void Rocket::forward() {
22 | draw(Black);
23 | if (off()) {
24 | return;
25 | }
26 | m_y -= k_length;
27 | draw(LightBlue);
28 | }
29 |
30 | bool Rocket::tryToKill(Alien * a) {
31 | if (off() || a->isGhost()) {
32 | return false;
33 | }
34 | if (abs(m_x - a->x()) < Alien::k_width/2 &&
35 | abs(m_y - a->y()) < Alien::k_height/2 + k_length/2) {
36 | a->killed();
37 | draw(Black);
38 | switchOff();
39 | return true;
40 | }
41 | return false;
42 | }
43 |
--------------------------------------------------------------------------------
/src/rocket.h:
--------------------------------------------------------------------------------
1 | #ifndef ROCKET_H
2 | #define ROCKET_H
3 |
4 | #include "display.h"
5 | #include "eadkpp.h"
6 |
7 | class Alien;
8 |
9 | class Rocket {
10 | public:
11 | Rocket() : m_x(-1), m_y(-1) {}
12 | int x() const { return m_x; }
13 | int y() const { return m_y; }
14 | void setLocation(int x, int y);
15 | void forward();
16 | bool tryToKill(Alien * a);
17 | bool off() const { return m_y < 0; }
18 | static constexpr int k_period = 5;
19 | private:
20 | static constexpr int k_length = 13;
21 | void draw(const EADK::Color c) const;
22 | void switchOff() { m_y = -1; }
23 | int m_x;
24 | int m_y;
25 | };
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/src/score.cpp:
--------------------------------------------------------------------------------
1 | #include "score.h"
2 | #include "life.h"
3 | #include "palette.h"
4 |
5 | void Score::draw() const {
6 | char buffer[k_maximalScoreNumberOfDigits + 1];
7 | char * c = buffer + k_maximalScoreNumberOfDigits;
8 | *c = 0;
9 | int score = m_value;
10 | while (c > buffer) {
11 | c--;
12 | *c = '0' + score % 10;
13 | score /= 10;
14 | }
15 | EADK::Display::drawString(buffer, EADK::Point(EADK::Screen::Width - 10 * k_maximalScoreNumberOfDigits - Display::CommonHorizontalMargin, 2 * Display::CommonVerticalMargin + Life::k_height), true, White, Black);
16 | }
17 |
18 | void Score::increment() {
19 | m_value++;
20 | if (m_value == k_maximalScore) {
21 | EADK::Display::pushRectUniform(EADK::Rect(0, 0, EADK::Screen::Width, EADK::Screen::Height), Yellow);
22 | EADK::Display::drawString("Well done", EADK::Point((EADK::Screen::Width - 9 * 10) / 2, (EADK::Screen::Height - 18) / 2), true, Black, Yellow);
23 | while(1) {}
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/score.h:
--------------------------------------------------------------------------------
1 | #ifndef SCORE_H
2 | #define SCORE_H
3 |
4 | #include "display.h"
5 | #include "eadkpp.h"
6 |
7 | class Score {
8 | public:
9 | Score() : m_value(0) {}
10 | void draw() const;
11 | void increment();
12 | private:
13 | constexpr static int k_maximalScore = 9999;
14 | constexpr static int k_maximalScoreNumberOfDigits = 4; // log(k_maximalScore, 10)
15 | int m_value;
16 | };
17 |
18 | #endif
19 |
20 |
--------------------------------------------------------------------------------
/src/spaceship.cpp:
--------------------------------------------------------------------------------
1 | #include "spaceship.h"
2 | #include "alien.h"
3 | #include "palette.h"
4 |
5 | Spaceship::Spaceship() :
6 | m_x(EADK::Screen::Width/2),
7 | m_y(EADK::Screen::Height - Display::CommonVerticalMargin),
8 | m_numberOfLives(k_maxNumberOfLives)
9 | {
10 |
11 | draw(Yellow);
12 | for (int i = 0; i < k_maxNumberOfLives; i++) {
13 | m_lives[i].setIndex(i);
14 | m_lives[i].draw();
15 | }
16 | }
17 |
18 | void Spaceship::draw(const EADK::Color color) const {
19 | int xMin = m_x - k_width/2;
20 | int xMax = xMin + k_width;
21 | int yMin = m_y - k_height/2;
22 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 11, yMin + 10, 13, 11), color);
23 | // Wings
24 | EADK::Display::pushRectUniform(EADK::Rect(xMin, yMin + 14, k_width, 2), color);
25 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 3, yMin + 17, k_width - 6, 2), color);
26 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 2, yMin + 8, 1, 6), color);
27 | EADK::Display::pushRectUniform(EADK::Rect(xMax - 3, yMin + 8, 1, 6), color);
28 | // Nose
29 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 15, yMin + 6, 5, 4), color);
30 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 16, yMin + 4, 3, 2), color);
31 | EADK::Display::pushRectUniform(EADK::Rect(xMin + 17, yMin, 1, 4), color);
32 | }
33 |
34 | void Spaceship::move(int deltaX, int deltaY) {
35 | draw(Black);
36 | m_x += deltaX;
37 | m_y += deltaY;
38 | m_x = m_x <= k_xLowerBound ? k_xLowerBound : m_x;
39 | m_y = m_y <= k_yLowerBound ? k_yLowerBound : m_y;
40 | m_x = m_x >= k_xUpperBound ? k_xUpperBound : m_x;
41 | m_y = m_y >= k_yUpperBound ? k_yUpperBound : m_y;
42 | draw(Yellow);
43 | }
44 |
45 | bool Spaceship::hit() {
46 | m_lives[m_numberOfLives - 1].breaks();
47 | m_numberOfLives--;
48 | for (int i = 0; i < 5; i++) {
49 | draw(Red);
50 | EADK::Timing::msleep(10);
51 | draw(Yellow);
52 | EADK::Timing::msleep(10);
53 | }
54 | return m_numberOfLives == 0;
55 | }
56 |
57 | void Spaceship::createRockets() {
58 | int deltaX[] = {-15, 0, 14};
59 | for (int dx : deltaX) {
60 | int x = m_x + dx;
61 | int y = m_y - k_height;
62 | for (int i = 0; i < k_maxNumberOfRockets; i++) {
63 | if (m_rockets[i].x() == x && m_rockets[i].y() == y) {
64 | // A rocket has already been launched at this location
65 | continue;
66 | }
67 | }
68 | // Launch the first available rocket
69 | for (int i = 0; i < k_maxNumberOfRockets; i++) {
70 | if (m_rockets[i].off()) {
71 | m_rockets[i].setLocation(x, y);
72 | break;
73 | }
74 | }
75 | }
76 | }
77 |
78 | void Spaceship::rocketsAction(Alien aliens[], int numberOfAliens) {
79 | for (int i = 0; i < k_maxNumberOfRockets; i++) {
80 | m_rockets[i].forward();
81 | }
82 | checkForRocketsAliensCollisions(aliens, numberOfAliens);
83 | redrawLives();
84 |
85 | }
86 |
87 | void Spaceship::redrawLives() {
88 | for (int i = 0; i < k_maxNumberOfLives; i++) {
89 | m_lives[i].draw();
90 | }
91 | }
92 |
93 | void Spaceship::checkForRocketsAliensCollisions(Alien aliens[], int numberOfAliens) {
94 | for (int i = 0; i < k_maxNumberOfRockets; i++) {
95 | for (int j = 0; j < numberOfAliens; j++) {
96 | if (m_rockets[i].tryToKill(&aliens[j])) {
97 | m_score.increment();
98 | }
99 | }
100 | }
101 | m_score.draw();
102 | }
103 |
--------------------------------------------------------------------------------
/src/spaceship.h:
--------------------------------------------------------------------------------
1 | #ifndef SPACESHIP_H
2 | #define SPACESHIP_H
3 |
4 | #include "display.h"
5 | #include "eadkpp.h"
6 | #include "life.h"
7 | #include "rocket.h"
8 | #include "score.h"
9 |
10 | class Spaceship {
11 | public:
12 | Spaceship();
13 | int x() const { return m_x; }
14 | int y() const { return m_y; }
15 | void move(int deltaX, int deltaY);
16 | bool hit();
17 | void createRockets();
18 | void rocketsAction(Alien aliens[], int numberOfAliens);
19 | void checkForRocketsAliensCollisions(Alien aliens[], int numberOfAliens);
20 | static constexpr int k_step = 10;
21 | static constexpr int k_width = 35;
22 | static constexpr int k_height = 20;
23 | private:
24 | static constexpr int k_maxNumberOfRockets = 3*50;
25 | static constexpr int k_maxNumberOfLives = 3;
26 | static constexpr int k_xLowerBound = Display::CommonHorizontalMargin;
27 | static constexpr int k_xUpperBound = EADK::Screen::Width - Display::CommonHorizontalMargin;
28 | static constexpr int k_yLowerBound = 3*Display::CommonVerticalMargin;
29 | static constexpr int k_yUpperBound = EADK::Screen::Height - Display::CommonVerticalMargin;
30 | void draw(const EADK::Color c) const;
31 | void redrawLives();
32 | int m_x;
33 | int m_y;
34 | Rocket m_rockets[k_maxNumberOfRockets];
35 | int m_numberOfLives;
36 | Life m_lives[k_maxNumberOfLives];
37 | Score m_score;
38 | };
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------