From fdba6ef0776a22ad9b32492bc27cd183f3a97ebb Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 1 Mar 2026 17:49:14 +0000 Subject: [PATCH] Add Earth sprite to moon parallax background Render earth.png as a non-tiling feature sprite in the moon sky, drawn between the far starfield and near crater terrain layers. Introduce ParallaxFeature struct for single-instance background sprites that scroll with the camera but never tile. Clean up the feature reference in parallax_free to prevent stale pointers between level transitions. --- assets/sprites/earth.png | Bin 0 -> 2758 bytes src/engine/parallax.c | 36 ++++++++++++++++++++++++++++++++++++ src/engine/parallax.h | 17 ++++++++++++++--- 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 assets/sprites/earth.png diff --git a/assets/sprites/earth.png b/assets/sprites/earth.png new file mode 100644 index 0000000000000000000000000000000000000000..58d1b1919a3e931ba6cde2a72c085442133257a3 GIT binary patch literal 2758 zcmZ`*cU+U_7R~^KfME~AihyP?5~gel31tWxmIOsg2p}Vi5HWyY3CIqJgc7+dQLBh- zum}!jEl}BLMP*uyh(s?IX(jarwc6X?z3=b$zUTSQ`#jJ2zH|QhatH_9c0ynf006Ml z!yWG{j_~a*EhRqVlak&50KntXI2<7s=Z2$G=uAHbi5!8Cj%QFvi3D>109kbW%&q-> z1eJk>n~a7SC%S6J!ydQjZ+RpZR!$xf*4KN{){+= z0)B_$lX6>BM-+Ru)PF{A_U^AD>+<*;!QGmKU4|PVN`2`uK0N2eX5`yAl*09txs7{ScUxPYxI&KFNVnqf<5zyP!rFH3*7mGc|R%Fo_bi?K975-0@# zixCh25+5*tjaE&kJIi9ZmteS3D4S=%7r zh56~SaM#a;YR@vnOS zpYczxFU13B(JaTm4E~Y+q_W3sx9o2%{5YQvS@D1zA@-Qh!xmrN+?4_VND)2o&i+K1 zOM$VJ;sA}*+YN6Up3FR%p|0KrYL-fjq1Iit(u$B8>3??XhK^P5Hihv7@|(^z&IUCy zKWdOwPVoU!CDnV&qzfb>mel&Ry5_p}|1kTg@#ReF?PsxfX7AHf4;F6(&#pU;CXUz$ zlZO`WsHL21T2(3J9!an#V^5J~HU%EmA&<^2?0sl)_;484KIfVj9Jf+<` z9<(S9mAcbFD8mpA-z_Vja=jkiCv05h20bI-E2q`LbJSs5-a*8XX|-0xc30crPLcW{ z&EKLLVwKOFme4N?@$ES>da};GCloHkbtGZ5Bqwr|{oI|E=qmCOfyQcM-6uh=< z5s@N$2=qSdX>jJ$GDJTlrVjTYUpqMlM9eo@tawDyil)1!6savB$HqHlYCXf}IQ|M! zWp0TrBl+LgxRk%F`qr>1TQ=QMqE`{5`YK=)xh!(FPJCtz^UVE@dEx^Y!dD&pnc{9q5U1l-iD?`D_(Qd0YmAu zZvU2ZhwznD*`&9|4}1Bbwm(G2?)34qvRdE?CPI^rrVF!j>At`Umx94RN5>szxOPh? z&aRz94+9a#ld+VL;L{nP_>&5e6)-2R*9Cli|Kx9K{wtPIC=C!|sfn}%_1{%DA@f+C zZL9jz!)fc(4Ta3`7!_0rL5b+_WUle~ynS#Gr>gHT#qXQG`sO2irdNblp=F+hE8}&; zHL{Hz>S8ejbXVH`eUBBNwU-F{M#@dMt}3heKuZYf?!a!t)sxa?3+dPxE-|nj3cUk1 z`y+}6+r*`6SV(2`?v{I$X=>|Krfg1y-`=^XRxWfbj7hPC@w%$vlAI!BS)Qy<-Geah z;J0ZFA~rKtVS#Nsp$!7Z= z=ziMJMuaQPN7_gayv~9OcOWw_G$AY8O(K3;>Bk_(!f0vtsOgxeVBN(9XvglQaK`#t zgeY4g0|Juw;mIi|a@C}zCJ?{ITspH-NLTDq?O|Xad7{lTGb?ZbG)v?b3t2uiwK=`c9kHJgXXFeeNKgg!9U+PU=aTf@!u z?m-Ue`2)a1dFpGUDGCCqk$2C0f`W4jTA;7d&29GTj4)G}>fDZ_tx!{nlW#z#<6&dJ z8sa5|o^y3;MKm@@XR72rDfv;t>&W2ZuY6M{RYSpEk{f!F`Yp$g~{G^qm9WI>9MG}!+ #include @@ -795,6 +796,21 @@ static void generate_moon_far(Parallax *p, SDL_Renderer *renderer) { p->far_layer.scroll_y = 0.03f; p->far_layer.active = true; p->far_layer.owns_texture = true; + + /* Earth in the sky — non-tiling feature sprite */ + SDL_Texture *earth_tex = assets_get_texture("assets/sprites/earth.png"); + if (earth_tex) { + int ew, eh; + SDL_QueryTexture(earth_tex, NULL, NULL, &ew, &eh); + p->feature.texture = earth_tex; + p->feature.tex_w = ew; + p->feature.tex_h = eh; + p->feature.x = (float)(w / 4); + p->feature.y = (float)(h * 0.08f); + p->feature.scroll_x = 0.03f; /* same as far layer */ + p->feature.scroll_y = 0.03f; + p->feature.active = true; + } } static void generate_moon_near(Parallax *p, SDL_Renderer *renderer) { @@ -953,9 +969,25 @@ static void render_layer(const ParallaxLayer *layer, const Camera *cam, } } +static void render_feature(const ParallaxFeature *f, const Camera *cam, + SDL_Renderer *renderer) { + if (!f->active || !f->texture) return; + + /* Scroll with camera but do not tile */ + int draw_x = (int)(f->x - cam->pos.x * f->scroll_x); + int draw_y = (int)(f->y - cam->pos.y * f->scroll_y); + + SDL_Rect dst = {draw_x, draw_y, f->tex_w, f->tex_h}; + if (dst.x + dst.w < 0 || dst.x >= SCREEN_WIDTH) return; + if (dst.y + dst.h < 0 || dst.y >= SCREEN_HEIGHT) return; + + SDL_RenderCopy(renderer, f->texture, NULL, &dst); +} + void parallax_render(const Parallax *p, const Camera *cam, SDL_Renderer *renderer) { if (!p || !cam) return; render_layer(&p->far_layer, cam, renderer); + render_feature(&p->feature, cam, renderer); render_layer(&p->near_layer, cam, renderer); } @@ -974,4 +1006,8 @@ void parallax_free(Parallax *p) { } p->near_layer.texture = NULL; p->near_layer.active = false; + + /* Feature sprite is asset-manager-owned — just clear the reference */ + p->feature.texture = NULL; + p->feature.active = false; } diff --git a/src/engine/parallax.h b/src/engine/parallax.h index 11c3de2..35f7d8f 100644 --- a/src/engine/parallax.h +++ b/src/engine/parallax.h @@ -18,10 +18,21 @@ typedef struct ParallaxLayer { bool owns_texture; /* true if we generated it (must free) */ } ParallaxLayer; -/* Parallax background system — up to two layers */ +/* A non-tiling sprite drawn once in the background (e.g. Earth from moon) */ +typedef struct ParallaxFeature { + SDL_Texture *texture; /* sprite image */ + int tex_w, tex_h; /* sprite dimensions */ + float x, y; /* base position (pixels, screen) */ + float scroll_x; /* horizontal scroll factor */ + float scroll_y; /* vertical scroll factor */ + bool active; +} ParallaxFeature; + +/* Parallax background system — two tiling layers + optional feature sprite */ typedef struct Parallax { - ParallaxLayer far_layer; /* distant background (stars) */ - ParallaxLayer near_layer; /* mid-ground background (nebula) */ + ParallaxLayer far_layer; /* distant background (stars) */ + ParallaxLayer near_layer; /* mid-ground background (nebula) */ + ParallaxFeature feature; /* non-tiling overlay (planet, etc.) */ } Parallax; /* Initialize parallax (zeroes everything) */