123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323 |
- #include "common/sketchbook.hpp"
- #include "common/math.hpp"
- float quadratic_out(float x) { return 1.f - motion::quadratic_curve(1.f - x); };
- float stairwave(float x)
- {
-
- constexpr auto levels = 12.f;
- return int(std::clamp(common::sinus(x) *levels, -levels,levels)) /levels;
- }
- struct key_vector
- {
- scancode k;
- float2 v;
- };
- struct keymap
- {
- std::array<key_vector,4> legwork;
- std::array<scancode,3> punch;
- };
- keymap wasd
- {
- {
- key_vector{scancode::w, -float2::j()},
- key_vector{scancode::a, -float2::i()},
- key_vector{scancode::s, float2::j()},
- key_vector{scancode::d, float2::i()}
- },
- { scancode::tab, scancode::f, scancode::v }
- };
- keymap ijkl
- {
- {
- key_vector{scancode::i, -float2::j()},
- key_vector{scancode::j, -float2::i()},
- key_vector{scancode::k, float2::j()},
- key_vector{scancode::l, float2::i()}
- },
- { scancode::h, scancode::semicolon, scancode::n}
- };
- keymap arrow_keys
- {
- {
- key_vector{scancode::up, -float2::j()},
- key_vector{scancode::left, -float2::i()},
- key_vector{scancode::down, float2::j()},
- key_vector{scancode::right, float2::i()}
- },
- { scancode::rctrl, scancode::rshift, scancode::enter }
- };
- std::vector control_options {wasd, ijkl, arrow_keys};
- rect ring{};
- constexpr float2 half = float2::one(0.5);
- constexpr float2 starting_pos = float2::one(0.1);
- constexpr auto scale(rect ratio, rect world)
- {
- ratio.size *= world.size;
- ratio.position *= world.size;
- ratio.position += world.position;
- return ratio;
- }
- constexpr auto faded_wave(float fade_factor, auto&& f)
- {
- return [fade_factor, f = std::forward<decltype(f)>(f)](auto ratio) mutable
- {
- auto [freq, ampl] = f(ratio);
- float fade = std::min(ratio*fade_factor, (1-ratio)*fade_factor);
- return way(0.f,common::sinus(ratio * freq) * ampl, std::min(fade, 1.f));
- };
- };
- struct blood_drop
- {
- rgb cola;
- movement<float2, motion::cubic_curve> veloc;
- float2 position;
- float size = 0.01f;
- void update(frame& frame, auto delta)
- {
- veloc.advance(delta);
- auto oldpos = position;
- position += veloc.value();
- frame.begin_sketch()
- .line(ring.size*oldpos + ring.position, ring.size*position + ring.position)
- .line_width(size * ring.size.x())
- .line_cap(sketch::cap::round)
- .outline(cola)
- ;
- }
- bool done()
- {
- return veloc.done();
- }
- };
- std::vector<blood_drop> blood;
- struct complayer
- {
- using timer = movement<support::tuple_void_t>;
-
- std::array<Program::duration, 3> punch_intervals = {600ms, 300ms, 100ms};
- Program::duration legwork_interval = 250ms;
- Program::duration stun_interval = 250ms;
-
- timer legwork_timer = {legwork_interval};
- timer punch_timer = {punch_intervals[0]};
- timer stun_timer = {stun_interval};
- float2 legwork_flank = {};
- int sidestep = 0;
- };
- struct vein
- {
- std::vector<float2> path = {};
- float2 velocity = {};
- float width = 0.f;
- };
- std::vector<vein> bloodshot()
- {
- constexpr float radius = 0.95f;
- std::vector<vein> veins;
- const auto bursts = trand_int({1, 4});
- std::generate_n(std::back_inserter(veins), bursts, [radius]()
- {
- const auto start_point = common::rotate(float2::j(radius), common::protractor<>::tau(trand_float()));
- return vein
- {
- .path = {start_point},
- .velocity = -start_point/10,
- .width = 1/30.f
- };
- });
- auto current_veins = range{support::offset_expander(veins), support::offset_expander(veins, veins.end())};
- for(int i = 3; i --> 0;)
- {
- for(vein& vein : current_veins)
- {
- for(int i = 4; i --> 0;)
- {
- const auto velocity = common::rotate(vein.velocity,
- common::protractor<>::tau(
- support::wrap(trand_float({-1/8.f, 1/8.f}), 1.f)
- )
- );
- vein.path.push_back(vein.path.back() + velocity);
- if(vein.path.back().quadrance() > radius*radius)
- {
- auto tangent = common::rotate(-vein.path.back(),
- common::protractor<>::tau(1/4.f));
- vein.path.back() -= velocity;
- vein.velocity = common::reflect(velocity, tangent);
- vein.path.back() += vein.velocity;
- }
- }
- }
- auto new_veins_end = current_veins.end();
- for(vein& v : current_veins)
- {
- auto split = 2;
- const auto step = 1/2.f / split;
- auto angle_range = range{-1/4.f, -1/4.f + step};
- new_veins_end = std::generate_n(new_veins_end, split,
- [
- root = v.path.back(),
- velocity = v.velocity,
- width = v.width * 0.6f,
- &angle_range, &step
- ]()
- {
- auto v = vein
- {
- .path = {root},
- .velocity = common::rotate(velocity,
- common::protractor<>::tau(
- support::wrap(trand_float(angle_range), 1.f)
- )
- ),
- .width = width
- };
- angle_range += step;
- return v;
- });
- }
- current_veins.lower() = current_veins.end();
- current_veins.upper() = new_veins_end;
- }
- return veins;
- }
- struct eye_t
- {
- constexpr static auto size = float2::one(0.035f);
- float2 position = {};
- float2 velocity = {};
- float2 drift = {};
- unsigned pop_damage = 100;
- melody<
- movement<float, quadratic_out>,
- movement<float, motion::quadratic_curve>
- >
- drifting = { {1s, 0, 1}, {1s, 1, 0}};
- melody
- <
- movement<float>,
- movement<float, quadratic_out>,
- movement<float>,
- movement<float, motion::quadratic_curve>
- >
- blink
- {
- {1s, 0.f, 0.f},
- {50ms, 0.f, 1.f},
- {100ms, 1.f, 1.f},
- {50ms, 1.f, 0.f},
- };
- float drift_ratio = 0.f;
- melody
- <
- movement<float>,
- movement<float, [](float x) -> float { return quadratic_out(x) * (1.f + common::sinus(x * 4.f))/2.f; } >
- >
- twitch
- {
- {1s, 0.f, 0.f},
- {500ms, 0.f, 1.f},
- };
- float lid_value = 0.f;
- std::vector<vein> veins = bloodshot();
- movement<float, [](float x){ return common::sinus(x/2); }> fall = {500ms, 1.f, 2.f};
- movement<support::tuple_void_t> bleed = {10ms};
- support::array<rect, 20> black = support::make_array<20>([](auto)
- {
- return rect
- {
- size/4 * trand_float({1.f, 2.f}),
- common::protractor<>::tau(
- support::wrap(trand_float({-1/4.f, 1/4.f}), 1.f)
- ),
- float2::one()/2
- };
- });
- };
- struct boxa
- {
- rgb cola;
- float2 position = {};
- float2 velocity = {};
- float speed = 0.5;
- movement<float, common::sinus> idle = {500ms, 0.f, 0.005f};
- movement<float2, quadratic_out> shovement = {0s, float2::zero(), float2::zero()};
- melody<movement<float2>, movement<float2>> squeezement = {};
- melody<movement<float2>, movement<float2>> glove_squeezement = {};
- boxa* target;
- std::optional<keymap> controls{};
- std::optional<complayer> brainz;
- std::array<eye_t,2> eyes = {{ {position + float2::j() * eye_t::size * 1.2f}, {position - float2::j() * eye_t::size * 1.2f} }};
- float punch = 0.0f;
- bool hit = false;
- unsigned punch_glove = 0;
- unsigned hit_glove = 0;
- unsigned damage = 0;
- Program* program;
- auto direction() const
- {
- const auto distance = target->position - position;
- auto clamped_distance = distance;
- if(clamped_distance.y() > 0.2)
- clamped_distance.y() = 0.2;
- else if(clamped_distance.y() < -0.2)
- clamped_distance.y() = -0.2;
- if(abs(clamped_distance.x()) > 0.18f)
- clamped_distance *= float2::i();
- else
- clamped_distance = support::average(clamped_distance, clamped_distance * float2::i());
- return common::normalize(clamped_distance);
- }
- rect body() const
- {
- return {float2::one(0.1), position, half};
- };
- auto elbows() const
- {
- const auto normal = common::rotate(this->direction(), common::protractor<>::tau(1/4.f));
- auto body = this->body();
- std::array<float2,2> elbows{};
- const auto spread = 1.7f;
- auto arm = 1;
- for(auto&& elbow : elbows)
- {
- elbow = body.position;
- elbow += body.size.x() * normal * spread * arm;
- elbow += - this->direction() * body.size.x()/2 * 1.3;
- if(elbow == elbows[punch_glove])
- elbow -= body.size.x() * normal * (0.7 * punch) * arm;
- arm = -arm;
- }
- elbows[punch_glove] += this->direction() * punch * 0.1;
- return elbows;
- };
- auto gloves() const
- {
- const auto normal = common::rotate(this->direction(), common::protractor<>::tau(1/4.f));
- auto body = this->body();
- std::array<rect,2> gloves{};
- for(auto&& glove : gloves)
- {
- glove = body;
- glove.size *= 0.9;
- glove.position += body.size.x() * normal;
- glove.position += this->direction() * body.size.x()/2;
- }
- gloves[1].position -= body.size.x() * 2 * normal;
- gloves[punch_glove].position += this->direction() * punch * 0.2;
- return gloves;
- };
- void draw(frame& f, auto delta_time)
- {
- const auto direction = this->direction();
- const auto normal = common::rotate(direction, common::protractor<>::tau(1/4.f));
- const auto facingdom = float2x2{direction, normal};
- auto body = this->body();
- {
- float2 squeezement = float2::one();
- this->squeezement.move(squeezement, delta_time);
- const auto squeezed_body_size = body.size * squeezement;
- body.position += direction * (squeezed_body_size.x() - body.size.x());
- body.size = squeezed_body_size;
- }
- auto elbows = this->elbows();
- auto gloves = this->gloves();
- {
- float idle_sway = 0.f;
- auto idle_normal = normal;
- motion::loop(idle_sway, idle, delta_time);
- for(auto&& glove : gloves)
- {
- glove.position += idle_normal * idle_sway;
- idle_normal = -idle_normal;
- }
- body.position += direction * idle_sway;
- }
- {
- gloves[punch_glove].size += gloves[punch_glove].size * float2{0.1f, -0.1f} * punch;
- }
- {
- float2 squeezement = float2::one();
- this->glove_squeezement.move(squeezement, delta_time);
- const auto squeezed_glove_size = gloves[hit_glove].size * squeezement;
- gloves[hit_glove].position += direction * (squeezed_glove_size.x() - gloves[hit_glove].size.x());
- gloves[hit_glove].size = squeezed_glove_size;
- }
- {
- auto scaled_body = scale(body,ring);
- for(auto&& [elbow, glove] : range{vbegin(elbows, gloves), vend(elbows,gloves)})
- {
- auto scaled_elbow = elbow * ring.size + ring.position;
- auto scaled_glove = scale(glove,ring);
- f.begin_sketch()
- .move(scaled_body.position)
- .bezier(scaled_elbow, scaled_glove.position)
- .line_width((0.04f) * ring.size.x()).outline(0x333333_rgb)
- ;
- }
- f.begin_sketch()
- .ellipse(scaled_body.position, facingdom * geom::column(scaled_body.size/2))
- .fill(rgb::white(0.5))
- ;
- }
- {
- auto thumb_offsetdom = geom::vector(
- -direction * 0.1f,
- -normal * 0.4f
- );
- auto glove_sketch = f.begin_sketch();
- for(auto&& glove : gloves)
- {
- auto scaled_glove = scale(glove,ring);
- glove_sketch
- .ellipse(scaled_glove.position, facingdom * geom::column(scaled_glove.size/2))
- .ellipse(scaled_glove.position + thumb_offsetdom(scaled_glove.size), facingdom * geom::column(scaled_glove.size/2/2))
- ;
- thumb_offsetdom[1] = -thumb_offsetdom[1];
- }
- glove_sketch.fill(cola);
- }
- auto socket_position = [&](float side) { return body.position + eye_t::size/2 * (direction + side * normal * 1.2f); };
- {
- auto eye_difference = eyes[0].position - eyes[1].position;
- const auto overlapance = eye_t::size.x() * eye_t::size.x() - eye_difference.quadrance();
- for(auto&& eye : eyes)
- if(damage < (eye.pop_damage + 20) && motion::loop(eye.drift_ratio, eye.drifting, delta_time))
- {
- eye.drift = common::rotate(float2::i(trand_float({0.2f, 0.8f})), common::protractor<>::tau(trand_float()));
- eye.drifting.total<0>() = eye.drifting.total<1>() = 700ms + trand_float() * 200ms;
- }
- if(overlapance > 0)
- {
- auto impact_surface = common::rotate(eye_difference, common::protractor<>::tau(1/4.f));
- float side = 1;
- for(auto&& eye : eyes)
- {
- eye.drift = common::reflect(eye.drift, impact_surface);
- eye.position += side * common::normalize(eye_difference) * support::root2(overlapance)/2;
- side = -side;
- }
- }
- float side = 1;
- for(auto&& eye : eyes)
- {
- if(damage >= (eye.pop_damage + 20) && not eye.fall.done())
- {
- if(eye.fall.advance(delta_time))
- {
- for(int i = 3; i --> 0;) blood.push_back({
- 0xffbbbb_rgb,
- {250ms, (trand_float2() - 0.5f) / 50, float2::zero()},
- eye.position,
- 0.01f
- });
- for(int i = 20; i --> 0;) blood.push_back({
- rgb::red(0.8f),
- {250ms, (trand_float2() - 0.5f) / 50, float2::zero()},
- eye.position,
- 0.005f
- });
- blood.push_back({
- rgb::white(0),
- {250ms, (trand_float2() - 0.5f) / 50, float2::zero()},
- eye.position,
- 0.005f
- });
- auto spit = [tick = 0u, noise = 1.f](auto ratio) mutable
- {
- ++tick;
- if(tick % 10 == 0)
- noise = trand_float({ratio/2, 0.5f + (1-ratio)/2});;
- return std::pair
- {
- common::cosinus(ratio) * 20.f + 20.f,
- (1 - ratio) * noise
- };
- };
- program->request_wave({faded_wave(20, std::move(spit)), 50ms});
- }
- if(motion::loop(eye.bleed, delta_time))
- {
- const auto velocity = common::rotate(direction,
- common::protractor<>::tau(
- support::wrap(trand_float({-1/16.f, 1/16.f}), 1.f)
- )
- );
- for(int i = 3; i --> 0;) blood.push_back({
- rgb::red(0.8f),
- {250ms, velocity / 50 * eye.fall.value()/2, float2::zero()},
- socket_position(side),
- 0.005f
- });
- }
- }
- auto eye_offset = socket_position(side) - eye.position;
- eye.velocity = (damage < (eye.pop_damage + 20)) ? 10 * eye_offset : (eye.drift * (1-eye.drift_ratio));
- eye.position += (eye.velocity + eye.drift_ratio * eye.drift) * delta_time.count();
- if(damage < eye.pop_damage)
- eye.position = socket_position(side);
- side = -side;
- }
- }
- {
- float side = 1;
- for(auto&& eye : eyes)
- {
- if(damage > eye.pop_damage/2)
- {
- for(auto blackeye : eye.black)
- {
- blackeye.position = socket_position(side) + common::rotate(direction * eye_t::size/2 * 0.9f, blackeye.position);
- f.begin_sketch()
- .ellipse(scale(blackeye, ring))
- .fill(0x55007733_rgba)
- ;
- }
- }
- auto scaled_socket = scale(rect{eye_t::size * 0.8, socket_position(side)}, ring);
- f.begin_sketch()
- .ellipse(scaled_socket.position, facingdom * geom::column(scaled_socket.size/2))
- .fill(0x660000_rgb)
- ;
- side = -side;
- }
- }
- for(auto&& eye : eyes)
- {
- if(damage < eye.pop_damage * 0.8f)
- {
- if(0 != motion::loop(eye.lid_value, eye.blink, delta_time))
- {
- eye.blink.total<0>() = 500ms + trand_float() * 2s;
- float heft = std::max((damage - eye.pop_damage/3.f)/(eye.pop_damage/3.f), 0.f);
- eye.blink.total<2>() = eye.blink.total<2>() = 100ms + heft * 500ms;
- }
- }
- else
- {
- if(0 != motion::loop(eye.lid_value, eye.twitch, delta_time))
- {
- eye.twitch.total<0>() = 500ms + trand_float() * 2s;
- }
- }
- }
- if(damage < std::min(eyes[0].pop_damage, eyes[1].pop_damage)/3)
- {
- eyes[0].blink.total<0>() = eyes[1].blink.total<0>();
- }
- {
- float side = 1;
- for(auto&& eye : eyes)
- {
- if(damage >= eye.pop_damage)
- {
- auto scaled_eyelid = scale(rect{eye_t::size * 0.8f, socket_position(side)}, ring);
- auto center_offset = direction * (0.4f + eye.lid_value) * scaled_eyelid.size.x()/2;
- f.begin_sketch()
- .arc(scaled_eyelid.position, range{normal, -normal} * scaled_eyelid.size.x()/2, 3)
- .sector(scaled_eyelid.position + center_offset, range{-normal, normal} * scaled_eyelid.size.x()/2 - center_offset, -3)
- .fill(rgb::red(0.8f))
- ;
- }
- side = -side;
- }
- }
- {
- float side = 1;
- for(auto&& eye : eyes)
- {
- auto scaled_socket = scale(rect{eye_t::size * 0.8, socket_position(side)}, ring);
- auto scaled_eye = scale(rect{eye_t::size, eye.position}, ring);
- auto distance = (socket_position(side) - eye.position).quadrance();
- auto scaled_root = scale(rect{eye_t::size * 0.4, socket_position(side)}, ring);
- f.begin_sketch()
- .ellipse(scaled_root.position, facingdom * geom::column(scaled_root.size/2))
- .fill(rgb::red(0.8f))
- ;
- if(damage < (eye.pop_damage + 20))
- f.begin_sketch()
- .line(scaled_socket.position, scaled_eye.position)
- .line_width( scaled_socket.size.x() * std::max(0.2f, 1.f-distance/(eye_t::size.x()*eye_t::size.x()*9))/2 )
- .outline(rgb::red(0.8f))
- ;
- side = -side;
- }
- }
- const auto look_direction = common::normalize(target->position - position);
- for(auto&& eye : eyes)
- {
- bool eye_popped = damage >= eye.pop_damage;
- const auto eye_direction = (eye.velocity != float2::zero() && eye_popped) ? -common::normalize(eye.velocity) : look_direction;
- const auto eye_normal = common::rotate(eye_direction, common::protractor<>::tau(1/4.f));
- const auto eye_stretch = eye_popped ? 1+eye.velocity.quadrance() : 1;
- const auto eye_facingdom = float2x2{eye_direction * eye_stretch, eye_normal};
- auto scaled_eye = scale(rect{eye_t::size, eye.position}, ring);
- const auto pupil_size = eye_t::size * 0.45f * eye.fall.value();
-
- const auto pupil_blink_offset = (damage < eye.pop_damage * 0.8f) ? eye_direction * eye.lid_value * pupil_size/4 : float2::zero();
- auto scaled_pupil = scale(rect{pupil_size, eye.position + eye_direction * pupil_size - pupil_blink_offset}, ring);
- if(not eye.fall.done())
- {
- f.begin_sketch()
- .ellipse(scaled_pupil.position, eye_facingdom * geom::column(scaled_pupil.size/2))
- .fill(rgb::white(0.f))
- ;
- f.begin_sketch()
- .ellipse(scaled_eye.position, eye_facingdom * geom::column(scaled_eye.size/2 * eye.fall.value()))
- .fill(wayback(0xffffff_rgb, 0xffbbbb_rgb, std::min(1.f, damage/(eye.pop_damage * 0.8f))))
- ;
- }
-
- if(damage > eye.pop_damage * 0.6 && not eye.fall.done())
- for(auto&& vein : eye.veins)
- {
- auto sketch = f.begin_sketch();
- sketch.move(eye_facingdom(vein.path.front()) * scaled_eye.size/2 + scaled_eye.position);
- for(auto&& x : range{vein.path.begin()+1, vein.path.end()})
- {
- sketch.vertex(eye_facingdom(x) * scaled_eye.size/2 * eye.fall.value() + scaled_eye.position);
- }
- sketch
- .line_width(vein.width * scaled_eye.size.x() * eye.fall.value())
- .line_cap(sketch::cap::round)
- .line_join(sketch::join::round)
- .outline(rgb::red())
- ;
- }
- }
- {
- float side = 1;
- for(auto&& eye : eyes)
- {
- if(damage < eye.pop_damage * 0.8f)
- {
- auto scaled_eyelid = scale(rect{eye_t::size * 1.2, socket_position(side)}, ring);
- auto center_offset = direction * (1-eye.lid_value) * scaled_eyelid.size.x();
- auto color = damage > eye.pop_damage/2 ? rgb(0x554477_rgb) : rgb::white(0.4f);
- f.begin_sketch()
- .arc(scaled_eyelid.position, range{normal, -normal} * scaled_eyelid.size.x()/2, 3)
-
- .sector(scaled_eyelid.position - center_offset, range{-normal, normal} * scaled_eyelid.size.x()/2 + center_offset, 3)
- .fill(color)
- ;
- }
- else if(damage < eye.pop_damage)
- {
- auto scaled_eyelid = scale(rect{eye_t::size, socket_position(side)}, ring);
- scaled_eyelid.size += 1;
- auto center_offset = direction * (0.4f + eye.lid_value) * (scaled_eyelid.size.x()/2);
- f.begin_sketch()
- .arc(scaled_eyelid.position, range{normal, -normal} * scaled_eyelid.size.x()/2, 3)
- .sector(scaled_eyelid.position + center_offset, range{-normal, normal} * scaled_eyelid.size.x()/2 - center_offset, -3)
- .fill(rgb::red(0.8f))
- ;
- }
- side = -side;
- }
- }
- }
- void move(auto delta_time)
- {
- const range2f bounds {position - 0.05, position + 0.05};
- constexpr auto world_bounds = range2f{float2::zero(), float2::one()};
- const auto lower_overshoot = world_bounds.lower() - bounds.lower();
- const auto upper_overshoot = bounds.upper() - world_bounds.upper();
- constexpr float springback_factor = 20;
- velocity += springback_factor * max(lower_overshoot, float2::zero());
- velocity -= springback_factor * max(upper_overshoot, float2::zero());
- position += velocity * speed * delta_time.count();
- if(not shovement.done())
- {
- position += shovement.value() * (speed /2) * delta_time.count();
- shovement.advance(delta_time);
- }
- auto diameter = 0.2f;
- const auto distance = target->position - position;
- if(distance.quadrance() < diameter * diameter)
- {
- auto direction = common::normalize(distance);
- auto force_field = direction * diameter;
- auto correction = distance - force_field;
- position += correction;
- }
- if(punch > 0 || hit)
- {
- if(not hit)
- {
- punch += 10 * delta_time.count();;
- if(punch >= 1)
- {
- hit = true;
- punch = 1;
- }
- }
- else
- {
- if(punch > 0)
- {
- punch -= 10 * delta_time.count();
- }
- else
- {
- punch = 0;
- hit = false;
- }
- }
- }
- }
- void update(frame& f, auto delta_time)
- {
- move(delta_time);
- draw(f, delta_time);
- }
- std::optional<rect> punching_glove() const
- {
- const bool forward = punch > 0 && not hit;
- const bool edge_case = punch >= 1;
- if(forward || edge_case)
- return gloves()[punch_glove];
- else
- return std::nullopt;
- }
- unsigned closest_glove_index() const
- {
- return
- (position.y() > target->position.y())
- ^
- (position.x() > target->position.x())
- ;
- }
- rect closest_glove() const
- { return gloves()[closest_glove_index()]; }
- };
- std::array boxas
- {
- boxa{rgb::red(), starting_pos},
- boxa{rgb::blue(), float2::one() - starting_pos}
- };
- constexpr rect fit_square(float2 size)
- {
- float2 fit_size = float2::one(*min_element(size));
- return {fit_size, (size - fit_size) / 2};
- }
- void punch(Program& program, boxa& boxa)
- {
- if(boxa.punch == 0)
- {
- auto hiss = [rand = make_trand_float({0,0.1})](auto ratio) mutable
- { return std::pair{rand(), ratio/5}; };
- program.request_wave({faded_wave(20, std::move(hiss)), 125ms});
-
- boxa.punch = 0.001;
- boxa.punch_glove = boxa.closest_glove_index();
- }
- }
- void start(Program& program)
- {
-
-
-
- program.resizable = true;
- program.size = float2::one(400.f);
- ring = fit_square(program.size);
- boxas[0].target = &boxas[1];
- boxas[1].target = &boxas[0];
- boxas[1].brainz = complayer{};
-
- for(auto&& boxa : boxas)
- {
- boxa.idle.advance(trand_float() * boxa.idle.total);
- auto last_pop = trand_int({70,91});
- auto last_eye = trand_int({0,2});
- boxa.eyes[last_eye].pop_damage = last_pop;
- boxa.eyes[not last_eye].pop_damage = last_pop - trand_int({10,21});
- boxa.program = &program;
- }
- if(program.argc > 2)
- {
- using support::ston;
- using seed_t = decltype(tiny_rand());
- tiny_rand.seed({ ston<seed_t>(program.argv[1]), ston<seed_t>(program.argv[2]) });
- }
- std::cout << "seed: " << std::hex << std::showbase << tiny_rand << '\n';
- program.key_up = [&](scancode code, keycode)
- {
- switch(code)
- {
- case scancode::leftbracket:
- case scancode::c:
- if(pressed(scancode::rctrl) || pressed(scancode::lctrl))
- case scancode::escape:
- program.end();
- break;
- default: break;
- }
- };
- program.key_down = [&](scancode code, keycode)
- {
- for(auto&& boxa : boxas)
- if(boxa.controls)
- if(support::find(boxa.controls->punch, code) != boxa.controls->punch.end())
- punch(program, boxa);
- };
- program.mouse_down = [&](float2, auto)
- {
- };
- program.size_changed = [&](float2 size)
- {
- std::cout << size << '\n';
- ring = fit_square(size);
- std::cout << ring << '\n';
- };
- program.draw_loop = [&](auto frame, auto delta_time)
- {
- frame.begin_sketch()
- .rectangle(rect{frame.size})
- .fill(rgb::white(0.4f))
- ;
- frame.begin_sketch()
- .rectangle(ring)
- .fill(rgb(0.3f, 0.4f, 0.0f))
- ;
- for(auto&& boxa : boxas)
- {
- boxa.velocity = float2::zero();
- if(boxa.controls)
- {
- for(auto&& kv : boxa.controls->legwork)
- if(pressed(kv.k))
- boxa.velocity += kv.v;
- if(boxa.velocity != float2::zero())
- boxa.velocity = common::normalize(boxa.velocity);
- }
- else
- {
- auto selected = support::find_if(control_options, [](auto option)
- {
- return support::any_of(option.legwork, [](auto kv) { return pressed(kv.k); });
- });
- if(selected != control_options.end())
- {
- boxa.controls = *selected;
- control_options.erase(selected);
- }
- }
- if(boxa.brainz)
- {
- if(boxa.brainz->stun_timer.done())
- {
-
- const auto get_flank_offset = [&boxa](float2 flank){ return boxa.target->position + flank - boxa.position; };
- if(motion::loop(boxa.brainz->legwork_timer, delta_time) != 0)
- {
- const auto direction = boxa.target->direction();
- const auto normal = common::rotate(direction, common::protractor<>::tau(1/4.f));
- const auto faceoff = direction * boxa.target->body().size * 2.9;
- const auto backstab = -faceoff;
- const auto normal_flank = faceoff + normal * boxa.target->body().size;
- const auto unnormal_flank = faceoff - normal * boxa.target->body().size;
- const auto back_normal_flank = backstab + normal * boxa.target->body().size;
- const auto back_unnormal_flank = backstab - normal * boxa.target->body().size;
- std::array<float2, 4> candidates {normal_flank, unnormal_flank, back_normal_flank, back_unnormal_flank};
- auto ring_candidates_end = std::remove_if(candidates.begin(), candidates.end(),
- [&boxa](auto flank){ return not range{float2::zero(),float2::one()}.contains(boxa.target->position + flank); });
- using support::transform_arg;
- boxa.brainz->legwork_flank = *std::min_element(candidates.begin(), ring_candidates_end,
- transform_arg{get_flank_offset, transform_arg{&float2::quadrance}});
- if(boxa.direction().y() != 0 && (boxa.position - boxa.target->position).quadrance() < 0.04)
- {
- auto normal = common::rotate(boxa.direction(), common::protractor<>::tau(1/4.f));
- auto diff = boxa.target->position - boxa.position;
- boxa.brainz->sidestep = normal(diff) > 0 ? -1 : 1;
- }
- else
- boxa.brainz->sidestep = 0;
- }
- auto flank_offset = get_flank_offset(boxa.brainz->legwork_flank);
- if(flank_offset.quadrance() > 0.0001)
- boxa.velocity = common::normalize(flank_offset);
- if(boxa.brainz->sidestep != 0)
- {
- auto normal = common::rotate(boxa.direction(), common::protractor<>::tau(1/4.f));
- boxa.velocity = normal * boxa.brainz->sidestep;
- }
- {
- const auto closest_glove_offset = (boxa.target->position - boxa.closest_glove().position);
- if(boxa.brainz->punch_timer.done())
- {
-
- const float punch_length = 0.26f;
- const float punch_width = 0.25f * (0.1f + 0.9f * std::max((1.f - boxa.damage/100.f),0.f));
- auto normal = common::rotate(boxa.direction(), common::protractor<>::tau(1/4.f));
- if
- (
- closest_glove_offset(boxa.direction()) < punch_length
- && closest_glove_offset(normal) < punch_width
- && closest_glove_offset(-normal) < punch_width
- )
- {
- punch(program, boxa);
- boxa.brainz->punch_timer.total = boxa.brainz->punch_intervals[trand_int({0, (int)boxa.brainz->punch_intervals.size()})];
- boxa.brainz->punch_timer.total *= 0.25f + 0.75f * std::max((1.f - boxa.damage/100.f),0.f);
- boxa.brainz->punch_timer.reset();
- }
- }
- else
- boxa.brainz->punch_timer.advance(delta_time);
- }
-
-
- }
- else
- boxa.brainz->stun_timer.advance(delta_time);
- }
- }
- for(auto&& b : boxas) b.update(frame, delta_time);
- for(auto&& b : boxas)
- {
- auto glove = b.punching_glove();
- bool collided_target = false;
- bool collided_a_glove = false;
- bool collided_punching_glove = false;
- if(glove)
- {
- collided_target = (glove->position - b.target->position).quadrance() <= 0.085*0.085;
- if(not collided_target)
- {
- auto other_gloves = b.target->gloves();
- auto collided_glove = support::find_if(other_gloves, [pos = glove->position](auto x) {
- return (pos - x.position).quadrance() <= x.size.quadrance()/2;
- });
- collided_a_glove = collided_glove != other_gloves.end();
- auto other_punching_glove = b.target->punching_glove();
- if(collided_a_glove && other_punching_glove)
- {
- collided_punching_glove = (*collided_glove) == (*other_punching_glove);
- }
- if(collided_a_glove)
- {
- b.target->hit_glove = collided_glove - other_gloves.begin();
- }
- }
- }
- if(collided_target)
- {
- b.target->shovement.total = 250ms;
- const auto normal = common::rotate(b.direction(), common::protractor<>::tau(1/4.f));
- const auto target_normal = common::rotate(b.target->direction(), common::protractor<>::tau(1/4.f));
- const auto target_offset = b.position - b.target->position;
- b.target->shovement.start = (b.direction() + (target_normal(target_offset) < 0 ? normal*2 : -normal*2)) * 5;
- b.target->shovement.reset();
- {
- constexpr auto squeezementat = float2{0.5f, 1.5f};
- b.target->squeezement = melody{
- movement<float2>{125ms, float2::one(), squeezementat},
- movement<float2>{125ms, squeezementat, float2::one()}
- };
- }
- ++b.target->damage;
- for(auto&& eye : b.target->eyes)
- {
- if(b.target->damage == eye.pop_damage)
- {
- auto plok = [](auto ratio)
- { return std::pair{common::cosinus(ratio) * 10.f + 10.f, 1}; };
- program.request_wave({faded_wave(20, std::move(plok)), 50ms});
- }
- if(b.target->damage == eye.pop_damage + 20)
- {
- auto tips = [tick = 0u, noise = 1.f](auto ratio) mutable
- {
- ratio = 1-ratio;
- ++tick;
- if(tick % 10 == 0)
- noise = trand_float({ratio/2, 0.5f + (1-ratio)/2});;
- return std::pair
- {
- common::cosinus(ratio) * 20.f + 20.f,
- (1 - ratio) * noise
- };
- };
- program.request_wave({faded_wave(20, std::move(tips)), 50ms});
- }
- }
- auto boink = [](auto ratio)
- { return std::pair{way(40.f, 20.f, ratio), 1}; };
- program.request_wave({faded_wave(20, std::move(boink)), 125ms});
- const auto splatter_direction = (b.target->position - b.position).signum();
- for(int i = 10; i --> 0;) blood.push_back({
- wayback(0x55cccc_rgb,0x660000_rgb, std::min(b.target->damage/50.f, 1.f)),
- {250ms, trand_float2() / 50 * splatter_direction, float2::zero()},
- b.target->position
- });
- if(b.target->brainz)
- {
- 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));
- b.target->brainz->stun_timer.reset();
- }
- std::cout << std::dec << boxas[0].damage << " vs " << boxas[1].damage << '\n';
- }
- if(collided_a_glove)
- {
- constexpr auto squeezementat = float2{0.7f, 1.3f};
- b.target->glove_squeezement = melody{
- movement<float2>{60ms, float2::one(), squeezementat},
- movement<float2>{60ms, squeezementat, float2::one()}
- };
- auto bunk = [](auto ratio)
- { return std::pair{way(25.f, 0.f, ratio), .3f}; };
- program.request_wave({faded_wave(20, std::move(bunk)), 125ms});
- }
- if(collided_target || collided_a_glove)
- b.hit = true;
- if(collided_punching_glove)
- {
- b.target->hit = true;
- break;
- }
- }
- for(auto&& b : blood) b.update(frame, delta_time);
- blood.erase(std::remove_if(blood.begin(),blood.end(), [](auto& b) {return b.done();}), blood.end());
- };
-
-
- program.request_wave({
- [
- time = 0.f, freq = 0.f,
- musac = motion::melody
- {
- movement<float>{50ms, 0.f,120.f},
- movement<float>{50ms, 120.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,150.f},
- movement<float>{50ms, 150.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,100.f},
- movement<float>{50ms, 100.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,200.f},
- movement<float>{50ms, 200.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,220.f},
- movement<float>{50ms, 220.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,250.f},
- movement<float>{50ms, 250.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,200.f},
- movement<float>{50ms, 200.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,100.f},
- movement<float>{50ms, 100.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,120.f},
- movement<float>{50ms, 120.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,0.f},
- movement<float>{50ms, 0.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,100.f},
- movement<float>{50ms, 100.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,200.f},
- movement<float>{50ms, 200.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,0.f},
- movement<float>{50ms, 0.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,250.f},
- movement<float>{50ms, 250.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,200.f},
- movement<float>{50ms, 200.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- movement<float>{50ms, 0.f,0.f},
- movement<float>{50ms, 0.f,0.f},
- movement<float>{100ms, 0.f,0.f},
- }
- ](auto tick) mutable
- {
- motion::loop(freq, musac, Program::duration(tick));
- time += tick * freq;
- time = support::wrap(time,1.f);
- return stairwave(time) * 0.2f;
- },
- 0ms});
- program.request_wave({
- [
- time = 0.f, freq = 0.f,
- musac = motion::melody
- {
- movement<float, quadratic_out>{10ms, 0.f, 350.f},
- movement<float, quadratic_out>{90ms, 350.f,420.f},
- movement<float, motion::quadratic_curve>{200ms, 420.f,350.f},
- movement<float, quadratic_out>{100ms, 350.f,350.f},
- movement<float, quadratic_out>{100ms, 350.f,750.f},
- movement<float, motion::quadratic_curve>{200ms, 750.f,350.f},
- movement<float, quadratic_out>{100ms, 350.f,350.f},
- movement<float, motion::quadratic_curve>{200ms, 350.f,400.f},
- movement<float, quadratic_out>{100ms, 400.f,350.f},
- movement<float, quadratic_out>{500ms, 350.f,350.f},
- movement<float, quadratic_out>{100ms, 350.f,420.f},
- movement<float, motion::quadratic_curve>{200ms, 420.f,350.f},
- movement<float, quadratic_out>{100ms, 350.f,350.f},
- movement<float, quadratic_out>{100ms, 350.f,750.f},
- movement<float, motion::quadratic_curve>{200ms, 750.f,350.f},
- movement<float, quadratic_out>{90ms, 350.f,350.f},
- movement<float, quadratic_out>{10ms, 350.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- }
- ](auto tick) mutable
- {
- motion::loop(freq, musac, Program::duration(tick));
- time += tick * (freq/3);
- time = support::wrap(time,1.f);
- return stairwave(time) * 0.3f;
- },
- 0ms});
- program.request_wave({
- [
- time = 0.f, freq = 0.f,
- musac = motion::melody
- {
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float>{800ms, 0.f,0.f},
- movement<float, quadratic_out>{400ms, 0.f, 1350.f},
- movement<float, quadratic_out>{400ms, 1350.f,1000.f},
- movement<float, quadratic_out>{400ms, 1000.f, 1200.f},
- movement<float, quadratic_out>{400ms, 1200.f, 350.f},
- movement<float, quadratic_out>{400ms, 350.f,1000.f},
- movement<float, quadratic_out>{400ms, 1000.f, 0.f},
- movement<float>{800ms, 0.f,0.f},
- }
- ](auto tick) mutable
- {
- motion::loop(freq, musac, Program::duration(tick));
- time += tick * (freq/3);
- time = support::wrap(time,1.f);
- return stairwave(time) * 0.1f;
- },
- 0ms});
- }
|