Files
bounce_cpp/bounce_cpp/main.cpp
T

367 lines
9.7 KiB
C++

/*
Anytime I edit any C++ Code, in specifically Win32 code i get acute seizures cause of the naming
conventions. I hope that whoever came up with these fuckass names gets fucked by a femboy. idk.
Anyways this is under the AGPL3.0 License so do whatever you want under that license terms.
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.
This is not malware or a virus, its purely for fun and entertainment purpouses.
rattatwinko - 04/05/26
*/
#include <windows.h>
#include <windowsx.h>
#include <gdiplus.h>
#include <objidl.h>
#include <math.h>
#include <cstring>
#pragma comment(lib, "gdiplus.lib")
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
// globals
static ULONG_PTR g_gdiplusToken = 0;
static Image* g_image = nullptr;
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;
// resize status
static bool g_resizing = false;
static bool g_dragging = false;
static int g_dragOffsetX = 0;
static int g_dragOffsetY = 0;
static POINT g_lastMouse = {};
static double g_lastMouseTime = 0.0;
// helper definitions
static double Now() {
LARGE_INTEGER t;
QueryPerformanceCounter(&t);
return static_cast<double>(t.QuadPart) / static_cast<double>(g_freq.QuadPart);
}
static Image* LoadImageFromResource(HINSTANCE hInst, int resId) {
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resId), RT_RCDATA);
if (!hRes) return nullptr;
DWORD size = SizeofResource(hInst, hRes);
if (!size) return nullptr;
HGLOBAL hData = LoadResource(hInst, hRes);
if (!hData) return nullptr;
void* pData = LockResource(hData);
if (!pData) return nullptr;
HGLOBAL hBuf = GlobalAlloc(GMEM_MOVEABLE, size);
if (!hBuf) return nullptr;
void* pBuf = GlobalLock(hBuf);
if (!pBuf) { GlobalFree(hBuf); return nullptr; }
memcpy(pBuf, pData, size);
GlobalUnlock(hBuf);
IStream* stream = nullptr;
if (FAILED(CreateStreamOnHGlobal(hBuf, TRUE, &stream)) || !stream) {
GlobalFree(hBuf);
return nullptr;
}
Image* img = Image::FromStream(stream);
stream->Release();
if (!img || img->GetLastStatus() != Ok) {
delete img;
return nullptr;
}
return img;
}
// 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) {
// init the window
case WM_CREATE: {
QueryPerformanceFrequency(&g_freq);
GdiplusStartupInput si;
GdiplusStartup(&g_gdiplusToken, &si, nullptr);
HINSTANCE hInst = reinterpret_cast<CREATESTRUCT*>(lParam)->hInstance;
g_image = LoadImageFromResource(hInst, IMAGE_RESOURCE);
if (g_image) {
g_winW = static_cast<int>(g_image->GetWidth());
g_winH = static_cast<int>(g_image->GetHeight());
SetWindowPos(hwnd, nullptr, 0, 0, g_winW, g_winH,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
g_lastTime = Now();
g_lastMouseTime = g_lastTime;
SetTimer(hwnd, 1, TIMER_MS, nullptr);
break;
}
// drag start
case WM_LBUTTONDOWN: {
g_dragging = true;
g_dragOffsetX = GET_X_LPARAM(lParam);
g_dragOffsetY = GET_Y_LPARAM(lParam);
g_vx = g_vy = 0.0;
GetCursorPos(&g_lastMouse);
g_lastMouseTime = Now();
SetCapture(hwnd);
break;
}
// drag move
case WM_MOUSEMOVE: {
if (!g_dragging) break;
POINT p;
GetCursorPos(&p);
const double now = Now();
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;
}
g_lastMouse = p;
g_lastMouseTime = now;
g_x = p.x - g_dragOffsetX;
g_y = p.y - g_dragOffsetY;
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
// drag end
case WM_LBUTTONUP:
g_dragging = false;
ReleaseCapture();
break;
// while dragging nutz accross fohead disable OS from doing shit
case WM_ENTERSIZEMOVE:
g_resizing = true;
break;
case WM_EXITSIZEMOVE: {
g_resizing = false;
g_vx = g_vy = 0.0;
// 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_lastTime = Now(); // after everything is clean then redraw
break;
}
// weird asf logic
case WM_NCHITTEST: {
POINT p = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
RECT r;
GetWindowRect(hwnd, &r);
const bool left = p.x < r.left + RESIZE_BORDER;
const bool right = p.x >= r.right - RESIZE_BORDER;
const bool top = p.y < r.top + RESIZE_BORDER;
const bool bottom = p.y >= r.bottom - RESIZE_BORDER;
if (top && left) return HTTOPLEFT;
if (top && right) return HTTOPRIGHT;
if (bottom && left) return HTBOTTOMLEFT;
if (bottom && right) return HTBOTTOMRIGHT;
if (left) return HTLEFT;
if (right) return HTRIGHT;
if (top) return HTTOP;
if (bottom) return HTBOTTOM;
return HTCLIENT;
}
// sync dimensions after resizing
case WM_SIZE:
g_winW = LOWORD(lParam);
g_winH = HIWORD(lParam);
InvalidateRect(hwnd, nullptr, FALSE);
break;
// cursor, bunch of this dont work
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT) {
SetCursor(LoadCursor(nullptr, g_dragging ? IDC_SIZEALL : IDC_HAND));
return TRUE;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
// phyisks tick
case WM_TIMER: {
const double now = Now();
const double dt = now - g_lastTime;
g_lastTime = now;
if (!g_dragging && !g_resizing) {
StepPhysics(dt);
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
}
// pablo picasso
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// db to avoid me floppin on the floor like a fish (cause of flashing lights)
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBmp = CreateCompatibleBitmap(hdc, g_winW, g_winH);
HGDIOBJ old = SelectObject(memDC, memBmp);
Graphics g(memDC);
g.SetInterpolationMode(InterpolationModeHighQualityBicubic);
if (g_image)
g.DrawImage(g_image, 0, 0, g_winW, g_winH);
BitBlt(hdc, 0, 0, g_winW, g_winH, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, old);
DeleteObject(memBmp);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
break;
}
// escape destroy
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
else if (wParam == VK_SPACE)
DestroyWindow(hwnd);
break;
// consuela
case WM_DESTROY:
KillTimer(hwnd, 1);
delete g_image;
g_image = nullptr;
GdiplusShutdown(g_gdiplusToken);
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
// right around the entry point (reference to asshair guy)
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) {
WNDCLASSW wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Hyrax";
wc.hCursor = LoadCursor(nullptr, IDC_HAND);
RegisterClassW(&wc);
HWND hwnd = CreateWindowExW(
WS_EX_TOPMOST,
L"Hyrax", L"",
WS_POPUP,
static_cast<int>(g_x), static_cast<int>(g_y),
g_winW, g_winH,
nullptr, nullptr, hInstance, nullptr
);
if (!hwnd)
return 1;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessageW(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return static_cast<int>(msg.wParam);
}