diff --git a/bounce_cpp/Resource.aps b/bounce_cpp/Resource.aps index 1235c37..059d8fe 100644 Binary files a/bounce_cpp/Resource.aps and b/bounce_cpp/Resource.aps differ diff --git a/bounce_cpp/Resource.rc b/bounce_cpp/Resource.rc index 816f46b..815ac93 100644 Binary files a/bounce_cpp/Resource.rc and b/bounce_cpp/Resource.rc differ diff --git a/bounce_cpp/bounce_cpp.vcxproj b/bounce_cpp/bounce_cpp.vcxproj index 2aecb3d..5b3b94c 100644 --- a/bounce_cpp/bounce_cpp.vcxproj +++ b/bounce_cpp/bounce_cpp.vcxproj @@ -140,6 +140,21 @@ + + + + + + + + + + + + + + + diff --git a/bounce_cpp/bounce_cpp.vcxproj.filters b/bounce_cpp/bounce_cpp.vcxproj.filters index cefc746..e8ca333 100644 --- a/bounce_cpp/bounce_cpp.vcxproj.filters +++ b/bounce_cpp/bounce_cpp.vcxproj.filters @@ -39,5 +39,42 @@ Resource Files + + Resource Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + Resource Files + \ No newline at end of file diff --git a/bounce_cpp/hyrax1.wav b/bounce_cpp/hyrax1.wav new file mode 100644 index 0000000..5a92936 Binary files /dev/null and b/bounce_cpp/hyrax1.wav differ diff --git a/bounce_cpp/hyrax2.wav b/bounce_cpp/hyrax2.wav new file mode 100644 index 0000000..a145ecc Binary files /dev/null and b/bounce_cpp/hyrax2.wav differ diff --git a/bounce_cpp/hyrax3.wav b/bounce_cpp/hyrax3.wav new file mode 100644 index 0000000..55153a0 Binary files /dev/null and b/bounce_cpp/hyrax3.wav differ diff --git a/bounce_cpp/hyrax4.wav b/bounce_cpp/hyrax4.wav new file mode 100644 index 0000000..84e2bdf Binary files /dev/null and b/bounce_cpp/hyrax4.wav differ diff --git a/bounce_cpp/hyrax5.wav b/bounce_cpp/hyrax5.wav new file mode 100644 index 0000000..791bd9e Binary files /dev/null and b/bounce_cpp/hyrax5.wav differ diff --git a/bounce_cpp/hyrax6.wav b/bounce_cpp/hyrax6.wav new file mode 100644 index 0000000..589aba8 Binary files /dev/null and b/bounce_cpp/hyrax6.wav differ diff --git a/bounce_cpp/hyrax7.wav b/bounce_cpp/hyrax7.wav new file mode 100644 index 0000000..f7d38d9 Binary files /dev/null and b/bounce_cpp/hyrax7.wav differ diff --git a/bounce_cpp/hyrax8.wav b/bounce_cpp/hyrax8.wav new file mode 100644 index 0000000..9f7acfe Binary files /dev/null and b/bounce_cpp/hyrax8.wav differ diff --git a/bounce_cpp/hyrax9.wav b/bounce_cpp/hyrax9.wav new file mode 100644 index 0000000..1bdd258 Binary files /dev/null and b/bounce_cpp/hyrax9.wav differ diff --git a/bounce_cpp/hyrax_scream.jpeg b/bounce_cpp/hyrax_scream.jpeg new file mode 100644 index 0000000..caa63a8 Binary files /dev/null and b/bounce_cpp/hyrax_scream.jpeg differ diff --git a/bounce_cpp/main.cpp b/bounce_cpp/main.cpp index 088e03d..2a338a4 100644 --- a/bounce_cpp/main.cpp +++ b/bounce_cpp/main.cpp @@ -18,10 +18,16 @@ rattatwinko - 04/05/26 #include #include #include +#include #include #include +#include +#include +#include +#include "resource.h" #pragma comment(lib, "gdiplus.lib") +#pragma comment(lib, "winmm.lib") using namespace Gdiplus; @@ -33,13 +39,19 @@ static constexpr double GROUND_FRICTION = 0.8; static constexpr double REST_THRESHOLD = 5.0; static constexpr UINT TIMER_MS = 10; static constexpr int IMAGE_RESOURCE = 111; -static constexpr int RESIZE_BORDER = 12; // make wider so easier grab +static constexpr int RESIZE_BORDER = 12; // make wider so easier grab +static constexpr int DRAG_THRESHOLD = 5; // pixels before its a drag not a click +static constexpr int WAV_COUNT = 9; +static constexpr int MAX_PEAKS = 64; // max screams per sound +static constexpr double PEAK_HOLD = 0.10; // seconds which we hold the awawa face +static constexpr double PEAK_THRESH = 0.65; // fraction of max amplitude to count as a peak // globals -static ULONG_PTR g_gdiplusToken = 0; -static Image* g_image = nullptr; -static int g_winW = 640; -static int g_winH = 480; +static ULONG_PTR g_gdiplusToken = 0; +static Image* g_image = nullptr; // normal hyrax +static Image* g_imageScream = nullptr; // awawa hyrax +static int g_winW = 640; +static int g_winH = 480; static LARGE_INTEGER g_freq = {}; // physics states @@ -47,14 +59,24 @@ static double g_x = 200.0, g_y = 50.0; static double g_vx = 0.0, g_vy = 0.0; static double g_lastTime = 0.0; -// resize status +// resize / drag status static bool g_resizing = false; static bool g_dragging = false; +static bool g_didDrag = false; static int g_dragOffsetX = 0; static int g_dragOffsetY = 0; static POINT g_lastMouse = {}; +static POINT g_clickOrigin = {}; static double g_lastMouseTime = 0.0; +// scream face stuff +static bool g_screaming = false; +static double g_screamUntil = 0.0; // timestamp to stop screaming +static double g_soundStart = 0.0; // when playback began +static double g_peakTimes[MAX_PEAKS] = {}; +static int g_peakCount = 0; +static int g_peakIdx = 0; // next peak we havent fired yet + // helper definitions static double Now() { LARGE_INTEGER t; @@ -62,8 +84,9 @@ static double Now() { return static_cast(t.QuadPart) / static_cast(g_freq.QuadPart); } -static Image* LoadImageFromResource(HINSTANCE hInst, int resId) { - HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resId), RT_RCDATA); +// load any image from any rc resource type +static Image* LoadImageFromResource(HINSTANCE hInst, int resId, LPCWSTR resType) { + HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resId), resType); if (!hRes) return nullptr; DWORD size = SizeofResource(hInst, hRes); @@ -101,6 +124,93 @@ static Image* LoadImageFromResource(HINSTANCE hInst, int resId) { return img; } +// walk RAW fucking PCM data and check for peaks +static void FindWavPeaks(const BYTE* buf, DWORD size) { + g_peakCount = 0; + g_peakIdx = 0; + + if (size < 44) return; + if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0) return; + + WORD channels = 0; + DWORD sampleRate = 0; + WORD bitsPerSample = 0; + const BYTE* pcm = nullptr; + DWORD pcmSize = 0; + + // walk the chunks + DWORD pos = 12; + while (pos + 8 <= size) { + DWORD chunkSize = *reinterpret_cast(buf + pos + 4); + + if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) { + channels = *reinterpret_cast (buf + pos + 10); + sampleRate = *reinterpret_cast(buf + pos + 12); + bitsPerSample = *reinterpret_cast (buf + pos + 22); + } + else if (memcmp(buf + pos, "data", 4) == 0) { + pcm = buf + pos + 8; + pcmSize = chunkSize; + break; + } + + pos += 8 + chunkSize; + if (chunkSize & 1) pos++; // align word cause microsoft likes to fuck my ass + } + + // only 16bit PCM cause everything else is just stoopid + if (!pcm || !sampleRate || bitsPerSample != 16) return; + + DWORD totalSamples = pcmSize / (channels * sizeof(int16_t)); + + // global maximum + int maxAmp = 1; + for (DWORD i = 0; i < pcmSize / sizeof(int16_t); i++) { + int s = abs((int)*reinterpret_cast(pcm + i * sizeof(int16_t))); + if (s > maxAmp) maxAmp = s; + } + + int threshold = static_cast(maxAmp * PEAK_THRESH); + DWORD windowSamples = sampleRate / 50; // 20ms windows + DWORD debounce = sampleRate / 8; // 125ms min gap so we dont spam + DWORD lastPeak = 0xFFFFFFFF; + + // stupid fucking logic. scan for loud moments + for (DWORD s = 0; s + windowSamples <= totalSamples; s += windowSamples / 2) { + int winMax = 0; + for (DWORD j = 0; j < windowSamples * channels; j++) { + DWORD byteOff = (s * channels + j) * sizeof(int16_t); + if (byteOff + sizeof(int16_t) > pcmSize) break; + int amp = abs((int)*reinterpret_cast(pcm + byteOff)); + if (amp > winMax) winMax = amp; + } + + bool gapOk = (lastPeak == 0xFFFFFFFF) || (s - lastPeak >= debounce); + if (winMax >= threshold && gapOk && g_peakCount < MAX_PEAKS) { + g_peakTimes[g_peakCount++] = static_cast(s) / sampleRate; + lastPeak = s; + } + } +} + +// play a random hyrax sound and prep the scream timeline +static void PlayRandomSound(HINSTANCE hInst) { + int id = IDR_WAVE1 + (rand() % WAV_COUNT); + + // grab raw wave data and look for peaks ; possible cause of .rc WAVE type + HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(id), L"WAVE"); + HGLOBAL hData = hRes ? LoadResource(hInst, hRes) : nullptr; + BYTE* pData = hData ? static_cast(LockResource(hData)) : nullptr; + DWORD sz = hRes ? SizeofResource(hInst, hRes) : 0; + + if (pData && sz) + FindWavPeaks(pData, sz); + + g_soundStart = Now(); + + PlaySoundW(MAKEINTRESOURCEW(id), hInst, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT); +} + // physics "engine" static void StepPhysics(double dt) { const int screenW = GetSystemMetrics(SM_CXSCREEN); @@ -113,7 +223,7 @@ static void StepPhysics(double dt) { g_x += g_vx * dt; g_y += g_vy * dt; - // left/ right monitor borders + // left / right monitor borders if (g_x < 0.0) { g_x = 0.0; g_vx = -g_vx * RESTITUTION; @@ -143,15 +253,18 @@ static void StepPhysics(double dt) { static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - // init the window + // init the window case WM_CREATE: { QueryPerformanceFrequency(&g_freq); + srand(static_cast(time(nullptr))); GdiplusStartupInput si; GdiplusStartup(&g_gdiplusToken, &si, nullptr); HINSTANCE hInst = reinterpret_cast(lParam)->hInstance; - g_image = LoadImageFromResource(hInst, IMAGE_RESOURCE); + + g_image = LoadImageFromResource(hInst, IDR_RCDATA1, RT_RCDATA); + g_imageScream = LoadImageFromResource(hInst, IDB_BITMAP1, L"JPG"); if (g_image) { g_winW = static_cast(g_image->GetWidth()); @@ -170,11 +283,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara // drag start case WM_LBUTTONDOWN: { g_dragging = true; + g_didDrag = false; g_dragOffsetX = GET_X_LPARAM(lParam); g_dragOffsetY = GET_Y_LPARAM(lParam); g_vx = g_vy = 0.0; GetCursorPos(&g_lastMouse); + g_clickOrigin = g_lastMouse; g_lastMouseTime = Now(); SetCapture(hwnd); @@ -188,6 +303,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara POINT p; GetCursorPos(&p); + if (abs(p.x - g_clickOrigin.x) > DRAG_THRESHOLD || + abs(p.y - g_clickOrigin.y) > DRAG_THRESHOLD) + g_didDrag = true; + const double now = Now(); const double dt = now - g_lastMouseTime; @@ -207,12 +326,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara break; } - // drag end - case WM_LBUTTONUP: + // drag end or click + case WM_LBUTTONUP: { + if (!g_didDrag) { + HINSTANCE hInst = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_HINSTANCE)); + PlayRandomSound(hInst); + } g_dragging = false; ReleaseCapture(); break; - + } + // while dragging nutz accross fohead disable OS from doing shit case WM_ENTERSIZEMOVE: g_resizing = true; @@ -269,7 +393,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara } return DefWindowProcW(hwnd, msg, wParam, lParam); - // phyisks tick + // phyisks tick and scream face check case WM_TIMER: { const double now = Now(); const double dt = now - g_lastTime; @@ -280,6 +404,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara SetWindowPos(hwnd, nullptr, static_cast(g_x), static_cast(g_y), 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } + + // extend scream hold + if (g_peakCount > 0) { + double elapsed = now - g_soundStart; + while (g_peakIdx < g_peakCount && elapsed >= g_peakTimes[g_peakIdx]) { + g_screamUntil = now + PEAK_HOLD; + g_peakIdx++; + } + } + + // flip face state + bool shouldScream = (g_imageScream && now < g_screamUntil); + if (shouldScream != g_screaming) { + g_screaming = shouldScream; + InvalidateRect(hwnd, nullptr, FALSE); + } + break; } @@ -296,8 +437,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara Graphics g(memDC); g.SetInterpolationMode(InterpolationModeHighQualityBicubic); - if (g_image) - g.DrawImage(g_image, 0, 0, g_winW, g_winH); + // swap to scream face on peaks + Image* frame = (g_screaming && g_imageScream) ? g_imageScream : g_image; + if (frame) + g.DrawImage(frame, 0, 0, g_winW, g_winH); BitBlt(hdc, 0, 0, g_winW, g_winH, memDC, 0, 0, SRCCOPY); @@ -317,11 +460,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara DestroyWindow(hwnd); break; + // consuela case WM_DESTROY: KillTimer(hwnd, 1); delete g_image; - g_image = nullptr; + delete g_imageScream; + g_image = g_imageScream = nullptr; GdiplusShutdown(g_gdiplusToken); PostQuitMessage(0); break; diff --git a/bounce_cpp/resource.h b/bounce_cpp/resource.h index 74fc674..b604e36 100644 --- a/bounce_cpp/resource.h +++ b/bounce_cpp/resource.h @@ -4,12 +4,22 @@ // #define IDR_RCDATA1 111 #define IDI_ICON1 114 +#define IDR_WAVE1 115 +#define IDR_WAVE2 116 +#define IDR_WAVE3 118 +#define IDR_WAVE4 119 +#define IDR_WAVE5 120 +#define IDR_WAVE6 121 +#define IDR_WAVE7 122 +#define IDR_WAVE8 123 +#define IDR_WAVE9 124 +#define IDB_BITMAP1 126 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 115 +#define _APS_NEXT_RESOURCE_VALUE 127 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101