Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8630078345 | |||
| fc4dfd6f43 | |||
| 4951a79124 |
@@ -23,7 +23,8 @@
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{61c6f079-9b1c-4d66-b9c7-4646f87181a6}</ProjectGuid>
|
||||
<RootNamespace>bouncecpp</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>hyrax</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@@ -109,6 +110,8 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>gdiplus.lib;
|
||||
winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@@ -125,13 +128,18 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>gdiplus.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="physics.cpp" />
|
||||
<ClCompile Include="sound_handler.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="physics.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="sound_handler.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc" />
|
||||
|
||||
@@ -18,11 +18,23 @@
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="physics.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sound_handler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="physics.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sound_handler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc">
|
||||
|
||||
Binary file not shown.
+45
-177
@@ -8,6 +8,7 @@ Quick description of the app:
|
||||
- A hyrax picture which bounces around your desktop, like bonzi buddy but not destructive, more of a fun thing which you can open
|
||||
when you get bored.
|
||||
- Run the exe youll find out.
|
||||
- you can click and it will awawa
|
||||
|
||||
This is not malware or a virus, its purely for fun and entertainment purpouses.
|
||||
|
||||
@@ -24,27 +25,18 @@ rattatwinko - 04/05/26
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "resource.h"
|
||||
|
||||
#pragma comment(lib, "gdiplus.lib")
|
||||
#pragma comment(lib, "winmm.lib")
|
||||
#include "sound_handler.h"
|
||||
#include "physics.h"
|
||||
|
||||
using namespace Gdiplus;
|
||||
|
||||
// consts
|
||||
static constexpr double GRAVITY = 1200.0;
|
||||
static constexpr double RESTITUTION = 0.6;
|
||||
static constexpr double AIR_DRAG = 0.999;
|
||||
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 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;
|
||||
@@ -54,10 +46,8 @@ static int g_winW = 640;
|
||||
static int g_winH = 480;
|
||||
static LARGE_INTEGER g_freq = {};
|
||||
|
||||
// physics states
|
||||
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;
|
||||
// physics object (initial position 200,50 with zero velocity)
|
||||
static physics g_physics(200.0, 50.0, 0.0, 0.0);
|
||||
|
||||
// resize / drag status
|
||||
static bool g_resizing = false;
|
||||
@@ -68,14 +58,10 @@ static int g_dragOffsetY = 0;
|
||||
static POINT g_lastMouse = {};
|
||||
static POINT g_clickOrigin = {};
|
||||
static double g_lastMouseTime = 0.0;
|
||||
static double g_lastTime = 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
|
||||
static std::unique_ptr<sound_handler> g_sound;
|
||||
|
||||
// helper definitions
|
||||
static double Now() {
|
||||
@@ -124,131 +110,6 @@ static Image* LoadImageFromResource(HINSTANCE hInst, int resId, LPCWSTR resType)
|
||||
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<const DWORD*>(buf + pos + 4);
|
||||
|
||||
if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) {
|
||||
channels = *reinterpret_cast<const WORD*> (buf + pos + 10);
|
||||
sampleRate = *reinterpret_cast<const DWORD*>(buf + pos + 12);
|
||||
bitsPerSample = *reinterpret_cast<const WORD*> (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<const int16_t*>(pcm + i * sizeof(int16_t)));
|
||||
if (s > maxAmp) maxAmp = s;
|
||||
}
|
||||
|
||||
int threshold = static_cast<int>(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<const int16_t*>(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<double>(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<BYTE*>(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);
|
||||
const int screenH = GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
g_vy += GRAVITY * dt;
|
||||
g_vx *= AIR_DRAG;
|
||||
g_vy *= AIR_DRAG;
|
||||
|
||||
g_x += g_vx * dt;
|
||||
g_y += g_vy * dt;
|
||||
|
||||
// left / right monitor borders
|
||||
if (g_x < 0.0) {
|
||||
g_x = 0.0;
|
||||
g_vx = -g_vx * RESTITUTION;
|
||||
}
|
||||
else if (g_x > screenW - g_winW) {
|
||||
g_x = static_cast<double>(screenW - g_winW);
|
||||
g_vx = -g_vx * RESTITUTION;
|
||||
}
|
||||
|
||||
// top
|
||||
if (g_y < 0.0) {
|
||||
g_y = 0.0;
|
||||
g_vy = -g_vy * RESTITUTION;
|
||||
}
|
||||
|
||||
// bottom
|
||||
if (g_y > screenH - g_winH) {
|
||||
g_y = static_cast<double>(screenH - g_winH);
|
||||
g_vy = -g_vy * RESTITUTION;
|
||||
g_vx *= GROUND_FRICTION;
|
||||
if (fabs(g_vy) < REST_THRESHOLD)
|
||||
g_vy = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// win api window proc
|
||||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (msg) {
|
||||
@@ -267,12 +128,22 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
g_imageScream = LoadImageFromResource(hInst, IDB_BITMAP1, L"JPG");
|
||||
|
||||
if (g_image) {
|
||||
g_winW = static_cast<int>(g_image->GetWidth());
|
||||
g_winH = static_cast<int>(g_image->GetHeight());
|
||||
g_winW = 480;
|
||||
g_winH = 480;
|
||||
SetWindowPos(hwnd, nullptr, 0, 0, g_winW, g_winH,
|
||||
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
// sound handler
|
||||
g_sound = std::make_unique<sound_handler>(
|
||||
hInst,
|
||||
std::initializer_list<int>{
|
||||
IDR_WAVE1, IDR_WAVE2, IDR_WAVE3,
|
||||
IDR_WAVE4, IDR_WAVE5, IDR_WAVE6,
|
||||
IDR_WAVE7, IDR_WAVE8, IDR_WAVE9
|
||||
}
|
||||
);
|
||||
|
||||
g_lastTime = Now();
|
||||
g_lastMouseTime = g_lastTime;
|
||||
|
||||
@@ -286,7 +157,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
g_didDrag = false;
|
||||
g_dragOffsetX = GET_X_LPARAM(lParam);
|
||||
g_dragOffsetY = GET_Y_LPARAM(lParam);
|
||||
g_vx = g_vy = 0.0;
|
||||
g_physics.resetVelocity(); // stop all motion
|
||||
|
||||
GetCursorPos(&g_lastMouse);
|
||||
g_clickOrigin = g_lastMouse;
|
||||
@@ -311,17 +182,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
const double dt = now - g_lastMouseTime;
|
||||
|
||||
if (dt > 0.0) {
|
||||
g_vx = (p.x - g_lastMouse.x) / dt;
|
||||
g_vy = (p.y - g_lastMouse.y) / dt;
|
||||
double vx = (p.x - g_lastMouse.x) / dt;
|
||||
double vy = (p.y - g_lastMouse.y) / dt;
|
||||
g_physics.setVelocity(vx, vy);
|
||||
}
|
||||
|
||||
g_lastMouse = p;
|
||||
g_lastMouseTime = now;
|
||||
|
||||
g_x = p.x - g_dragOffsetX;
|
||||
g_y = p.y - g_dragOffsetY;
|
||||
g_physics.setPos(p.x - g_dragOffsetX, p.y - g_dragOffsetY);
|
||||
|
||||
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
|
||||
SetWindowPos(hwnd, nullptr, static_cast<int>(g_physics.x()), static_cast<int>(g_physics.y()),
|
||||
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
break;
|
||||
}
|
||||
@@ -329,8 +200,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
// drag end or click
|
||||
case WM_LBUTTONUP: {
|
||||
if (!g_didDrag) {
|
||||
HINSTANCE hInst = reinterpret_cast<HINSTANCE>(GetWindowLongPtrW(hwnd, GWLP_HINSTANCE));
|
||||
PlayRandomSound(hInst);
|
||||
// play random sound
|
||||
if (g_sound)
|
||||
g_sound->PlayRandom();
|
||||
}
|
||||
g_dragging = false;
|
||||
ReleaseCapture();
|
||||
@@ -344,13 +216,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
|
||||
case WM_EXITSIZEMOVE: {
|
||||
g_resizing = false;
|
||||
g_vx = g_vy = 0.0;
|
||||
g_physics.resetVelocity();
|
||||
|
||||
// sync gx/y with resized cords
|
||||
RECT r;
|
||||
GetWindowRect(hwnd, &r);
|
||||
g_x = static_cast<double>(r.left);
|
||||
g_y = static_cast<double>(r.top);
|
||||
g_physics.setPos(static_cast<double>(r.left), static_cast<double>(r.top));
|
||||
|
||||
g_lastTime = Now(); // after everything is clean then redraw
|
||||
break;
|
||||
@@ -400,27 +271,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
g_lastTime = now;
|
||||
|
||||
if (!g_dragging && !g_resizing) {
|
||||
StepPhysics(dt);
|
||||
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
|
||||
int screenW = GetSystemMetrics(SM_CXSCREEN);
|
||||
int screenH = GetSystemMetrics(SM_CYSCREEN);
|
||||
g_physics.update(dt, screenW, screenH, g_winW, g_winH);
|
||||
SetWindowPos(hwnd, nullptr, static_cast<int>(g_physics.x()), static_cast<int>(g_physics.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++;
|
||||
// use sound handler instead of doing this in main
|
||||
if (g_sound) {
|
||||
g_sound->Update(now);
|
||||
bool shouldScream = (g_imageScream && g_sound->IsAwAwA(now));
|
||||
if (shouldScream != g_screaming) {
|
||||
g_screaming = shouldScream;
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// flip face state
|
||||
bool shouldScream = (g_imageScream && now < g_screamUntil);
|
||||
if (shouldScream != g_screaming) {
|
||||
g_screaming = shouldScream;
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -460,10 +327,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
|
||||
|
||||
|
||||
// consuela
|
||||
case WM_DESTROY:
|
||||
KillTimer(hwnd, 1);
|
||||
g_sound.reset(); // clean up sound handler
|
||||
delete g_image;
|
||||
delete g_imageScream;
|
||||
g_image = g_imageScream = nullptr;
|
||||
@@ -491,7 +359,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) {
|
||||
WS_EX_TOPMOST,
|
||||
L"Hyrax", L"",
|
||||
WS_POPUP,
|
||||
static_cast<int>(g_x), static_cast<int>(g_y),
|
||||
static_cast<int>(g_physics.x()), static_cast<int>(g_physics.y()),
|
||||
g_winW, g_winH,
|
||||
nullptr, nullptr, hInstance, nullptr
|
||||
);
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Preamble:
|
||||
These exact physics calculations are documented in a TeX Document soon to be in the source tree of
|
||||
the project. (as of 06/05/26 not in the tree; as of 07/05/26 there is a TeX doc. file name: hyrax_exe-TeX_07_05_2026.pdf)
|
||||
|
||||
The physics calculations are done using a Semi-Implicit Euler integrator. (big words)
|
||||
|
||||
| Parameter | Value | Description | Mapped code identifier |
|
||||
|--------------------|--------|------------------------------------------------|------------------------------|
|
||||
| `GRAVITY` | 1200.0 | Downward acceleration (px s^-2) | `GRAVITY` (constant) |
|
||||
| `RESTITUTION` | 0.6 | Energy retention after bounce | `RESTITUTION` (constant) |
|
||||
| `AIR_DRAG` | 0.999 | Velocity multiplier per frame | `AIR_DRAG` (constant) |
|
||||
| `GROUND_FRICTION` | 0.8 | Horizontal speed reduction on ground | `GROUND_FRICTION` (constant) |
|
||||
| `REST_THRESHOLD` | 5.0 | Minimum `\|v_y\|` to consider at rest | `REST_THRESHOLD` (constant) |
|
||||
|
||||
|
||||
06/05/26 - rattatwinko ; when cleaning the source code
|
||||
*/
|
||||
|
||||
#include "physics.h"
|
||||
#include <cmath>
|
||||
|
||||
physics::physics() : physics(0.0, 0.0) {}
|
||||
|
||||
physics::physics(double x, double y, double vx, double vy)
|
||||
: m_x(x), m_y(y), m_vx(vx), m_vy(vy) {
|
||||
}
|
||||
|
||||
void physics::update(double dt, int screenWidth, int screenHeight, int objWidth, int objHeight) {
|
||||
m_vy += GRAVITY * dt;
|
||||
m_vx *= AIR_DRAG;
|
||||
m_vy *= AIR_DRAG;
|
||||
|
||||
m_x += m_vx * dt;
|
||||
m_y += m_vy * dt;
|
||||
|
||||
const double rightBound = static_cast<double>(screenWidth - objWidth);
|
||||
const double bottomBound = static_cast<double>(screenHeight - objHeight);
|
||||
|
||||
if (m_x < 0.0) {
|
||||
m_x = 0.0;
|
||||
m_vx = -m_vx * RESTITUTION;
|
||||
}
|
||||
else if (m_x > rightBound) {
|
||||
m_x = rightBound;
|
||||
m_vx = -m_vx * RESTITUTION;
|
||||
}
|
||||
|
||||
if (m_y < 0.0) {
|
||||
m_y = 0.0;
|
||||
m_vy = -m_vy * RESTITUTION;
|
||||
}
|
||||
else if (m_y > bottomBound) {
|
||||
m_y = bottomBound;
|
||||
m_vy = -m_vy * RESTITUTION;
|
||||
m_vx *= GROUND_FRICTION;
|
||||
if (std::fabs(m_vy) < REST_THRESHOLD)
|
||||
m_vy = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void physics::setPos(double x, double y) {
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
}
|
||||
|
||||
void physics::setVelocity(double vx, double vy) {
|
||||
m_vx = vx;
|
||||
m_vy = vy;
|
||||
}
|
||||
|
||||
void physics::resetVelocity() {
|
||||
m_vx = m_vy = 0.0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#define scd static constexpr double
|
||||
|
||||
class physics
|
||||
{
|
||||
public:
|
||||
scd GRAVITY = 2500.0;
|
||||
scd RESTITUTION = 0.9;
|
||||
scd AIR_DRAG = 0.999;
|
||||
scd GROUND_FRICTION = 0.8;
|
||||
scd REST_THRESHOLD = 5.0;
|
||||
|
||||
physics();
|
||||
physics(double x, double y, double vx = 0.0, double vy = 0.0);
|
||||
void update(double dt, int screenW, int screenH, int objW, int objH);
|
||||
|
||||
// getters / setters
|
||||
double x() const { return m_x; }
|
||||
double y() const { return m_y; }
|
||||
double vx() const { return m_vx; }
|
||||
double vy() const { return m_vy; }
|
||||
void setPos(double x, double y);
|
||||
void setVelocity(double vx, double vy);
|
||||
void resetVelocity();
|
||||
|
||||
private:
|
||||
double m_x, m_y;
|
||||
double m_vx, m_vy;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "sound_handler.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
sound_handler::sound_handler(HINSTANCE hInst, std::initializer_list<int> waveIds)
|
||||
: m_hInst(hInst), m_wavIds(waveIds)
|
||||
{
|
||||
for (int id : m_wavIds)
|
||||
m_peaks.push_back(AnalyzeWavResource(id));
|
||||
}
|
||||
|
||||
void sound_handler::PlayRandom()
|
||||
{
|
||||
if (m_wavIds.empty()) return;
|
||||
|
||||
int idx = rand() % static_cast<int>(m_wavIds.size());
|
||||
m_currentWave = idx;
|
||||
m_soundStart = Now();
|
||||
m_nextPeak = 0;
|
||||
|
||||
int id = m_wavIds[idx];
|
||||
PlaySoundW(MAKEINTRESOURCEW(id), m_hInst,
|
||||
SND_RESOURCE | SND_ASYNC | SND_NODEFAULT);
|
||||
}
|
||||
|
||||
void sound_handler::Update(double now)
|
||||
{
|
||||
if (m_currentWave < 0 || m_currentWave >= static_cast<int>(m_peaks.size()))
|
||||
return;
|
||||
|
||||
const auto& peaks = m_peaks[m_currentWave].times;
|
||||
double elapsed = now - m_soundStart;
|
||||
|
||||
while (m_nextPeak < static_cast<int>(peaks.size()) && elapsed >= peaks[m_nextPeak]) {
|
||||
m_screamUntil = now + p_hold;
|
||||
++m_nextPeak;
|
||||
}
|
||||
}
|
||||
|
||||
bool sound_handler::IsAwAwA(double now) const
|
||||
{
|
||||
return now < m_screamUntil;
|
||||
}
|
||||
|
||||
sound_handler::PeakData sound_handler::AnalyzeWavResource(int resId)
|
||||
{
|
||||
PeakData data;
|
||||
|
||||
HRSRC hRes = FindResource(m_hInst, MAKEINTRESOURCE(resId), L"WAVE");
|
||||
if (!hRes) return data;
|
||||
|
||||
HGLOBAL hData = LoadResource(m_hInst, hRes);
|
||||
if (!hData) return data;
|
||||
|
||||
DWORD size = SizeofResource(m_hInst, hRes);
|
||||
const BYTE* buf = static_cast<const BYTE*>(LockResource(hData));
|
||||
if (!buf) return data;
|
||||
|
||||
if (size < 44) return data;
|
||||
if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0)
|
||||
return data;
|
||||
|
||||
WORD channels = 0;
|
||||
DWORD sampleRate = 0;
|
||||
WORD bitsPerSample = 0;
|
||||
const BYTE* pcm = nullptr;
|
||||
DWORD pcmSize = 0;
|
||||
|
||||
DWORD pos = 12;
|
||||
while (pos + 8 <= size) {
|
||||
DWORD chunkSize = *reinterpret_cast<const DWORD*>(buf + pos + 4);
|
||||
|
||||
if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) {
|
||||
channels = *reinterpret_cast<const WORD*>(buf + pos + 10);
|
||||
sampleRate = *reinterpret_cast<const DWORD*>(buf + pos + 12);
|
||||
bitsPerSample = *reinterpret_cast<const WORD*>(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++;
|
||||
}
|
||||
|
||||
if (!pcm || bitsPerSample != 16) return data;
|
||||
|
||||
DWORD totalSamples = pcmSize / (channels * sizeof(int16_t));
|
||||
|
||||
int maxAmp = 1;
|
||||
for (DWORD i = 0; i < pcmSize / sizeof(int16_t); i++) {
|
||||
int s = abs(static_cast<int>(
|
||||
*reinterpret_cast<const int16_t*>(pcm + i * sizeof(int16_t))));
|
||||
if (s > maxAmp) maxAmp = s;
|
||||
}
|
||||
|
||||
int threshold = static_cast<int>(maxAmp * p_thresh);
|
||||
DWORD windowSamples = sampleRate / 50; // 20 ms windows
|
||||
DWORD debounce = sampleRate / 8; // 125 ms minimum gap
|
||||
DWORD lastPeak = 0xFFFFFFFF;
|
||||
|
||||
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(static_cast<int>(
|
||||
*reinterpret_cast<const int16_t*>(pcm + byteOff)));
|
||||
if (amp > winMax) winMax = amp;
|
||||
}
|
||||
|
||||
bool gapOk = (lastPeak == 0xFFFFFFFF) || (s - lastPeak >= debounce);
|
||||
if (winMax >= threshold && gapOk && data.times.size() < p_max) {
|
||||
data.times.push_back(static_cast<double>(s) / sampleRate);
|
||||
lastPeak = s;
|
||||
if (data.times.size() >= p_max) break;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
double sound_handler::Now()
|
||||
{
|
||||
static LARGE_INTEGER freq = [] {
|
||||
LARGE_INTEGER f;
|
||||
QueryPerformanceFrequency(&f);
|
||||
return f;
|
||||
}();
|
||||
LARGE_INTEGER t;
|
||||
QueryPerformanceCounter(&t);
|
||||
return static_cast<double>(t.QuadPart) / static_cast<double>(freq.QuadPart);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class sound_handler
|
||||
{
|
||||
public:
|
||||
struct PeakData {
|
||||
vector<double> times;
|
||||
};
|
||||
|
||||
private:
|
||||
HINSTANCE m_hInst;
|
||||
vector<int> m_wavIds;
|
||||
vector<PeakData> m_peaks;
|
||||
|
||||
int m_currentWave = -1;
|
||||
double m_soundStart = 0.0; // time of playsound
|
||||
int m_nextPeak = 0;
|
||||
double m_screamUntil = 0.0;
|
||||
|
||||
static constexpr double p_hold = 0.10;
|
||||
static constexpr double p_thresh = 0.65;
|
||||
static constexpr int p_max = 64;
|
||||
|
||||
public:
|
||||
sound_handler(HINSTANCE hInst, initializer_list<int> waveIds);
|
||||
void PlayRandom();
|
||||
void Update(double now);
|
||||
bool IsAwAwA(double now) const;
|
||||
|
||||
private:
|
||||
PeakData AnalyzeWavResource(int resId);
|
||||
static double Now();
|
||||
};
|
||||
Reference in New Issue
Block a user