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