From 16a5e45c0a4c717a10b02011f7325a165c396daa Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Sat, 17 Nov 2018 19:30:50 +0100 Subject: [PATCH] racing:: first commit --- compileunits.h | 5 + graph.cpp | 20 +++ hyper.h | 20 +++ hypgraph.cpp | 9 ++ menus.cpp | 5 + racing.cpp | 383 +++++++++++++++++++++++++++++++++++++++++++++++++ shmup.cpp | 28 +++- sysconfig.h | 4 + system.cpp | 7 + 9 files changed, 479 insertions(+), 2 deletions(-) create mode 100644 racing.cpp diff --git a/compileunits.h b/compileunits.h index 1007f0a0..7a936319 100644 --- a/compileunits.h +++ b/compileunits.h @@ -80,6 +80,11 @@ namespace hr { namespace inv { bool on, activating; } } #include "geom-exp.cpp" #include "quit.cpp" #include "shmup.cpp" + +#if CAP_RACING +#include "racing.cpp" +#endif + #include "conformal.cpp" #include "rug.cpp" #include "control.cpp" diff --git a/graph.cpp b/graph.cpp index 363ad09a..90afc914 100644 --- a/graph.cpp +++ b/graph.cpp @@ -5135,6 +5135,18 @@ void drawMarkers() { } } + #if CAP_RACING + if(racing::on && racing::player_relative) { + using namespace racing; + cell *goal = NULL; + for(cell *c: track) if(inscreenrange(c)) goal = c; + hyperpoint H = tC0(ggmatrix(goal)); + queuechr(H, 2*vid.fsize, 'X', 0x10100 * int(128 + 100 * sintick(150))); + queuestr(H, vid.fsize, its(celldistance(cwt.at, track.back())), 0x10101 * int(128 - 100 * sintick(150))); + addauraspecial(H, 0x10100, 0); + } + #endif + if(lmouseover && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON) { queuecircleat(lmouseover, .8, darkena(lmouseover->cpdist > 1 ? 0x00FFFF : 0xFF0000, 0, 0xFF)); } @@ -5671,6 +5683,14 @@ void drawfullmap() { void gamescreen(int _darken) { + if(subscreen_split([=] () { + calcparam(); + current_display->set_projection(0, false); + current_display->set_viewport(0); + compute_graphical_distance(); + gamescreen(_darken); + })) return; + if((cmode & sm::MAYDARK) && !current_display->sidescreen) { _darken += 2; } diff --git a/hyper.h b/hyper.h index bdf05037..6b8fb8ef 100644 --- a/hyper.h +++ b/hyper.h @@ -4675,5 +4675,25 @@ extern bool showquotients; bool do_draw(cell *c, const transmatrix& T); +#if CAP_RACING +namespace racing { + extern bool on, player_relative; + void generate_track(); + void show(); + void prepare_subscreens(); + extern vector track; + extern map > trackstage; + extern int current_player; + } + +bool subscreen_split(reaction_t for_each_subscreen); + +#else + +namespace racing { static const bool on = false; } +inline bool subscreen_split(reaction_t for_each_subscreen) { return false; } +#endif + + } diff --git a/hypgraph.cpp b/hypgraph.cpp index 3050b087..747007d8 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -1002,6 +1002,13 @@ void centerpc(ld aspd) { if(geometry == gCrystal) crystal::centerrug(aspd); + + #if CAP_RACING + if(racing::on) { + racing::set_view(); + return; + } + #endif if(ors::mode == 2 && vid.sspeed < 5) return; if(vid.sspeed >= 4.99) aspd = 1000; @@ -1049,6 +1056,8 @@ void centerpc(ld aspd) { } void optimizeview() { + + subscreen_split(optimizeview); if(centerover.at && inmirror(centerover.at)) { anims::reflect_view(); diff --git a/menus.cpp b/menus.cpp index 2df73efd..6ac45912 100644 --- a/menus.cpp +++ b/menus.cpp @@ -857,6 +857,11 @@ named_functionality get_o_key() { }); #endif + #if CAP_RACING + if(racing::on) + return named_dialog(XLAT("racing mode"), racing::show); + #endif + if(viewdists) return named_functionality(XLAT("geometry experiments"), runGeometryExperiments); diff --git a/racing.cpp b/racing.cpp new file mode 100644 index 00000000..0f55db26 --- /dev/null +++ b/racing.cpp @@ -0,0 +1,383 @@ +// Hyperbolic Rogue + +// namespaces for complex features (whirlwind, whirlpool, elec, princess, clearing, +// mirror, hive, heat + livecaves, etc.) + +// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details + +namespace hr { + +namespace racing { + +bool on; +bool player_relative = false; + +static const int LENGTH = 250; +static const int TWIDTH = 6; + +vector track; +map > trackstage; + +int trackval(cell *c) { + int v = celldist(c); + int bonus = 0; + forCellEx(c2, c) { + int d = celldist(c2) - v; + if(d < 0 && !passable(c2, c, P_ISPLAYER)) + bonus += 2; + if(d == 0 && !passable(c2, c, P_ISPLAYER)) + bonus ++; + } + return v + bonus; + } + +void generate_track() { + + track.clear(); + + /* + int t = -1; + bignum full_id; + bool onlychild = true; + */ + + cell *s = currentmap->gamestart(); + setdist(s, 6, NULL); + makeEmpty(s); + + map parent; + map > cellbydist; + cellbydist[0].push_back(s); + + cell *goal; + + while(true) { + if(cellbydist.empty()) { + stop_game(); + start_game(); + return; + } + auto it = cellbydist.end(); + it--; + // if(hrand(100) < 85 && it != cellbydist.begin()) it--; + auto& v = it->second; + if(v.empty()) { cellbydist.erase(it); continue; } + int id = hrand(isize(v)); + cell *c = v[id]; + v[id] = v.back(); v.pop_back(); + if(it->first >= LENGTH) { + goal = c; + break; + } + setdist(c, 4, parent[c]); + forCellEx(c1, c) if(passable(c, c1, P_ISPLAYER) && !parent.count(c1)) { + parent[c1] = c; + cellbydist[trackval(c1)].push_back(c1); + } + } + + while(goal != s) { + track.push_back(goal); + forCellEx(c2, goal) if(celldist(c2) < celldist(goal)) { goal = c2; break; } + } + + track.push_back(s); + reverse(track.begin(), track.end()); + + + /* + transmatrix At = spin(hrand(1000)); + + track.push_back(s); + + while(isize(track) < LENGTH) { + At = At * xpush(.1); + cell *sb = s; + virtualRebase(s, At, true); + fixmatrix(At); + if(s != sb) track.push_back(s); + } + */ + + /* + + cellwalker ycw(s, hrand(s->type)); + + track.push_back(s); + + for(int i=0; iland != laMirror) c->bardir = NOBARRIERS; + for(cell *c:track) setdist(c, 0, NULL); + + manual_celllister cl; + + for(int i=0; iitem = itNone; + if(c->wall == waMirror || c->wall == waCloud) c->wall = waNone; + if(!isIvy(c)) + c->monst = moNone; + if(inmirror(c->land)) + ; + else if(p.first == TWIDTH) + c->wall = waBarrier, + c->land = laBarrier; + else if(p.first > TWIDTH) + c->land = laMemory, + c->wall = waChasm; + if(p.second >= win && p.first < TWIDTH) c->wall = hrand(2) ? waMirror : waCloud; + } + + int byat[256]; + for(int a=0; a<16; a++) byat[a] = 0; + for(auto s: trackstage) byat[s.second.first]++; + for(int a=0; a<16; a++) printf("%d: %d\n", a, byat[a]); + + /* + for(cell *c: track) { + int i = trackval(c) - celldist(c); + if(i == 0) c->item = itDiamond; + if(i == 1) c->item = itGold; + if(i == 2) c->item = itEmerald; + if(i == 3) c->item = itSapphire; + if(i == 4) c->item = itRuby; + if(i >= 5) c->item = itBone; + } + */ + } + +vector > history; + +bool inrec = false; + +ld race_angle = 90; + +int current_player; + +void set_view() { + + if(subscreen_split(set_view)) return; + + shmup::monster *who = shmup::pc[current_player]; + + safety = true; + printf("%d\n", ticks); + if(!inrec) history.emplace_back(ticks, who->base, who->at, who->footphase); + + transmatrix at = ggmatrix(who->base) * who->at; + + if(racing::player_relative) + View = spin(race_angle * degree) * inverse(at) * View; + else { + int z = racing::trackstage[who->base].second; + int steps = euclid ? 1000 : 20; + cell *c1 = racing::track[max(z-steps, 0)]; + cell *c2 = racing::track[min(z+steps, isize(racing::track)-1)]; + transmatrix T1 = ggmatrix(c1); + transmatrix T2 = ggmatrix(c2); + transmatrix T = spintox(inverse(T1) * T2 * C0); + hyperpoint h = T * inverse(T1) * at * C0; + ld y = asin_auto(h[1]); + ld x = asin_auto(h[0] / cos_auto(y)); + // printf("%d %lf\n", z, x); + transmatrix Z = T1 * inverse(T) * xpush(x); + View = spin(race_angle * degree) * inverse(Z) * View; + } + } + +#if CAP_COMMANDLINE +void show(); + +int readArgs() { + using namespace arg; + + if(0) ; + else if(argis("-racing")) { + PHASEFROM(2); + stop_game(); + shmup::on = true; + racing::on = true; + tactic::on = true; + timerghost = false; + } + else return 1; + return 0; + } +#endif + +int tstart, tstop; +heptspin sview; + +/* +void restore_time(int t) { + tuple sf = make_tuple(t, nullptr, Id, 0); + auto it = lower_bound(history.begin(), history.end(), sf, [] (auto a, auto b) { return get<0>(a) < get<0>(b); }); + auto& m = shmup::pc[0]; + tie(t, m->base, m->at, m->footphase) = *it; + shmup::pc[0]->pat = ggmatrix(shmup::pc[0]->base) * shmup::pc[0]->at; + } +*/ + +/* +bool akh(int sym, int uni) { + if(uni == '1') { tstart = ticks; sview = viewctr; } + else if(uni == '2') { tstop = ticks; } + else if(uni == '3') { conformal::model_orientation = 90; pmodel = mdBand; player_relative = false; } + else if(uni == '4') { conformal::model_orientation = 180; pmodel = mdHalfplane; conformal::halfplane_scale = 2; player_relative = false; } + else if(uni == '5') { pmodel = mdDisk; player_relative = true; } + else if(uni == '6') { vid.use_smart_range = true; vid.smart_range_detail = 2; } + else if(uni == '7' && tstart && tstop) { + viewctr = sview; + nohud = true; + restore_time(tstart); + drawthemap(); + centerpc(0); + int t = tstart; + int i = 0; + inrec = true; + while(t < tstop) { + ticks = t; + char buf[1000]; + restore_time(t); + shmup::fixStorage(); + centerpc(0); + optimizeview(); + cwt.at = shmup::pc[0]->base; + drawthemap(); + fullcenter(); + cmode = sm::NORMAL; + drawthemap(); + centerpc(0); + optimizeview(); + snprintf(buf, 1000, "animations/race/race%d-%03d.png", int(pmodel), i++); + saveHighQualityShot(buf); + t += 40; + } + inrec = false; + } + else return false; + return true; + } +*/ + +#if CAP_COMMANDLINE +auto hook = + addHook(hooks_args, 100, readArgs) +// + addHook(hooks_handleKey, 120, akh); + ; +#endif + +vector race_lands = { + laIce, laDesert, + + laCrossroads, /* need editing */ + laCaves, /* need fixing */ + laJungle, /* need pacifying Ivy */ + laMirror, + laHell, + laDryForest, + laDeadCaves, + laRedRock, + laElementalWall, + laWildWest, + laDragon, + laHunting, + laTerracotta, /* disable traps and warriors */ + laRuins, + }; + +void show() { + dialog::init(XLAT("Racing")); + + dialog::addBoolItem(XLAT("player relative"), player_relative, 'r'); + dialog::add_action([] () { + player_relative = !player_relative; + if(pmodel == mdBand || pmodel == mdHalfplane) + pmodel = mdDisk; + }); + + dialog::addSelItem(XLAT("projection"), conformal::get_model_name(pmodel), 'm'); + dialog::add_action([] () { + switch(pmodel) { + case mdDisk: + pmodel = mdBand; + conformal::model_orientation = race_angle; + break; + case mdBand: + pmodel = mdHalfplane; + conformal::model_orientation = race_angle + 90; + break; + default: + pmodel = mdDisk; + } + }); + + dialog::addSelItem(XLAT("race angle"), fts(race_angle), 'm'); + dialog::add_action([] () { + dialog::editNumber(race_angle, 0, 360, 15, 0, XLAT("spiral angle"), ""); + int q = conformal::model_orientation - race_angle; + dialog::reaction = [q] () { conformal::model_orientation = race_angle + q; }; + }); + + dialog::addBack(); + dialog::display(); + + } + } + +void prepare_subscreens() { + } + +} diff --git a/shmup.cpp b/shmup.cpp index fe32f32a..1bfaa20e 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -1259,7 +1259,9 @@ void killThePlayer(eMonster m) { monster *playerCrash(monster *who, hyperpoint where) { if(who->isVirtual) return NULL; - for(int j=0; jisVirtual) continue; double d = intval(pc[j]->pat*C0, where); if(d < 0.1 * SCALE2 || d > 100) return pc[j]; @@ -1493,6 +1495,10 @@ static const int reflectflag = P_MIRRORWALL; void movePlayer(monster *m, int delta) { cpid = m->pid; + + #if CAP_RACING + if(racing::on && cpid != racing::current_player) return; + #endif double mturn = 0, mgo = 0, mdx = 0, mdy = 0; @@ -1562,6 +1568,13 @@ void movePlayer(monster *m, int delta) { // if(mturn > 1) mturn = 1; // if(mturn < -1) mturn = -1; + #if CAP_RACING + if(racing::on) { + if(abs(mdy) > abs(mgo)) mgo = -mdy; + if(abs(mdx) > abs(mturn)) mturn = -mdx; + mdx = mdy = 0; + } + #endif playerturn[cpid] = mturn * delta / 150.0; @@ -1611,7 +1624,15 @@ void movePlayer(monster *m, int delta) { if(mgo < -1) mgo = -1; if(!canmove) mgo = 0; - playergo[cpid] = mgo * SCALE * delta / 600; + if(racing::on) { + m->vel += mgo * delta / 600; + playergo[cpid] = m->vel * SCALE * delta / 600; + printf("vel = %lf go = %lf\n", m->vel, playergo[cpid]); + } + + else { + playergo[cpid] = mgo * SCALE * delta / 600; + } if(playergo[cpid] && markOrb(itOrbDash)) playergo[cpid] *= 1.5; @@ -2935,8 +2956,11 @@ hookset *hooks_turn; void turn(int delta) { + if(subscreen_split( [delta] () { turn(delta); })) return; + if(callhandlers(false, hooks_turn, delta)) return; if(!shmup::on) return; + timetowait = 0; passive_switch = (gold() & 1) ? moSwitch1 : moSwitch2; diff --git a/sysconfig.h b/sysconfig.h index 53c0a6e2..8495eac1 100644 --- a/sysconfig.h +++ b/sysconfig.h @@ -257,6 +257,10 @@ #define CAP_COMPLEX2 CAP_BONUS #endif +#ifndef CAP_RACING +#define CAP_RACING CAP_BONUS +#endif + #if ISMOBILE #define EXTRALICENSE "\n\nHyperRogue soundtrack by Shawn Parrotte (http://www.shawnparrotte.com), under the Creative Commons BY-SA 3.0 license, http://creativecommons.org/licenses/by-sa/3.0/" #undef XEXTRALICENSE diff --git a/system.cpp b/system.cpp index 6a561298..49c647c3 100644 --- a/system.cpp +++ b/system.cpp @@ -175,6 +175,10 @@ void initgame() { yendor::init(2); + #if CAP_RACING + if(racing::on) racing::generate_track(); + #endif + clear_euland(specialland); if(euclid && specialland == laPrincessQuest) { @@ -1301,6 +1305,9 @@ void start_game() { resetmusic(); #if CAP_TEXTURE texture::config.remap(); +#endif +#if CAP_RACING + racing::prepare_subscreens(); #endif }