racing:: remembering ghost, track selection (with times), completion percentage shown

This commit is contained in:
Zeno Rogue 2018-11-24 15:35:02 +01:00
parent 27011f1cf0
commit c67d7e06c6
6 changed files with 160 additions and 60 deletions

View File

@ -33,7 +33,7 @@ ld fractick(int period, ld phase = 0) {
return t;
}
ld sintick(int period, ld phase = 0) {
ld sintick(int period, ld phase) {
return sin(ptick(period, phase));
}
@ -5136,15 +5136,7 @@ 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);
}
racing::markers();
#endif
if(lmouseover && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON) {

38
hud.cpp
View File

@ -343,22 +343,6 @@ void drawMobileArrow(int i) {
bool nofps = false;
string racetimeformat(int t) {
string times = "";
int digits = 0;
bool minus = (t < 0);
if(t < 0) t = -t;
while(t || digits < 6) {
int mby = (digits == 5 ? 6 : 10);
times = char('0'+(t%mby)) + times;
t /= mby; digits++;
if(digits == 3) times = "." + times;
if(digits == 5) times = ":" + times;
}
if(minus) times = "-" + times;
return times;
}
void drawStats() {
if(nohud || vid.stereo_mode == sLR) return;
if(callhandlers(false, hooks_prestats)) return;
@ -501,21 +485,29 @@ void drawStats() {
string s0;
if(racing::on) {
#if CAP_RACING
using namespace racing;
color_t col;
if(ticks >= racing::race_start_tick)
if(ticks >= race_start_tick)
col = 0x00FF00;
else if(ticks >= racing::race_start_tick - 2000)
else if(ticks >= race_start_tick - 2000)
col = 0xFFFF00;
else
col = 0xFF0000;
for(int i=0; i<multi::players; i++) if(racing::race_finish_tick[i])
for(int i=0; i<multi::players; i++) if(race_finish_tick[i])
col = 0xFFFFFF;
dynamicval<int> x(vid.fsize, vid.fsize*2);
if(displayButtonS(vid.xres - 8, vid.fsize, racetimeformat(ticks - racing::race_start_tick), col, 16, vid.fsize));
for(int i=0; i<multi::players; i++) if(racing::race_finish_tick[i]) {
multi::cpid = i;
if(displayButtonS(vid.xres - 8, vid.fsize * (3+i), racetimeformat(racing::race_finish_tick[i] - racing::race_start_tick), (getcs().uicolor >> 8), 16, vid.fsize));
if(displayButtonS(vid.xres - 8, vid.fsize, racetimeformat(ticks - race_start_tick), col, 16, vid.fsize));
for(int i=0; i<multi::players; i++) {
if(race_finish_tick[i]) {
multi::cpid = i;
if(displayButtonS(vid.xres - 8, vid.fsize * (3+i), racetimeformat(race_finish_tick[i] - race_start_tick), (getcs().uicolor >> 8), 16, vid.fsize));
}
else {
int comp = get_percentage(i);
if(displayButtonS(vid.xres - 8, vid.fsize * (3+i), its(comp) + "%", (getcs().uicolor >> 8), 16, vid.fsize));
}
}
#endif
}

View File

@ -4674,6 +4674,7 @@ hyperpoint nearcorner(cell *c, int i);
extern bool showquotients;
bool do_draw(cell *c, const transmatrix& T);
ld sintick(int period, ld phase = 0);
#if CAP_RACING
namespace racing {
@ -4682,11 +4683,13 @@ namespace racing {
void show();
void prepare_subscreens();
extern vector<cell*> track;
extern map<cell*, pair<int, int> > trackstage;
extern int current_player;
extern vector<eLand> race_lands;
extern string track_code;
extern int race_start_tick, race_finish_tick[MAXPLAYER];
void race_won();
void apply_seed();
string racetimeformat(int t);
}
bool subscreen_split(reaction_t for_each_subscreen);

View File

@ -13,26 +13,46 @@ bool on;
bool player_relative = false;
bool track_ready;
static const int LENGTH = 250;
static const int LENGTH = 50;
static const int TWIDTH = 6;
static const int DROP = 1;
struct race_cellinfo {
cell *c;
int from_track;
int completion;
};
vector<race_cellinfo> rti;
vector<cell*> track;
map<cell*, pair<int, int> > trackstage;
map<cell*, int> rti_id;
string track_code = "OFFICIAL";
void apply_seed() {
int s = 0;
for(char c: track_code) s = 713 * s + c;
shrand(s);
}
int race_start_tick, race_finish_tick[MAXPLAYER];
struct ghostmoment {
int step;
cell *where;
int where_id;
transmatrix T;
ld footphase;
};
map<string, map<int, vector<ghostmoment> > > race_ghosts;
struct ghost {
charstyle cs;
int result;
vector<ghostmoment> history;
};
vector<ghostmoment> current_history[MAXPLAYER];
map<pair<string, int>, map<eLand, vector<ghost> > > race_ghosts;
array<vector<ghostmoment>, MAXPLAYER> current_history;
void fix_cave(cell *c) {
int v = 0;
@ -71,6 +91,15 @@ int trackval(cell *c) {
return v + bonus;
}
void tie_info(cell *c, int from_track, int comp) {
rti_id[c] = isize(rti);
rti.emplace_back(race_cellinfo{c, from_track, comp});
}
race_cellinfo& get_info(cell *c) {
return rti[rti_id.at(c)];
}
void generate_track() {
track.clear();
@ -211,18 +240,18 @@ void generate_track() {
if(1) {
manual_celllister cl;
for(int i=0; i<isize(track); i++) {
trackstage[track[i]] = {0, i};
for(int i=0; i<isize(track); i++) {
tie_info(track[i], 0, i);
cl.add(track[i]);
}
int win = isize(track) - 10;
int win = isize(track) - DROP;
for(int i=0; i<isize(cl.lst); i++) {
cell *c = cl.lst[i];
auto p = trackstage[c];
forCellEx(c2, c) if(!trackstage.count(c2)) {
trackstage[c2] = {p.first+1, p.second};
auto p = get_info(c);
forCellEx(c2, c) if(!rti_id.count(c2)) {
tie_info(c2, p.from_track+1, p.completion);
cl.add(c2);
}
c->item = itNone;
@ -232,17 +261,17 @@ void generate_track() {
if(c->monst == moIvyHead) c->monst = moIvyWait;
if(inmirror(c->land))
;
else if(p.first == TWIDTH) {
else if(p.from_track == TWIDTH) {
killMonster(c, moNone, 0);
c->wall = waBarrier;
c->land = laBarrier;
}
else if(p.first > TWIDTH) {
else if(p.from_track > TWIDTH) {
killMonster(c, moNone, 0);
c->land = laMemory;
c->wall = waChasm;
}
if(p.second >= win && p.first < TWIDTH) {
if(p.completion >= win && p.from_track < TWIDTH) {
c->wall = hrand(2) ? waMirror : waCloud;
killMonster(c, moNone, 0);
}
@ -252,7 +281,7 @@ void generate_track() {
for(int i=0; i<motypes; i++) kills[i] = 0;
int byat[256];
for(int a=0; a<16; a++) byat[a] = 0;
for(auto s: trackstage) byat[s.second.first]++;
for(const auto s: rti) byat[s.from_track]++;
for(int a=0; a<16; a++) printf("%d: %d\n", a, byat[a]);
if(s->land == laCaves) {
@ -260,11 +289,11 @@ void generate_track() {
while(true) {
unsigned hashval = 7;
int id = 0;
for(auto s: trackstage) {
fix_cave(s.first);
if(s.first->wall == waCavewall)
for(auto s: rti) {
fix_cave(s.c);
if(s.c->wall == waCavewall)
hashval = (3+2*(id++)) * hashval + 1;
if(s.first->wall == waCavefloor)
if(s.c->wall == waCavefloor)
hashval = (3+2*(id++)) * hashval + 2;
}
printf("hashval = %x id = %d\n", hashval, id);
@ -324,15 +353,14 @@ void set_view() {
shmup::monster *who = shmup::pc[current_player];
safety = true;
if(!inrec) current_history[current_player].emplace_back(ghostmoment{ticks - race_start_tick, who->base, who->at, who->footphase});
if(!inrec) current_history[current_player].emplace_back(ghostmoment{ticks - race_start_tick, rti_id[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 z = get_info(who->base).completion;
int steps = euclid ? 1000 : 20;
cell *c1 = racing::track[max(z-steps, 0)];
cell *c2 = racing::track[min(z+steps, isize(racing::track)-1)];
@ -428,7 +456,9 @@ auto hook =
+ addHook(clearmemory, 0, []() {
track_ready = false;
track.clear();
trackstage.clear();
rti.clear();
rti_id.clear();
for(auto &ch: current_history) ch.clear();
})
// + addHook(hooks_handleKey, 120, akh);
;
@ -461,6 +491,47 @@ vector<string> playercmds_race = {
"", "change camera", "", ""
};
string racetimeformat(int t) {
string times = "";
int digits = 0;
bool minus = (t < 0);
if(t < 0) t = -t;
while(t || digits < 6) {
int mby = (digits == 5 ? 6 : 10);
times = char('0'+(t%mby)) + times;
t /= mby; digits++;
if(digits == 3) times = "." + times;
if(digits == 5) times = ":" + times;
}
if(minus) times = "-" + times;
return times;
}
void track_chooser() {
dialog::init(XLAT("Racing"));
char let = 'a';
for(eLand l: race_lands) {
auto& gh = race_ghosts[{track_code, modecode()}] [l];
const int LOST = 3600000;
int best = LOST;
for(auto& gc: gh) best = min(best, gc.result);
string s = (best == LOST) ? "" : racetimeformat(best);
dialog::addSelItem(XLAT1(linf[l].name), s, let++);
dialog::add_action([l] () {
stop_game();
specialland = l;
racing::on = true;
shmup::on = true;
start_game();
popScreenAll();
});
}
dialog::addBack();
dialog::display();
}
struct race_configurer {
int playercfg;
@ -521,6 +592,9 @@ struct race_configurer {
}
else dialog::addBreak(100);
dialog::addItem(XLAT("select the track and start!"), 's');
dialog::add_action([] () { pushScreen(track_chooser); });
dialog::addBack();
dialog::display();
@ -556,6 +630,41 @@ void prepare_subscreens() {
}
}
void race_won() {
if(!race_finish_tick[current_player]) {
race_finish_tick[current_player] = ticks;
charstyle gcs = getcs();
for(color_t *x: {&gcs.skincolor, &gcs.haircolor, &gcs.dresscolor, &gcs.swordcolor, &gcs.dresscolor2})
part(*x, 0) >>= 1;
race_ghosts[{track_code, modecode()}] [specialland].emplace_back(ghost{gcs, ticks - race_start_tick, current_history[current_player]});
}
}
void markers() {
if(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);
}
for(auto& ghost: race_ghosts[{track_code, modecode()}][specialland]) {
auto p = std::find_if(ghost.history.begin(), ghost.history.end(), [] (const ghostmoment gm) { return gm.step > ticks - race_start_tick;} );
if(p == ghost.history.end()) p--;
cell *w = rti[p->where_id].c;
if(!gmatrix.count(w)) continue;
dynamicval<charstyle> x(getcs(), ghost.cs);
drawMonsterType(moPlayer, w, gmatrix[w] * p->T, 0, p->footphase);
}
}
int get_percentage(int i) {
return min(get_info(shmup::pc[i]->base).completion * 100 / (isize(track) - DROP), 100);
}
}
bool subscreen_split(reaction_t what) {

View File

@ -1792,8 +1792,9 @@ void movePlayer(monster *m, int delta) {
mirror::createHere(cw, cpid);
mirror::breakMirror(cw, cpid);
awakenMimics(m, c2);
if(racing::on && !racing::race_finish_tick[racing::current_player])
racing::race_finish_tick[racing::current_player] = ticks;
#if CAP_RACING
if(racing::on) racing::race_won();
#endif
}
if(c2->wall == waGlass && items[itOrbAether]) {
items[itOrbAether] = 0;
@ -3225,6 +3226,7 @@ void init() {
else
pc[i]->at = spin(2*M_PI*i/players) * xpush(firstland == laMotion ? .5 : .3) * Id;
pc[i]->base = cwt.at;
pc[i]->vel = 0;
pc[i]->inBoat = (firstland == laCaribbean || firstland == laOcean || firstland == laLivefjord ||
firstland == laWhirlpool);
pc[i]->store();

View File

@ -35,7 +35,7 @@ bool verless(string v, string cmp) {
bool do_use_special_land() {
return
!safety &&
(peace::on || tactic::on || geometry || NONSTDVAR || randomPatternsMode || yendor::on);
(peace::on || tactic::on || geometry || NONSTDVAR || randomPatternsMode || yendor::on || racing::on);
}
hookset<bool()> *hooks_welcome_message;
@ -113,6 +113,8 @@ void initgame() {
firstland = safetyland;
}
if(racing::on) racing::apply_seed();
bool use_special_land = do_use_special_land();
if(use_special_land) firstland = specialland;