#define BALLS_NUM 7 #define SPRINGS_NUM 3 #define STRETCH 500.0 #define GRAVITY 50.0 //not really gravity #define BALL_RADIUS 5 #define BASE_SIZE 10 CMass balls[BALLS_NUM]; CSpring springs[SPRINGS_NUM]; F64 collision_t; U0 DrawIt(CTask *task, CDC *dc) { I64 i, cx = task->pix_width >> 1, cy = task->pix_height >> 1; Bool sound_on = FALSE; dc->color = BLACK; GrPrint(dc, 0, 0, "Protect your base."); GrRect(dc, cx - BASE_SIZE, cy - BASE_SIZE, BASE_SIZE * 2, BASE_SIZE * 2); dc->color = CYAN; GrRect(dc, cx - BASE_SIZE + 2, cy - BASE_SIZE + 2, BASE_SIZE * 2 - 4, BASE_SIZE * 2 - 4); dc->color = YELLOW; GrLine(dc, balls[0].x, balls[0].y, mouse.pos.x - task->pix_left - task->scroll_x, mouse.pos.y - task->pix_top - task->scroll_y); for (i = 0; i < SPRINGS_NUM; i++) GrLine(dc, springs[i].end1->x, springs[i].end1->y, springs[i].end2->x, springs[i].end2->y); dc->color = LTCYAN; GrCircle(dc, balls[0].x, balls[0].y, BALL_RADIUS); GrFloodFill(dc, balls[0].x, balls[0].y, TRUE); dc->color = BLACK; GrCircle(dc, balls[0].x, balls[0].y, BALL_RADIUS); for (i = 1; i < BALLS_NUM; i++) { dc->color = LTPURPLE; GrCircle(dc, balls[i].x, balls[i].y, BALL_RADIUS); GrFloodFill(dc, balls[i].x, balls[i].y, TRUE); if ( cx - BASE_SIZE - BALL_RADIUS <= balls[i].x <= cx + BASE_SIZE + BALL_RADIUS && cy - BASE_SIZE - BALL_RADIUS <= balls[i].y <= cy + BASE_SIZE + BALL_RADIUS) sound_on = TRUE; dc->color = BLACK; GrCircle(dc, balls[i].x, balls[i].y, BALL_RADIUS); } if (sound_on) Sound(74); else Sound; } U0 MyDerivative(CMathODE *ode, F64 t, COrder2D3 *, COrder2D3 *) { I64 i, j; F64 d, dd; CD3 p, p2; CTask *task = ode->win_task; D3SubEqu(D3Equ(&p2, mouse.pos.x - task->pix_left - task->scroll_x, mouse.pos.y - task->pix_top - task->scroll_y, 0), &balls[0].state->x); D3AddEqu(&balls[0].DstateDt->DxDt, D3MulEqu(&p2, STRETCH)); D3Equ(&p2, task->pix_width >> 1, task->pix_height >> 1, 0); for (i = 1; i < BALLS_NUM; i++) { D3Sub(&p, &p2, &balls[i].state->x); if (d = D3Norm(&p)) { //Gravity would be /(d*d*d), but that's too exponential. D3MulEqu(&p, GRAVITY/d); D3AddEqu(&balls[i].DstateDt->DxDt, &p); } } for (i = 0; i < BALLS_NUM; i++) for (j = i + 1; j < BALLS_NUM; j++) { D3Sub(&p, &balls[j].state->x, &balls[i].state->x); dd = D3NormSqr(&p); if (dd <= (2 * BALL_RADIUS) * (2 * BALL_RADIUS)) { if (t-collision_t > 0.05) { Noise(50, 102, 105); collision_t = t; } d = Sqrt(dd) + 0.0001; dd = 10.0 * Sqr(Sqr((2 * BALL_RADIUS) * (2 * BALL_RADIUS) - dd)); D3MulEqu(&p, dd / d); D3AddEqu(&balls[j].DstateDt->DxDt, &p); D3SubEqu(&balls[i].DstateDt->DxDt, &p); } } d = balls[0].state->x; if (d - BALL_RADIUS < 0) balls[0].DstateDt->DxDt += Sqr(Sqr(Sqr(d - BALL_RADIUS))); if (d + BALL_RADIUS > task->pix_width) balls[0].DstateDt->DxDt -= Sqr(Sqr(Sqr((d + BALL_RADIUS) - task->pix_width))); d = balls[0].state->y; if (d - BALL_RADIUS < 0) balls[0].DstateDt->DyDt += Sqr(Sqr(Sqr(d - BALL_RADIUS))); if (d + BALL_RADIUS > task->pix_height) balls[0].DstateDt->DyDt -= Sqr(Sqr(Sqr((d + BALL_RADIUS) - task->pix_height))); } U0 Whap() { I64 i; CMathODE *ode = ODENew(0, 1e-2, ODEF_HAS_MASSES); SettingsPush; //See SettingsPush AutoComplete; WinBorder; WinMax; MenuPush( "File {" " Abort(,CH_SHIFT_ESC);" " Exit(,CH_ESC);" "}" ); ode->derive = &MyDerivative; ode->drag_v2 = 0.002; ode->drag_v3 = 0.00001; ode->acceleration_limit = 5e3; MemSet(balls, 0, BALLS_NUM * sizeof(CMass)); D3Equ(&balls[0].x, 100, 100, 0); for (i = 1; i < BALLS_NUM; i++) D3Equ(&balls[i].x, RandI16 % 500 + Fs->pix_width >> 1, RandI16 % 500 + Fs->pix_height >> 1, 0); balls[0].x = mouse.pos.x - Fs->pix_left - Fs->scroll_x; balls[0].y = mouse.pos.y - Fs->pix_top - Fs->scroll_y; for (i = 0; i < BALLS_NUM; i++) { balls[i].mass = 1.0; balls[i].drag_profile_factor = 1.0; QueueInsert(&balls[i], ode->last_mass); } balls[2].x = balls[1].x + 15; balls[2].y = balls[1].y; balls[3].x = balls[1].x; balls[3].y = balls[1].y + 15; MemSet(springs, 0, SPRINGS_NUM * sizeof(CSpring)); springs[0].end1 = &balls[1]; springs[0].end2 = &balls[2]; springs[0].rest_len = 15; springs[0].const = 10000; QueueInsert(&springs[0], ode->last_spring); springs[1].end1 = &balls[1]; springs[1].end2 = &balls[3]; springs[1].rest_len = 15; springs[1].const = 10000; QueueInsert(&springs[1], ode->last_spring); springs[2].end1 = &balls[2]; springs[2].end2 = &balls[3]; springs[2].rest_len = sqrt2 * 15; springs[2].const = 10000; QueueInsert(&springs[2], ode->last_spring); collision_t = 0; QueueInsert(ode, Fs->last_ode); DocCursor; DocClear; Fs->draw_it = &DrawIt; CharGet; SettingsPop; QueueRemove(ode); ODEDel(ode); MenuPop; } Whap;