boxing.cpp 24 KB

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