boxing.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. // raboot of atari 2600 Boxing
  2. // TODO: computer player
  3. // TODO: gore (destructable nose, eyes)
  4. // TODO: ropes (and some padding)
  5. // TODO: gamepad controls
  6. // TODO: touch controls
  7. // TODO: finish music
  8. // TODO: menu - 1/2 players, audio levels
  9. // TODO: menu - gore level (in case become olympic sport),
  10. // TODO: menu - tutorial (for touchscreen plebs mostyl)
  11. /*
  12. #canvas {
  13. position: absolute;
  14. top: 0px;
  15. left: 0px;
  16. margin: 0px;
  17. width: 100%;
  18. height: 100%;
  19. overflow: hidden;
  20. display: block;
  21. }
  22. */
  23. #include "common/sketchbook.hpp"
  24. #include "common/math.hpp"
  25. float quadratic_out(float x) { return 1.f - motion::quadratic_curve(1.f - x); };
  26. float stairwave(float x)
  27. {
  28. // TODO: must be a way to just parameterize sinus to do less work and be less smooth, instead of doing more work to make it jagged
  29. constexpr auto levels = 12.f;
  30. return int(std::clamp(common::sinus(x) *levels, -levels,levels)) /levels;
  31. }
  32. struct key_vector
  33. {
  34. scancode k;
  35. float2 v;
  36. };
  37. struct keymap
  38. {
  39. std::array<key_vector,4> legwork;
  40. std::array<scancode,3> punch;
  41. };
  42. keymap wasd
  43. {
  44. {
  45. key_vector{scancode::w, -float2::j()},
  46. key_vector{scancode::a, -float2::i()},
  47. key_vector{scancode::s, float2::j()},
  48. key_vector{scancode::d, float2::i()}
  49. },
  50. { scancode::tab, scancode::f, scancode::v }
  51. };
  52. keymap ijkl
  53. {
  54. {
  55. key_vector{scancode::i, -float2::j()},
  56. key_vector{scancode::j, -float2::i()},
  57. key_vector{scancode::k, float2::j()},
  58. key_vector{scancode::l, float2::i()}
  59. },
  60. { scancode::h, scancode::semicolon, scancode::n}
  61. };
  62. keymap arrow_keys
  63. {
  64. {
  65. key_vector{scancode::up, -float2::j()},
  66. key_vector{scancode::left, -float2::i()},
  67. key_vector{scancode::down, float2::j()},
  68. key_vector{scancode::right, float2::i()}
  69. },
  70. {}
  71. };
  72. std::vector control_options {wasd, ijkl, arrow_keys}; // FIXME: numpad, gamepad, touchscreen, mouse
  73. rect ring{};
  74. constexpr float2 half = float2::one(0.5);
  75. constexpr float2 starting_pos = float2::one(0.1);
  76. constexpr auto scale(rect ratio, rect world)
  77. {
  78. ratio.size *= world.size;
  79. ratio.position *= world.size;
  80. ratio.position += world.position;
  81. return ratio;
  82. }
  83. constexpr auto faded_wave(float fade_factor, auto&& f)
  84. {
  85. return [fade_factor, f = std::forward<decltype(f)>(f)](auto ratio) mutable
  86. {
  87. auto [freq, ampl] = f(ratio);
  88. float fade = std::min(ratio*fade_factor, (1-ratio)*fade_factor);
  89. return way(0.f,common::sinus(ratio * freq) * ampl, std::min(fade, 1.f));
  90. };
  91. };
  92. struct blood_drop
  93. {
  94. rgb cola;
  95. movement<float2, motion::cubic_curve> veloc;
  96. float2 position;
  97. void update(frame& frame, auto delta)
  98. {
  99. veloc.advance(delta);
  100. auto oldpos = position;
  101. position += veloc.value();
  102. frame.begin_sketch()
  103. .line(ring.size*oldpos + ring.position, ring.size*position + ring.position)
  104. .line_width(0.01 * ring.size.x())
  105. .line_cap(sketch::cap::round)
  106. .outline(cola)
  107. ;
  108. }
  109. bool done()
  110. {
  111. return veloc.done();
  112. }
  113. };
  114. std::vector<blood_drop> blood;
  115. struct boxa
  116. {
  117. rgb cola;
  118. float2 position = {};
  119. float2 velocity = {};
  120. float speed = 0.5;
  121. movement<float, common::sinus> idle = {500ms, 0.f, 0.005f}; // FIXME: random delay per boxa to desync them
  122. movement<float2, quadratic_out> shovement = {0s, float2::zero(), float2::zero()};
  123. melody<movement<float2>, movement<float2>> squeezement = {};
  124. melody<movement<float2>, movement<float2>> glove_squeezement = {};
  125. boxa* target;
  126. std::optional<keymap> controls{};
  127. float punch = 0.0f;
  128. bool hit = false;
  129. unsigned closest_glove = 0;
  130. unsigned hit_glove = 0;
  131. unsigned damage = 0;
  132. auto direction()
  133. {
  134. const auto distance = target->position - position;
  135. auto clamped_distance = distance;
  136. if(clamped_distance.y() > 0.2)
  137. clamped_distance.y() = 0.2;
  138. else if(clamped_distance.y() < -0.2)
  139. clamped_distance.y() = -0.2;
  140. if(abs(clamped_distance.x()) > 0.18f)
  141. clamped_distance *= float2::i();
  142. else
  143. clamped_distance = support::average(clamped_distance, clamped_distance * float2::i());
  144. return common::normalize(clamped_distance);
  145. }
  146. rect body()
  147. {
  148. return {float2::one(0.1), position, half};
  149. };
  150. auto elbows()
  151. {
  152. const auto normal = common::rotate(this->direction(), common::protractor<>::tau(1/4.f));
  153. auto body = this->body();
  154. std::array<float2,2> elbows{};
  155. const auto spread = 1.7f;
  156. auto arm = 1;
  157. for(auto&& elbow : elbows)
  158. {
  159. elbow = body.position;
  160. elbow += body.size.x() * normal * spread * arm;
  161. elbow += - this->direction() * body.size.x()/2 * 1.3;
  162. if(elbow == elbows[closest_glove])
  163. elbow -= body.size.x() * normal * (0.7 * punch) * arm;
  164. arm = -arm;
  165. }
  166. elbows[closest_glove] += this->direction() * punch * 0.1;
  167. return elbows;
  168. };
  169. auto gloves()
  170. {
  171. const auto normal = common::rotate(this->direction(), common::protractor<>::tau(1/4.f));
  172. auto body = this->body();
  173. std::array<rect,2> gloves{};
  174. for(auto&& glove : gloves)
  175. {
  176. glove = body;
  177. glove.size *= 0.9;
  178. glove.position += body.size.x() * normal;
  179. glove.position += this->direction() * body.size.x()/2;
  180. }
  181. gloves[1].position -= body.size.x() * 2 * normal;
  182. gloves[closest_glove].position += this->direction() * punch * 0.2;
  183. return gloves;
  184. };
  185. void update(frame& f, auto delta_time)
  186. {
  187. { // draw
  188. const auto direction = this->direction();
  189. const auto normal = common::rotate(this->direction(), common::protractor<>::tau(1/4.f));
  190. const auto facingdom = float2x2{direction, normal};
  191. auto body = this->body();
  192. {
  193. float2 squeezement = float2::one();
  194. this->squeezement.move(squeezement, delta_time);
  195. const auto squeezed_body_size = body.size * squeezement;
  196. body.position += direction * (squeezed_body_size.x() - body.size.x());
  197. body.size = squeezed_body_size;
  198. }
  199. auto elbows = this->elbows();
  200. auto gloves = this->gloves();
  201. {
  202. float idle_sway = 0.f;
  203. auto idle_normal = normal;
  204. motion::loop(idle_sway, idle, delta_time);
  205. for(auto&& glove : gloves)
  206. {
  207. glove.position += idle_normal * idle_sway;
  208. idle_normal = -idle_normal;
  209. }
  210. body.position += direction * idle_sway;
  211. }
  212. {
  213. gloves[closest_glove].size += gloves[closest_glove].size * float2{0.1f, -0.1f} * punch;
  214. }
  215. {
  216. float2 squeezement = float2::one();
  217. this->glove_squeezement.move(squeezement, delta_time);
  218. const auto squeezed_glove_size = gloves[hit_glove].size * squeezement;
  219. gloves[hit_glove].position += direction * (squeezed_glove_size.x() - gloves[hit_glove].size.x());
  220. gloves[hit_glove].size = squeezed_glove_size;
  221. }
  222. auto scaled_body = scale(body,ring);
  223. for(auto&& [elbow, glove] : range{vbegin(elbows, gloves), vend(elbows,gloves)})
  224. {
  225. auto scaled_elbow = elbow * ring.size + ring.position;
  226. auto scaled_glove = scale(glove,ring);
  227. f.begin_sketch()
  228. .move(scaled_body.position)
  229. .bezier(scaled_elbow, scaled_glove.position)
  230. .line_width((0.04f) * ring.size.x()).outline(0x333333_rgb)
  231. ;
  232. }
  233. f.begin_sketch()
  234. .ellipse(scaled_body.position, facingdom * geom::column(scaled_body.size/2))
  235. .fill(rgb::white(0.5))
  236. ;
  237. auto thumb_offsetdom = geom::vector(
  238. -direction * 0.1f,
  239. -normal * 0.4f
  240. );
  241. auto glove_sketch = f.begin_sketch();
  242. for(auto&& glove : gloves)
  243. {
  244. auto scaled_glove = scale(glove,ring);
  245. glove_sketch
  246. .ellipse(scaled_glove.position, facingdom * geom::column(scaled_glove.size/2))
  247. .ellipse(scaled_glove.position + thumb_offsetdom(scaled_glove.size), facingdom * geom::column(scaled_glove.size/2/2))
  248. ;
  249. thumb_offsetdom[1] = -thumb_offsetdom[1];
  250. }
  251. glove_sketch.fill(cola);
  252. }
  253. { // move
  254. const range2f bounds {position - 0.05, position + 0.05};
  255. constexpr auto world_bounds = range2f{float2::zero(), float2::one()};
  256. const auto lower_overshoot = world_bounds.lower() - bounds.lower();
  257. const auto upper_overshoot = bounds.upper() - world_bounds.upper();
  258. constexpr float springback_factor = 20;
  259. velocity += springback_factor * max(lower_overshoot, float2::zero());
  260. velocity -= springback_factor * max(upper_overshoot, float2::zero());
  261. position += velocity * speed * delta_time.count();
  262. if(not shovement.done())
  263. {
  264. position += shovement.value() * (speed /2) * delta_time.count();
  265. shovement.advance(delta_time);
  266. }
  267. auto diameter = 0.2f;
  268. const auto distance = target->position - position;
  269. if(distance.quadrance() < diameter * diameter)
  270. {
  271. auto direction = common::normalize(distance);
  272. auto force_field = direction * diameter;
  273. auto correction = distance - force_field;
  274. position += correction;
  275. }
  276. if(punch > 0 || hit)
  277. {
  278. if(not hit)
  279. {
  280. punch += 10 * delta_time.count();;
  281. if(punch >= 1)
  282. {
  283. hit = true;
  284. punch = 1;
  285. }
  286. }
  287. else
  288. {
  289. if(punch > 0)
  290. {
  291. punch -= 10 * delta_time.count();
  292. }
  293. else
  294. {
  295. punch = 0;
  296. hit = false;
  297. }
  298. }
  299. }
  300. }
  301. }
  302. std::optional<rect> punching_glove()
  303. {
  304. if(punch > 0 && not hit)
  305. return gloves()[closest_glove];
  306. else
  307. return std::nullopt;
  308. }
  309. };
  310. std::array boxas
  311. {
  312. boxa{rgb::red(), starting_pos},
  313. boxa{rgb::blue(), float2::one() - starting_pos}
  314. };
  315. constexpr rect fit_square(float2 size)
  316. {
  317. float2 fit_size = float2::one(*min_element(size));
  318. return {fit_size, (size - fit_size) / 2}; // hmmm, does this sub/2 thing have a name? midpointish but not quite
  319. }
  320. void start(Program& program)
  321. {
  322. // program.frametime = framerate<60>::frametime;
  323. program.resizable = true;
  324. program.size = float2::one(400.f);
  325. ring = fit_square(program.size);
  326. if(program.argc > 2)
  327. {
  328. using support::ston;
  329. using seed_t = decltype(tiny_rand());
  330. tiny_rand.seed({ ston<seed_t>(program.argv[1]), ston<seed_t>(program.argv[2]) });
  331. }
  332. std::cout << "seed: " << std::hex << std::showbase << tiny_rand << '\n';
  333. program.key_up = [&](scancode code, keycode)
  334. {
  335. switch(code)
  336. {
  337. case scancode::leftbracket:
  338. case scancode::c:
  339. if(pressed(scancode::rctrl) || pressed(scancode::lctrl))
  340. case scancode::escape:
  341. program.end();
  342. break;
  343. default: break;
  344. }
  345. };
  346. program.key_down = [&](scancode code, keycode)
  347. {
  348. for(auto&& boxa : boxas)
  349. if(boxa.controls)
  350. if(support::find(boxa.controls->punch, code) != boxa.controls->punch.end())
  351. if(boxa.punch == 0)
  352. {
  353. auto hiss = [rand = make_trand_float({0,0.1})](auto ratio) mutable
  354. { return std::pair{rand(), ratio/5}; };
  355. program.request_wave({faded_wave(20, std::move(hiss)), 125ms});
  356. // program.request_wave({faded_wave(20, std::move(hiss)), 50ms});
  357. boxa.punch = 0.001;
  358. boxa.closest_glove = boxa.position.y() > boxa.target->position.y();
  359. boxa.closest_glove ^= boxa.position.x() > boxa.target->position.x();
  360. }
  361. };
  362. program.mouse_down = [&](float2, auto)
  363. {
  364. };
  365. program.size_changed = [&](float2 size)
  366. {
  367. std::cout << size << '\n';
  368. ring = fit_square(size);
  369. std::cout << ring << '\n';
  370. };
  371. program.draw_loop = [&](auto frame, auto delta_time)
  372. {
  373. frame.begin_sketch()
  374. .rectangle(rect{frame.size})
  375. .fill(rgb::white(0.4f))
  376. ;
  377. frame.begin_sketch()
  378. .rectangle(ring)
  379. .fill(rgb(0.3f, 0.4f, 0.0f))
  380. ;
  381. for(auto&& boxa : boxas)
  382. {
  383. boxa.velocity = float2::zero();
  384. if(boxa.controls)
  385. {
  386. for(auto&& kv : boxa.controls->legwork)
  387. if(pressed(kv.k))
  388. boxa.velocity += kv.v;
  389. if(boxa.velocity != float2::zero())
  390. boxa.velocity = common::normalize(boxa.velocity); // here length can only be root(2) or 1 so a bit of an overkill
  391. }
  392. else
  393. {
  394. auto selected = support::find_if(control_options, [](auto option)
  395. {
  396. return support::any_of(option.legwork, [](auto kv) { return pressed(kv.k); });
  397. });
  398. if(selected != control_options.end())
  399. {
  400. boxa.controls = *selected;
  401. control_options.erase(selected);
  402. }
  403. }
  404. }
  405. boxas[0].target = &boxas[1];
  406. boxas[1].target = &boxas[0];
  407. for(auto&& b : boxas) b.update(frame, delta_time);
  408. for(auto&& b : boxas)
  409. {
  410. auto glove = b.punching_glove();
  411. bool collided_target = false;
  412. bool collided_a_glove = false;
  413. bool collided_punching_glove = false;
  414. if(glove)
  415. {
  416. collided_target = (glove->position - b.target->position).quadrance() <= 0.095*0.095;
  417. if(not collided_target)
  418. {
  419. auto other_gloves = b.target->gloves();
  420. auto collided_glove = support::find_if(other_gloves, [pos = glove->position](auto x) {
  421. return (pos - x.position).quadrance() <= x.size.quadrance()/2;
  422. });
  423. collided_a_glove = collided_glove != other_gloves.end();
  424. auto other_punching_glove = b.target->punching_glove();
  425. if(collided_a_glove && other_punching_glove)
  426. {
  427. collided_punching_glove = (*collided_glove) == (*other_punching_glove);
  428. }
  429. if(collided_a_glove)
  430. {
  431. b.target->hit_glove = collided_glove - other_gloves.begin();
  432. }
  433. }
  434. }
  435. if(collided_target)
  436. {
  437. b.target->shovement.total = 250ms;
  438. b.target->shovement.start = (b.direction() + (b.position.y() < b.target->position.y() ? -float2::j(2) : float2::j(2))) * 5;
  439. b.target->shovement.reset();
  440. {
  441. constexpr auto squeezementat = float2{0.5f, 1.5f};
  442. b.target->squeezement = melody{
  443. movement<float2>{125ms, float2::one(), squeezementat},
  444. movement<float2>{125ms, squeezementat, float2::one()}
  445. };
  446. }
  447. ++b.target->damage; // FIXME: more damage for combos
  448. auto boink = [](auto ratio)
  449. { return std::pair{way(40.f, 20.f, ratio), 1}; };
  450. program.request_wave({faded_wave(20, std::move(boink)), 125ms});
  451. const auto splatter_direction = (b.target->position - b.position).signum();
  452. for(int i = 10; i --> 0;) blood.push_back({
  453. wayback(0x55cccc_rgb,0x660000_rgb, std::min(b.target->damage/50.f, 1.f)),
  454. {250ms, trand_float2() / 50 * splatter_direction, float2::zero()},
  455. b.target->position
  456. });
  457. }
  458. if(collided_a_glove)
  459. {
  460. constexpr auto squeezementat = float2{0.7f, 1.3f};
  461. b.target->glove_squeezement = melody{
  462. movement<float2>{60ms, float2::one(), squeezementat},
  463. movement<float2>{60ms, squeezementat, float2::one()}
  464. };
  465. auto bunk = [](auto ratio)
  466. { return std::pair{way(25.f, 0.f, ratio), .3f}; };
  467. program.request_wave({faded_wave(20, std::move(bunk)), 125ms});
  468. }
  469. if(collided_target || collided_a_glove)
  470. b.hit = true;
  471. if(collided_punching_glove)
  472. {
  473. b.target->hit = true;
  474. break;
  475. }
  476. }
  477. for(auto&& b : blood) b.update(frame, delta_time);
  478. blood.erase(std::remove_if(blood.begin(),blood.end(), [](auto& b) {return b.done();}), blood.end());
  479. };
  480. program.request_wave({
  481. [
  482. time = 0.f, freq = 0.f,
  483. musac = motion::melody
  484. {
  485. movement<float>{50ms, 0.f,120.f},
  486. movement<float>{50ms, 120.f,0.f},
  487. movement<float>{100ms, 0.f,0.f},
  488. movement<float>{50ms, 0.f,150.f},
  489. movement<float>{50ms, 150.f,0.f},
  490. movement<float>{100ms, 0.f,0.f},
  491. movement<float>{50ms, 0.f,100.f},
  492. movement<float>{50ms, 100.f,0.f},
  493. movement<float>{100ms, 0.f,0.f},
  494. movement<float>{50ms, 0.f,200.f},
  495. movement<float>{50ms, 200.f,0.f},
  496. movement<float>{100ms, 0.f,0.f},
  497. movement<float>{50ms, 0.f,220.f},
  498. movement<float>{50ms, 220.f,0.f},
  499. movement<float>{100ms, 0.f,0.f},
  500. movement<float>{50ms, 0.f,250.f},
  501. movement<float>{50ms, 250.f,0.f},
  502. movement<float>{100ms, 0.f,0.f},
  503. movement<float>{50ms, 0.f,200.f},
  504. movement<float>{50ms, 200.f,0.f},
  505. movement<float>{100ms, 0.f,0.f},
  506. movement<float>{50ms, 0.f,100.f},
  507. movement<float>{50ms, 100.f,0.f},
  508. movement<float>{100ms, 0.f,0.f},
  509. movement<float>{50ms, 0.f,120.f},
  510. movement<float>{50ms, 120.f,0.f},
  511. movement<float>{100ms, 0.f,0.f},
  512. movement<float>{50ms, 0.f,0.f},
  513. movement<float>{50ms, 0.f,0.f},
  514. movement<float>{100ms, 0.f,0.f},
  515. movement<float>{50ms, 0.f,100.f},
  516. movement<float>{50ms, 100.f,0.f},
  517. movement<float>{100ms, 0.f,0.f},
  518. movement<float>{50ms, 0.f,200.f},
  519. movement<float>{50ms, 200.f,0.f},
  520. movement<float>{100ms, 0.f,0.f},
  521. movement<float>{50ms, 0.f,0.f},
  522. movement<float>{50ms, 0.f,0.f},
  523. movement<float>{100ms, 0.f,0.f},
  524. movement<float>{50ms, 0.f,250.f},
  525. movement<float>{50ms, 250.f,0.f},
  526. movement<float>{100ms, 0.f,0.f},
  527. movement<float>{50ms, 0.f,200.f},
  528. movement<float>{50ms, 200.f,0.f},
  529. movement<float>{100ms, 0.f,0.f},
  530. movement<float>{50ms, 0.f,0.f},
  531. movement<float>{50ms, 0.f,0.f},
  532. movement<float>{100ms, 0.f,0.f},
  533. }
  534. ](auto tick) mutable
  535. {
  536. motion::loop(freq, musac, Program::duration(tick));
  537. time += tick * freq;
  538. time = support::wrap(time,1.f);
  539. return stairwave(time) * 0.2f;
  540. },
  541. 0ms});
  542. program.request_wave({
  543. [
  544. time = 0.f, freq = 0.f,
  545. musac = motion::melody
  546. {
  547. movement<float, quadratic_out>{10ms, 0.f, 350.f},
  548. movement<float, quadratic_out>{90ms, 350.f,420.f},
  549. movement<float, motion::quadratic_curve>{200ms, 420.f,350.f},
  550. movement<float, quadratic_out>{100ms, 350.f,350.f},
  551. movement<float, quadratic_out>{100ms, 350.f,750.f},
  552. movement<float, motion::quadratic_curve>{200ms, 750.f,350.f},
  553. movement<float, quadratic_out>{100ms, 350.f,350.f},
  554. movement<float, motion::quadratic_curve>{200ms, 350.f,400.f},
  555. movement<float, quadratic_out>{100ms, 400.f,350.f},
  556. movement<float, quadratic_out>{500ms, 350.f,350.f},
  557. movement<float, quadratic_out>{100ms, 350.f,420.f},
  558. movement<float, motion::quadratic_curve>{200ms, 420.f,350.f},
  559. movement<float, quadratic_out>{100ms, 350.f,350.f},
  560. movement<float, quadratic_out>{100ms, 350.f,750.f},
  561. movement<float, motion::quadratic_curve>{200ms, 750.f,350.f},
  562. movement<float, quadratic_out>{90ms, 350.f,350.f},
  563. movement<float, quadratic_out>{10ms, 350.f,0.f},
  564. movement<float>{800ms, 0.f,0.f},
  565. movement<float>{800ms, 0.f,0.f},
  566. movement<float>{800ms, 0.f,0.f},
  567. movement<float>{800ms, 0.f,0.f},
  568. movement<float>{800ms, 0.f,0.f},
  569. }
  570. ](auto tick) mutable
  571. {
  572. motion::loop(freq, musac, Program::duration(tick));
  573. time += tick * (freq/3);
  574. time = support::wrap(time,1.f);
  575. return stairwave(time) * 0.3f;
  576. },
  577. 0ms});
  578. program.request_wave({
  579. [
  580. time = 0.f, freq = 0.f,
  581. musac = motion::melody
  582. {
  583. movement<float>{800ms, 0.f,0.f},
  584. movement<float>{800ms, 0.f,0.f},
  585. movement<float>{800ms, 0.f,0.f},
  586. movement<float>{800ms, 0.f,0.f},
  587. movement<float>{800ms, 0.f,0.f},
  588. movement<float>{800ms, 0.f,0.f},
  589. movement<float>{800ms, 0.f,0.f},
  590. movement<float>{800ms, 0.f,0.f},
  591. movement<float>{800ms, 0.f,0.f},
  592. movement<float>{800ms, 0.f,0.f},
  593. movement<float>{800ms, 0.f,0.f},
  594. movement<float>{800ms, 0.f,0.f},
  595. movement<float, quadratic_out>{400ms, 0.f, 1350.f},
  596. movement<float, quadratic_out>{400ms, 1350.f,1000.f},
  597. movement<float, quadratic_out>{400ms, 1000.f, 1200.f},
  598. movement<float, quadratic_out>{400ms, 1200.f, 350.f},
  599. movement<float, quadratic_out>{400ms, 350.f,1000.f},
  600. movement<float, quadratic_out>{400ms, 1000.f, 0.f},
  601. movement<float>{800ms, 0.f,0.f},
  602. }
  603. ](auto tick) mutable
  604. {
  605. motion::loop(freq, musac, Program::duration(tick));
  606. time += tick * (freq/3);
  607. time = support::wrap(time,1.f);
  608. return stairwave(time) * 0.1f;
  609. },
  610. 0ms});
  611. }