├── .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 | [![Build](https://github.com/numworks/epsilon-sample-app-cpp/actions/workflows/build.yml/badge.svg)](https://github.com/numworks/epsilon-sample-app-cpp/actions/workflows/build.yml) 4 | 5 | Sample C++ app for the NumWorks graphing calculator 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 | --------------------------------------------------------------------------------