From 4a94bceb8ffb438230975f84da7e4ab66e6523a5 Mon Sep 17 00:00:00 2001 From: SlyVTT Date: Sat, 27 May 2023 21:48:20 +0200 Subject: [PATCH] Very first commit --- .gitignore | 15 ++ CMakeLists.txt | 39 ++++ README.md | 10 + TODO.txt | 8 + assets-cg/example.png | Bin 0 -> 3816 bytes assets-cg/font.png | Bin 0 -> 19394 bytes assets-cg/fxconv-metadata.txt | 4 + assets-cg/icon-cg.xcf | Bin 0 -> 55168 bytes assets-cg/icon-sel.png | Bin 0 -> 3536 bytes assets-cg/icon-uns.png | Bin 0 -> 1700 bytes build | 1 + buildpush | 3 + capture | 1 + clean | 4 + send | 1 + src/extrakeyboard.h | 258 +++++++++++++++++++++++ src/main.cpp | 387 ++++++++++++++++++++++++++++++++++ src/parameters.h | 9 + src/pinball_entities.h | 120 +++++++++++ src/utilities.h | 48 +++++ src/vector2D.h | 210 ++++++++++++++++++ 21 files changed, 1118 insertions(+) create mode 100755 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 TODO.txt create mode 100644 assets-cg/example.png create mode 100644 assets-cg/font.png create mode 100644 assets-cg/fxconv-metadata.txt create mode 100644 assets-cg/icon-cg.xcf create mode 100644 assets-cg/icon-sel.png create mode 100644 assets-cg/icon-uns.png create mode 100755 build create mode 100755 buildpush create mode 100755 capture create mode 100755 clean create mode 100755 send create mode 100644 src/extrakeyboard.h create mode 100644 src/main.cpp create mode 100644 src/parameters.h create mode 100644 src/pinball_entities.h create mode 100644 src/utilities.h create mode 100644 src/vector2D.h diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..87361b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Build files +/build-fx +/build-cg +/build-cg-push +/*.g1a +/*.g3a +/compile_flags.txt + +# Python bytecode +__pycache__/ + +# Common IDE files +*.sublime-project +*.sublime-workspace +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..09d00d7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +# Configure with [fxsdk build-fx] or [fxsdk build-cg], which provide the +# toolchain file and module path of the fxSDK + +cmake_minimum_required(VERSION 3.15) +project(Pinball VERSION 1.0 LANGUAGES CXX C ASM) + +include(GenerateG3A) +include(Fxconv) + +find_package(Azur 0.1 REQUIRED) +find_package(Gint 2.9 REQUIRED) +find_package(LibProf 2.4 REQUIRED) + + +set(SOURCES + src/main.cpp + # ... +) +# Shared assets, fx-9860G-only assets and fx-CG-50-only assets +set(ASSETS + # ... +) + +set(ASSETS_cg + assets-cg/font.png + # ... +) + +fxconv_declare_assets(${ASSETS} ${ASSETS_cg} WITH_METADATA) + +add_executable(myaddin ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}}) +target_compile_options(myaddin PRIVATE -Wall -Wextra -O3 -std=c++20) +target_link_options(myaddin PRIVATE -Wl,-Map=Build_Addin.map -Wl,--print-memory-usage) +target_link_libraries(myaddin Azur::Azur -lnum LibProf::LibProf Gint::Gint -lstdc++) + +if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50) + generate_g3a(TARGET myaddin OUTPUT "Pinball.g3a" + NAME "Pinball" ICONS assets-cg/icon-uns.png assets-cg/icon-sel.png) +endif() diff --git a/README.md b/README.md new file mode 100644 index 0000000..e58f32b --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# This is my contribution for the Casio Programming Contest #30 (CPC#30) + +Program made using fxSDK/gint/Azur from Lephenixnoir. + +The theme of the contest is "The depths". + +7 days to prepare a game around that general theme. + +Started Saturday May 27th 2023 6pm and to be done by Saturday June 3rd 2023 6pm. + diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..95cdae9 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,8 @@ +Add non circular obstacles + +Add several boards + +Add background pictures + +Add targets and enable bonus + diff --git a/assets-cg/example.png b/assets-cg/example.png new file mode 100644 index 0000000000000000000000000000000000000000..8826800383eeae92dade9545feaeb68faf0720f2 GIT binary patch literal 3816 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 eRZ5JQiUR=bBzf^!G-(0=0000Z%G&$ju0 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 literal 0 HcmV?d00001 diff --git a/assets-cg/fxconv-metadata.txt b/assets-cg/fxconv-metadata.txt new file mode 100644 index 0000000..5e3f5a8 --- /dev/null +++ b/assets-cg/fxconv-metadata.txt @@ -0,0 +1,4 @@ +*.png: + type: bopti-image + name_regex: (.*)\.png img_\1 + profile: p8_rgb565a diff --git a/assets-cg/icon-cg.xcf b/assets-cg/icon-cg.xcf new file mode 100644 index 0000000000000000000000000000000000000000..5b854f07eac00072a519754783db3331323791d7 GIT binary patch literal 55168 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 Ol}iESO+rylvHuUZ)lew_ literal 0 HcmV?d00001 diff --git a/assets-cg/icon-sel.png b/assets-cg/icon-sel.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce3029cc00aea4c3cfad161c6f2bd7b60f68f1f GIT binary patch literal 3536 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 KMNUMnLSTaFG`Vj8 literal 0 HcmV?d00001 diff --git a/assets-cg/icon-uns.png b/assets-cg/icon-uns.png new file mode 100644 index 0000000000000000000000000000000000000000..039c096cc2ab31206abf45b5867ee55594d732ae GIT binary patch literal 1700 zcmV;V23z@wP)F&-TE@ZP=eefJ$8 zG^>ga5(r-rB{fNk2(U0=+M;`!NPYc7ayIb-#~Yw{-%9pMfi* zT+WRi9s!MRNU z000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2j>F}6bvucs67h+00fvxL_t(|+U=XY zZxlxu#vkA9m+!Ew80iQQ3XBwmNLEn3&PGV2GELwQKok@%sHt;TgocU^iGl)&DoTfF zOoMHq;DhibB!nVNq<}3M47O$C-JO}^XxP))U}w zqtO6WW|boZ1e}0V5%B+Q8F2?Z&au3@hJjoTORK9&Ka2|j(=b}bTnOb~h6@hUFk8l4 zaOL$r7qCsEW$bhH>kGzVSZ2#uF!k#n*s~isG_bE!LeX&|&l!kHDI2bfCodM^GKMwR zg~z$pUG$g_$FkuuKa~nR--lz{@ceY^w|s_zZDGr2sn#>sg*7+`=DJ$zijIR_!^7B7 za4>Q3AifwHQnQqW=|_@0BhTh8n;Q=2TQ3h8n;Q=2TQ3h8n;Q=2TQ3 zh8n;Q=5$pq>UF&HJpA)=Q62c|#s-*%_sU9_q3bGLhOVo08M?00W$3y}_c+I^n0>FS zDJ86G*~4{|9_8UWN{{kz9i>NkxUSMOi-l=CJZZyJQFhXXsiN$p4O2(i+X6g|hs!G0 z^>A6`x*jg8yb}-46nK({tExNB!&TKC=i#dAj`Q%o1fJyKs_O0}hbMWss=7PL;h6!a z9G)?7%He(J;h$3u?+Xv798NiWTXMM3%N*`8zX|^IFGm3YS7v6LpSR&2e5xUas*pG!;`yrn#V4m`&|isH*$C!@UB=&lCs0&Bzw3rGn;tMrRf_ub8Iql zURHJh8$io=s#1-FfBug@W0l?Q2cS$99<~y>4ow_xo)cIL5B+;=tX#(9S}oR20N`=0 zhKtn-E?m1F`J@^6x(xr@pMK6v;hA~3ip9teJaSrTXE9qo!@|P{EzrY%6-kiWdARNk z=&7Z&%i(b;L{2LK>?B(Wd$oM5U|Sd~moZhTHZ7waRr)$ngpDHQxR|iPDQ{8I28dW-~^nC ufD>>k0#3lG2si;J;8X;hfKw6hey$IZ=p>OU@Ebt@0000 +#include +#include + +enum { + MYKEY_F1 = 0, + MYKEY_F2, + MYKEY_F3, + MYKEY_F4, + MYKEY_F5, + MYKEY_F6, + + MYKEY_SHIFT, + MYKEY_OPTN, + MYKEY_VARS, + MYKEY_MENU, + MYKEY_LEFT, + MYKEY_UP, + + MYKEY_ALPHA, + MYKEY_SQUARE, + MYKEY_POWER, + MYKEY_EXIT, + MYKEY_DOWN, + MYKEY_RIGHT, + + MYKEY_XOT, + MYKEY_LOG, + MYKEY_LN, + MYKEY_SIN, + MYKEY_COS, + MYKEY_TAN, + + MYKEY_FRAC, + MYKEY_FD, + MYKEY_LEFTP, + MYKEY_RIGHTP, + MYKEY_COMMA, + MYKEY_ARROW, + + MYKEY_7, + MYKEY_8, + MYKEY_9, + MYKEY_DEL, + + MYKEY_4, + MYKEY_5, + MYKEY_6, + MYKEY_MUL, + MYKEY_DIV, + + MYKEY_1, + MYKEY_2, + MYKEY_3, + MYKEY_ADD, + MYKEY_SUB, + + MYKEY_0, + MYKEY_DOT, + MYKEY_EXP, + MYKEY_NEG, + MYKEY_EXE, + + MYKEY_ACON, + + MYKEY_LASTENUM, +}; + +typedef struct { + bool pressed; + uint32_t since = 0; +} keyinfo; + +keyinfo MyKeyMapper[MYKEY_LASTENUM + 1]; + +class KeyboardExtra { +public: + KeyboardExtra() { + uint32_t timer = rtc_ticks(); + now = timer; + for (int i = 0; i <= MYKEY_LASTENUM; i++) + MyKeyMapper[i] = {.pressed = false, .since = timer}; + } + + ~KeyboardExtra() {} + + void Update(float dt) { + uint32_t timer = rtc_ticks(); + + now = timer; + + key_event_t ev; + + int keycode = -1; + + while ((ev = pollevent()).type != KEYEV_NONE) { + if (ev.key == KEY_F1) + keycode = MYKEY_F1; + else if (ev.key == KEY_F2) + keycode = MYKEY_F2; + else if (ev.key == KEY_F3) + keycode = MYKEY_F3; + else if (ev.key == KEY_F4) + keycode = MYKEY_F4; + else if (ev.key == KEY_F5) + keycode = MYKEY_F5; + else if (ev.key == KEY_F6) + keycode = MYKEY_F6; + + else if (ev.key == KEY_SHIFT) + keycode = MYKEY_SHIFT; + else if (ev.key == KEY_OPTN) + keycode = MYKEY_OPTN; + else if (ev.key == KEY_VARS) + keycode = MYKEY_VARS; + else if (ev.key == KEY_MENU) + keycode = MYKEY_MENU; + else if (ev.key == KEY_LEFT) + keycode = MYKEY_LEFT; + else if (ev.key == KEY_UP) + keycode = MYKEY_UP; + + else if (ev.key == KEY_ALPHA) + keycode = MYKEY_ALPHA; + else if (ev.key == KEY_SQUARE) + keycode = MYKEY_SQUARE; + else if (ev.key == KEY_POWER) + keycode = MYKEY_POWER; + else if (ev.key == KEY_EXIT) + keycode = MYKEY_EXIT; + else if (ev.key == KEY_DOWN) + keycode = MYKEY_DOWN; + else if (ev.key == KEY_RIGHT) + keycode = MYKEY_RIGHT; + + else if (ev.key == KEY_XOT) + keycode = MYKEY_XOT; + else if (ev.key == KEY_LOG) + keycode = MYKEY_LOG; + else if (ev.key == KEY_LN) + keycode = MYKEY_LN; + else if (ev.key == KEY_SIN) + keycode = MYKEY_SIN; + else if (ev.key == KEY_COS) + keycode = MYKEY_COS; + else if (ev.key == KEY_TAN) + keycode = MYKEY_TAN; + + else if (ev.key == KEY_FRAC) + keycode = MYKEY_FRAC; + else if (ev.key == KEY_FD) + keycode = MYKEY_FD; + else if (ev.key == KEY_LEFTP) + keycode = MYKEY_LEFTP; + else if (ev.key == KEY_RIGHTP) + keycode = MYKEY_RIGHTP; + else if (ev.key == KEY_COMMA) + keycode = MYKEY_COMMA; + else if (ev.key == KEY_ARROW) + keycode = MYKEY_ARROW; + + else if (ev.key == KEY_7) + keycode = MYKEY_7; + else if (ev.key == KEY_8) + keycode = MYKEY_8; + else if (ev.key == KEY_9) + keycode = MYKEY_9; + else if (ev.key == KEY_DEL) + keycode = MYKEY_DEL; + + else if (ev.key == KEY_4) + keycode = MYKEY_4; + else if (ev.key == KEY_5) + keycode = MYKEY_5; + else if (ev.key == KEY_6) + keycode = MYKEY_6; + else if (ev.key == KEY_MUL) + keycode = MYKEY_MUL; + else if (ev.key == KEY_DIV) + keycode = MYKEY_DIV; + + else if (ev.key == KEY_1) + keycode = MYKEY_1; + else if (ev.key == KEY_2) + keycode = MYKEY_2; + else if (ev.key == KEY_3) + keycode = MYKEY_3; + else if (ev.key == KEY_ADD) + keycode = MYKEY_ADD; + else if (ev.key == KEY_SUB) + keycode = MYKEY_SUB; + + else if (ev.key == KEY_0) + keycode = MYKEY_0; + else if (ev.key == KEY_DOT) + keycode = MYKEY_DOT; + else if (ev.key == KEY_EXP) + keycode = MYKEY_EXP; + else if (ev.key == KEY_NEG) + keycode = MYKEY_NEG; + else if (ev.key == KEY_EXE) + keycode = MYKEY_EXE; + + else if (ev.key == KEY_ACON) + keycode = MYKEY_ACON; + + if (keycode != -1) { + if (ev.type == KEYEV_DOWN) + MyKeyMapper[keycode] = {.pressed = true, .since = timer}; + else if (ev.type == KEYEV_UP) + MyKeyMapper[keycode] = {.pressed = false, .since = timer}; + else if (ev.type == KEYEV_HOLD) { + } + } else { + // do nothing, just unstack the event from the events queue + }; + } + } + + bool IsKeyPressedEvent(int key) { + return (MyKeyMapper[key].pressed && MyKeyMapper[key].since == now); + } + + bool IsKeyReleasedEvent(int key) { + return (!MyKeyMapper[key].pressed && MyKeyMapper[key].since == now); + } + + bool IsKeyPressed(int key) { return MyKeyMapper[key].pressed; } + + bool IsKeyReleased(int key) { return (!MyKeyMapper[key].pressed); } + + uint32_t IsKeyHoldPressed(int key) { + if (MyKeyMapper[key].pressed && MyKeyMapper[key].since < now) + return (uint32_t)(now - MyKeyMapper[key].since); + else + return 0; + } + + uint32_t IsKeyHoldReleased(int key) { + if (!MyKeyMapper[key].pressed && MyKeyMapper[key].since < now) + return (uint32_t)(now - MyKeyMapper[key].since); + else + return 0; + } + + uint32_t GetLastTickKeyEvent(int key) { + return (uint32_t)MyKeyMapper[key].since; + } + +private: + uint32_t now; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4602b32 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,387 @@ +#include "parameters.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "extrakeyboard.h" +#include "pinball_entities.h" +#include "stdint-gcc.h" +#include "utilities.h" +#include "vector2D.h" + +#include + +#include + +bool screenshot = false; +bool record = false; +bool textoutput = false; +bool exitToOS = false; + +uint8_t texttodraw = 1; + +#define SCALE_PIXEL 1 +#define X_RESOL (DWIDTH / SCALE_PIXEL) +#define Y_RESOL (DHEIGHT / SCALE_PIXEL) + +float elapsedTime = 0.0f; +uint32_t time_update = 0, time_render = 0; +prof_t perf_update, perf_render; + +static kmalloc_arena_t extended_ram = {0}; +static kmalloc_arena_t *_uram; +kmalloc_gint_stats_t *_uram_stats; +kmalloc_gint_stats_t *extram_stats; + +KeyboardExtra MyKeyboard; + +Scene MyPinball; + +libnum::num32 flipperHeight; +libnum::num32 cScale; +libnum::num32 simWidth; +libnum::num32 simHeight; + +/* return the scaled x component of a vector */ +uint16_t CX(Vector2D pos) { return (int) (pos.x * cScale); } + +/* return the scaled y component of a vector */ +uint16_t CY(Vector2D pos) { return (int) (libnum::num32(azrp_height) - pos.y * cScale); } + +/* create the pinball board */ +void SetupScene(void) { + libnum::num32 offset = libnum::num32(0.02); + + flipperHeight = libnum::num32(1.7); + cScale = libnum::num32(azrp_height) / flipperHeight; + simWidth = libnum::num32(azrp_width) / cScale; + simHeight = libnum::num32(azrp_height) / cScale; + + MyPinball.score = 0; + + MyPinball.borders.clear(); + MyPinball.borders.push_back( Vector2D( libnum::num32(0.74) , libnum::num32(0.25))); + MyPinball.borders.push_back( Vector2D( libnum::num32(1.0)-offset, libnum::num32(0.4))); + MyPinball.borders.push_back( Vector2D( libnum::num32(1.0)-offset, flipperHeight - offset)); + MyPinball.borders.push_back( Vector2D( offset , flipperHeight - offset)); + MyPinball.borders.push_back( Vector2D( offset , libnum::num32(0.4))); + MyPinball.borders.push_back( Vector2D( libnum::num32(0.26) , libnum::num32(0.25))); + MyPinball.borders.push_back( Vector2D( libnum::num32(0.26) , libnum::num32(offset))); + MyPinball.borders.push_back( Vector2D( libnum::num32(0.74) , libnum::num32(offset))); + + + MyPinball.balls.clear(); + libnum::num32 ball_radius = libnum::num32(0.03); + libnum::num32 ball_mass = libnum::num32( PI ) * ball_radius * ball_radius; + Vector2D ball_pos1( libnum::num32(0.92), libnum::num32(0.5) ); + Vector2D ball_vel1( libnum::num32(0.2) , libnum::num32(3.5) ); + + MyPinball.balls.push_back( Ball( ball_radius, ball_mass, ball_pos1, ball_vel1, 0.2 ) ); + Vector2D ball_pos2( libnum::num32(0.08), libnum::num32(0.5) ); + Vector2D ball_vel2( libnum::num32(0.2) , libnum::num32(3.5) ); + MyPinball.balls.push_back( Ball( ball_radius, ball_mass, ball_pos2, ball_vel2, 0.2 ) ); + + + MyPinball.obstacles.clear(); + MyPinball.obstacles.push_back( Obstacle( 0.1 , Vector2D( libnum::num32(0.25), libnum::num32(0.6) ), 2.0 ) ); + MyPinball.obstacles.push_back( Obstacle( 0.1 , Vector2D( libnum::num32(0.75), libnum::num32(0.5) ), 2.0 ) ); + MyPinball.obstacles.push_back( Obstacle( 0.12, Vector2D( libnum::num32(0.7 ), libnum::num32(1.0) ), 2.0 ) ); + MyPinball.obstacles.push_back( Obstacle( 0.1 , Vector2D( libnum::num32(0.2 ), libnum::num32(1.2) ), 2.0 ) ); + + + libnum::num32 flip_radius = libnum::num32( 0.03); + libnum::num32 flip_length = libnum::num32( 0.2); + libnum::num32 flip_maxRotation = libnum::num32(1.0); + libnum::num32 flip_restAngle = libnum::num32(0.5); + libnum::num32 flip_angularVelocity = libnum::num32(10.0); + libnum::num32 flip_restitution = libnum::num32(0.0); + Vector2D flip_pos1( libnum::num32(0.26), libnum::num32(0.22) ); + Vector2D flip_pos2( libnum::num32(0.74), libnum::num32(0.22) ); + MyPinball.flippers.push_back( Flipper( flip_radius, flip_pos1, flip_length, -flip_restAngle, flip_maxRotation, flip_angularVelocity, flip_restitution) ); + MyPinball.flippers.push_back( Flipper( flip_radius, flip_pos2, flip_length, libnum::num32(PI) + flip_restAngle, -flip_maxRotation, flip_angularVelocity, flip_restitution) ); +} + +void HandleBallBallCollision( Ball ball1, Ball ball2 ) +{ + libnum::num32 restitution = MIN( ball1.restitution, ball2.restitution ); + Vector2D dir = ball2.pos - ball1.pos; + libnum::num32 d = dir.Length(); + if ( d==libnum::num32(0) || d > (ball1.radius + ball2.radius) ) return; + + dir.Normalise(); + + libnum::num32 corr = (ball1.radius + ball2.radius-d) / libnum::num32(2); + ball1.pos.Add(dir, -corr); + ball2.pos.Add(dir, corr); + + libnum::num32 v1 = ball1.vel.Dot(dir); + libnum::num32 v2 = ball2.vel.Dot(dir); + + libnum::num32 m1 = ball1.mass; + libnum::num32 m2 = ball2.mass; + + libnum::num32 newV1 = (m1*v1 + m2*v2 - m2*(v1-v2)*restitution / (m1+m2) ); + libnum::num32 newV2 = (m1*v1 + m2*v2 - m1*(v2-v1)*restitution / (m1+m2) ); + + ball1.vel.Add(dir, newV1-v1 ); + ball2.vel.Add(dir, newV2-v2 ); +} + +void HandleBallOsbtacleCollision( Ball ball, Obstacle obstacle ) +{ + Vector2D dir = ball.pos - obstacle.pos; + libnum::num32 d = dir.Length(); + if ( d==libnum::num32(0) || d > (ball.radius + obstacle.radius) ) return; + + dir.Normalise(); + + libnum::num32 corr = ball.radius + obstacle.radius-d; + ball.pos.Add(dir, corr); + + libnum::num32 v = ball.vel.Dot(dir); + ball.vel.Add(dir, obstacle.pushVel-v ); + + MyPinball.score++; +} + + +static void hook_prefrag(int id, void *fragment, int size) { + if (!screenshot && !record) + return; + + /* Screenshot takes precedence */ + char const *type = screenshot ? "image" : "video"; + + int pipe = usb_ff_bulk_output(); + + if (id == 0) { + usb_fxlink_header_t h; + usb_fxlink_image_t sh; + int size = azrp_width * azrp_height * 2; + + usb_fxlink_fill_header(&h, "fxlink", type, size + sizeof sh); + sh.width = htole32(azrp_width); + sh.height = htole32(azrp_height); + sh.pixel_format = htole32(USB_FXLINK_IMAGE_RGB565); + + usb_write_sync(pipe, &h, sizeof h, false); + usb_write_sync(pipe, &sh, sizeof sh, false); + } + + usb_write_sync(pipe, fragment, size, false); + + if (id == azrp_frag_count - 1) { + usb_commit_sync(pipe); + screenshot = false; + } +} + +static void update(float dt) {} + +static void render(void) { + + azrp_clear(C_BLACK); + + int mod = MyPinball.borders.size(); + for( int i=0; ifree_memory + extram_stats->free_memory); +} + +static void get_inputs(float dt) { + if (MyKeyboard.IsKeyPressed(MYKEY_SHIFT) && + MyKeyboard.IsKeyHoldPressed(MYKEY_EXIT)) { + exitToOS = true; + }; + +#if (DEBUG_MODE) + if (MyKeyboard.IsKeyPressed(MYKEY_OPTN) && + MyKeyboard.IsKeyPressedEvent(MYKEY_7) && usb_is_open()) { + screenshot = true; + }; + if (MyKeyboard.IsKeyPressed(MYKEY_OPTN) && + MyKeyboard.IsKeyPressedEvent(MYKEY_8) && usb_is_open()) { + record = true; + }; + if (MyKeyboard.IsKeyPressed(MYKEY_OPTN) && + MyKeyboard.IsKeyPressedEvent(MYKEY_9) && usb_is_open()) { + record = false; + }; + + if (MyKeyboard.IsKeyPressedEvent(MYKEY_F1)) { + } + if (MyKeyboard.IsKeyPressedEvent(MYKEY_F2)) { + } + if (MyKeyboard.IsKeyPressedEvent(MYKEY_F3)) { + } + if (MyKeyboard.IsKeyPressedEvent(MYKEY_F4)) { + } + if (MyKeyboard.IsKeyPressedEvent(MYKEY_F5)) { + } + if (MyKeyboard.IsKeyPressedEvent(MYKEY_F6)) { + } +#endif + + /* we can have either LEFT or RIGHT or NONE OF THEM pressed for the direction + */ +} + +bool AddMoreRAM(void) { + /* allow more RAM */ + char const *osv = (char *)0x80020020; + + if ((!strncmp(osv, "03.", 3) && osv[3] <= '8') && + gint[HWCALC] == HWCALC_FXCG50) // CG-50 + { + extended_ram.name = "extram"; + extended_ram.is_default = true; + extended_ram.start = (void *)0x8c200000; + extended_ram.end = (void *)0x8c4e0000; + + kmalloc_init_arena(&extended_ram, true); + kmalloc_add_arena(&extended_ram); + return true; + } else if (gint[HWCALC] == HWCALC_PRIZM) // CG-10/20 + { + + extended_ram.name = "extram"; + extended_ram.is_default = true; + + uint16_t *vram1, *vram2; + dgetvram(&vram1, &vram2); + dsetvram(vram1, vram1); + + extended_ram.start = vram2; + extended_ram.end = (char *)vram2 + 396 * 224 * 2; + + kmalloc_init_arena(&extended_ram, true); + kmalloc_add_arena(&extended_ram); + return true; + + } else if (gint[HWCALC] == HWCALC_FXCG_MANAGER) // CG-50 EMULATOR + { + + extended_ram.name = "extram"; + extended_ram.is_default = true; + extended_ram.start = (void *)0x88200000; + extended_ram.end = (void *)0x884e0000; + + kmalloc_init_arena(&extended_ram, true); + kmalloc_add_arena(&extended_ram); + return true; + } else { + return false; + } +} + +void FreeMoreRAM(void) { + memset(extended_ram.start, 0, + (char *)extended_ram.end - (char *)extended_ram.start); +} + +int main(void) { + exitToOS = false; + + _uram = kmalloc_get_arena("_uram"); + + bool canWeAllocate3Mb = AddMoreRAM(); + + __printf_enable_fp(); + __printf_enable_fixed(); + + azrp_config_scale(SCALE_PIXEL); + + azrp_shader_clear_configure(); + azrp_shader_image_rgb16_configure(); + azrp_shader_image_p8_configure(); + azrp_shader_image_p4_configure(); + azrp_shader_line_configure(); + azrp_shader_circle_configure(); + + azrp_hook_set_prefrag(hook_prefrag); + + usb_interface_t const *interfaces[] = {&usb_ff_bulk, NULL}; + usb_open(interfaces, GINT_CALL_NULL); + + SetupScene(); + + prof_init(); + + do { + perf_update = prof_make(); + prof_enter(perf_update); + + { + // all the stuff to be update should be put here + MyKeyboard.Update(elapsedTime); + get_inputs(elapsedTime); + + update(elapsedTime); + + // update the RAM consumption status + _uram_stats = kmalloc_get_gint_stats(_uram); + extram_stats = kmalloc_get_gint_stats(&extended_ram); + } + + prof_leave(perf_update); + time_update = prof_time(perf_update); + + perf_render = prof_make(); + prof_enter(perf_render); + + { + // all the stuff to be rendered should be put here + render(); + + azrp_update(); + } + + prof_leave(perf_render); + time_render = prof_time(perf_render); + + elapsedTime = ((float)(time_update + time_render)) / 1000.0f; + + } while (exitToOS == false); + + prof_quit(); + usb_close(); + + if (canWeAllocate3Mb) + FreeMoreRAM(); + + return 1; +} diff --git a/src/parameters.h b/src/parameters.h new file mode 100644 index 0000000..d7588c2 --- /dev/null +++ b/src/parameters.h @@ -0,0 +1,9 @@ +#ifndef PARAMETERS_H +#define PARAMETERS_H + + +#define DEBUG_MODE 1 +#define BIAS 1 +#define NOBIAS (1-BIAS) + +#endif \ No newline at end of file diff --git a/src/pinball_entities.h b/src/pinball_entities.h new file mode 100644 index 0000000..1e0446a --- /dev/null +++ b/src/pinball_entities.h @@ -0,0 +1,120 @@ +#ifndef PINBALL_ENTITIES_H +#define PINBALL_ENTITIES_H + +#include "stdint-gcc.h" +#include "utilities.h" +#include "vector2D.h" +#include + +#include + +class Ball { +public: + Ball(libnum::num32 radius, libnum::num32 mass, Vector2D pos, Vector2D vel, + libnum::num32 restitution) { + this->radius = radius; + this->mass = mass; + this->pos = pos.Clone(); + this->vel = vel.Clone(); + this->restitution = restitution; + } + + ~Ball() {} + + void Simulate(libnum::num32 dt, Vector2D gravity) { + this->vel.Add(gravity, dt); + this->pos.Add(this->vel, dt); + } + + libnum::num32 radius, mass, restitution; + Vector2D pos, vel; +}; + + +class Obstacle { +public: + Obstacle(libnum::num32 radius, Vector2D pos, libnum::num32 pushVel) { + this->radius = radius; + this->pos = pos.Clone(); + this->pushVel = pushVel; + } + + Obstacle() {} + + libnum::num32 radius, pushVel; + Vector2D pos; +}; + + +class Flipper { +public: + Flipper(libnum::num32 radius, Vector2D pos, libnum::num32 length, + libnum::num32 restAngle, libnum::num32 maxRotation, + libnum::num32 angularVelocity, libnum::num32 restitution) { + this->radius = radius; + this->pos = pos.Clone(); + this->length = length; + this->restAngle = restAngle; + this->maxRotation = ABS(maxRotation); + this->sign = SIGN(maxRotation); + this->angularVelocity = angularVelocity; + this->rotation = libnum::num32(0); + this->currentAngularVelocity = libnum::num32(0); + this->touchIdentifier = libnum::num32(-1); + } + + void Simulate(libnum::num32 dt) { + libnum::num32 prevRotation = this->rotation; + bool pressed = this->touchIdentifier >= 0; + if (pressed) + this->rotation = MIN(this->rotation + dt * this->angularVelocity, this->maxRotation); + else + this->rotation = MAX(this->rotation - dt * this->angularVelocity, 0.0); + this->currentAngularVelocity = this->sign * (this->rotation - prevRotation) / dt; + } + + bool Select(Vector2D pos) + { + Vector2D D;; + D.SubtractVectors(this->pos, pos); + bool test = (D.Length() < this->length); + return test; + } + + Vector2D getTip() + { + libnum::num32 angle = this->restAngle + this->sign * this->rotation; + Vector2D D( COS(angle) , SIN(angle) ); + + Vector2D tip = this->pos.Clone(); + tip.Add(D, this->length); + return tip; + } + + libnum::num32 radius; + Vector2D pos; + libnum::num32 length; + libnum::num32 restAngle; + libnum::num32 maxRotation; + libnum::num32 sign; + libnum::num32 angularVelocity; + libnum::num32 rotation; + libnum::num32 currentAngularVelocity; + libnum::num32 touchIdentifier; +}; + + +struct Scene +{ + Vector2D gravity; + libnum::num32 dt; + uint32_t score; + bool pause; + std::vector borders; + std::vector balls; + std::vector obstacles; + std::vector flippers; +}; + + +#endif \ No newline at end of file diff --git a/src/utilities.h b/src/utilities.h new file mode 100644 index 0000000..a1439ee --- /dev/null +++ b/src/utilities.h @@ -0,0 +1,48 @@ +#ifndef UTILITIES_H +#define UTILITIES_H + +#include +#include +#include + +void azrp_draw_text(int x, int y, char const *fmt, ...) +{ + char str[128]; + va_list args; + va_start(args, fmt); + vsnprintf(str, 128, fmt, args); + va_end(args); + + extern bopti_image_t img_font; + + for(int i = 0; str[i]; i++) { + if(str[i] < 32 || str[i] >= 0x7f) continue; + + int row = (str[i] - 32) >> 4; + int col = (str[i] - 32) & 15; + azrp_subimage(x + 5 * i, y, &img_font, 7 * col + 1, 9 * row + 1, 6, 8, DIMAGE_NONE); + } +} + +libnum::num32 COS( libnum::num32 angle ) +{ + float a = (float) angle; + float c = cos( a ); + return libnum::num32(c); +} + +libnum::num32 SIN( libnum::num32 angle ) +{ + float a = (float) angle; + float s = sin( a ); + return libnum::num32(s); +} + +#define MIN(a,b) ( ((a)>=(b)) ? (b) : (a) ) +#define MAX(a,b) ( ((a)<=(b)) ? (b) : (a) ) +#define ABS(a) ( ((a)>=0) ? (a) : -(a) ) +#define SIGN(a) ( (a)<0 ? -1 : (a>0) ? +1 : 0 ) + +#define PI 3.14159265 + +#endif \ No newline at end of file diff --git a/src/vector2D.h b/src/vector2D.h new file mode 100644 index 0000000..3d048f4 --- /dev/null +++ b/src/vector2D.h @@ -0,0 +1,210 @@ +#ifndef VECTOR2D_H +#define VECTOR2D_H + +#include +#include + +libnum::num32 sqrt_num32(libnum::num32 v) { + uint32_t t, q, b, r; + r = v.v; + b = 0x40000000; + q = 0; + while (b > 0x40) { + t = q + b; + if (r >= t) { + r -= t; + q = t + b; + } + r <<= 1; + b >>= 1; + } + q >>= 8; + libnum::num32 ret; + ret.v = q; + return ret; +} + + +class Vector2D { + +public: + Vector2D() { + this->x = libnum::num32(0); + this->y = libnum::num32(0); + } + + Vector2D(float x, float y) { + this->x = libnum::num32(x); + this->y = libnum::num32(y); + } + + Vector2D(libnum::num32 x, libnum::num32 y) { + this->x = x; + this->y = y; + } + + Vector2D(const Vector2D &v) { + this->x = v.x; + this->y = v.y; + } + + ~Vector2D() {} + + void Set( Vector2D v ) + { + this->x = v.x; + this->y = v.y; + } + + void Normalise( void ) + { + libnum::num32 len = this->Length(); + this->x /= len; + this->y /= len; + } + + Vector2D Clone(void) { + Vector2D NewVector(this->x, this->y); + return NewVector; + } + + Vector2D MakeVector(Vector2D A, Vector2D B) { + Vector2D NewVector(B.x - A.x, B.y - A.y); + return NewVector; + } + + void AddVectors(Vector2D a, Vector2D b) { + this->x = a.x + b.x; + this->y = a.y + b.y; + } + + void Add(Vector2D v, libnum::num32 scale) { + this->x += v.x * scale; + this->y += v.y * scale; + } + + void SubtractVectors(Vector2D a, Vector2D b) { + this->x = a.x - b.x; + this->y = a.y - b.y; + } + + void Subtract(Vector2D v, libnum::num32 scale) { + this->x -= v.x * scale; + this->y -= v.y * scale; + } + + libnum::num32 Length(void) { + return sqrt_num32(this->x * this->x + this->y * this->y); + } + + void Scale(libnum::num32 scale) { + this->x *= scale; + this->y *= scale; + } + + libnum::num32 Dot(Vector2D v) { return (this->x * v.x + this->y * v.y); } + + libnum::num32 Det(Vector2D v) { return (this->x * v.y - this->y * v.x); } + + Vector2D PerpCW(void) { + Vector2D temp(-this->y, this->x); + return temp; + } + + Vector2D PerpCCW(void) { + Vector2D temp(this->y, -this->x); + return temp; + } + + /* overloading of most interesting operators */ + libnum::num32 operator[](uint8_t pos) { return pos == 0 ? x : y; } + + Vector2D &operator=(const Vector2D &v) { + this->x = v.x; + this->y = v.y; + return *this; + } + + Vector2D operator+(const Vector2D &v) const { + return Vector2D(x + v.x, y + v.y); + } + + Vector2D operator-(const Vector2D &v) const { + return Vector2D(x - v.x, y - v.y); + } + + Vector2D &operator+=(Vector2D const &other) { + this->x += other.x; + this->y += other.y; + return *this; + } + + Vector2D operator-() const { return (Vector2D(-x, -y)); } + + Vector2D operator+() const { return *this; } + + Vector2D &operator-=(Vector2D const &other) { + this->x -= other.x; + this->y -= other.y; + return *this; + } + + Vector2D &operator*=(libnum::num32 scale) { + this->x *= scale; + this->y *= scale; + return *this; + } + + Vector2D &operator/=(libnum::num32 scale) { + this->x /= scale; + this->y /= scale; + return *this; + } + + friend Vector2D operator*(libnum::num32 scale, Vector2D const &v) { + Vector2D r; + r.x = v.x * scale; + r.y = v.y * scale; + return r; + } + + friend Vector2D operator*(Vector2D const &v, libnum::num32 scale) { + Vector2D r; + r.x = v.x * scale; + r.y = v.y * scale; + return r; + } + + friend Vector2D operator/(Vector2D const &v, libnum::num32 scale) { + Vector2D r; + r.x = v.x / scale; + r.y = v.y / scale; + return r; + } + + libnum::num32 x; + libnum::num32 y; +}; + + + +Vector2D ClosestPointOnSegment(Vector2D P, Vector2D A, Vector2D B ) { + Vector2D AB = B - A; + + libnum::num32 t = AB.Dot(AB); + + if (t == 0) return A; + + libnum::num32 t2 = (P.Dot(AB) - A.Dot(AB)) / t; + + if (t2 < libnum::num32(0)) t2 = libnum::num32(0); + if (t2 > libnum::num32(1)) t2 = libnum::num32(1); + + Vector2D C = A.Clone(); + C.Add(AB, t2); + + return C; +} + + +#endif \ No newline at end of file