mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-17 18:54:48 +00:00
racing:: new system
This commit is contained in:
parent
9331309af9
commit
d2bd8933dd
@ -819,6 +819,15 @@ EX namespace mapstream {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(f.vernum >= 0xA912) {
|
||||||
|
f.write(racing::on);
|
||||||
|
if(racing::on) {
|
||||||
|
f.write<int>(isize(racing::track));
|
||||||
|
for(auto& t: racing::track) f.write<int>(cellids[t]);
|
||||||
|
racing::save_ghosts(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
callhooks(hooks_savemap, f);
|
callhooks(hooks_savemap, f);
|
||||||
f.write<int>(0);
|
f.write<int>(0);
|
||||||
|
|
||||||
@ -1063,6 +1072,20 @@ EX namespace mapstream {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(f.vernum >= 0xA912) {
|
||||||
|
f.read(racing::on);
|
||||||
|
if(racing::on) {
|
||||||
|
if(!shmup::on) {
|
||||||
|
shmup::on = true;
|
||||||
|
shmup::init();
|
||||||
|
}
|
||||||
|
racing::track.resize(f.get<int>());
|
||||||
|
for(auto& t: racing::track) t = cellbyid[f.get<int>()];
|
||||||
|
racing::load_ghosts(f);
|
||||||
|
racing::configure_track(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(f.vernum >= 0xA848) {
|
if(f.vernum >= 0xA848) {
|
||||||
int i;
|
int i;
|
||||||
f.read(i);
|
f.read(i);
|
||||||
|
363
racing.cpp
363
racing.cpp
@ -20,8 +20,7 @@ EX bool on;
|
|||||||
EX bool player_relative = false;
|
EX bool player_relative = false;
|
||||||
EX bool standard_centering = false;
|
EX bool standard_centering = false;
|
||||||
EX bool track_ready;
|
EX bool track_ready;
|
||||||
|
EX bool official_race = false;
|
||||||
bool official_race = false;
|
|
||||||
|
|
||||||
int TWIDTH;
|
int TWIDTH;
|
||||||
|
|
||||||
@ -46,18 +45,10 @@ map<cell*, int> rti_id;
|
|||||||
|
|
||||||
EX int trophy[MAXPLAYER];
|
EX int trophy[MAXPLAYER];
|
||||||
|
|
||||||
EX string track_code = "OFFICIAL";
|
|
||||||
|
|
||||||
transmatrix straight;
|
transmatrix straight;
|
||||||
|
|
||||||
int race_try;
|
int race_try;
|
||||||
|
|
||||||
EX void apply_seed() {
|
|
||||||
int s = race_try;
|
|
||||||
for(char c: track_code) s = 713 * s + c;
|
|
||||||
shrand(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
EX int race_start_tick, race_finish_tick[MAXPLAYER];
|
EX int race_start_tick, race_finish_tick[MAXPLAYER];
|
||||||
|
|
||||||
typedef unsigned char uchar;
|
typedef unsigned char uchar;
|
||||||
@ -71,7 +62,8 @@ transmatrix spin_uchar(uchar x) { return spin(uchar_to_frac(x) * 2 * M_PI); }
|
|||||||
static const ld distance_multiplier = 4;
|
static const ld distance_multiplier = 4;
|
||||||
|
|
||||||
struct ghostmoment {
|
struct ghostmoment {
|
||||||
int step, where_id;
|
int step;
|
||||||
|
cell *where_cell;
|
||||||
uchar alpha, distance, beta, footphase;
|
uchar alpha, distance, beta, footphase;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,54 +75,24 @@ struct ghost {
|
|||||||
vector<ghostmoment> history;
|
vector<ghostmoment> history;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef map<eLand, vector<ghost>> raceset;
|
vector<ghost> ghostset;
|
||||||
map<pair<string, modecode_t>, raceset> race_ghosts;
|
|
||||||
|
|
||||||
map<pair<string, modecode_t>, raceset> official_race_ghosts;
|
|
||||||
|
|
||||||
raceset& ghostset() { return race_ghosts[make_pair(track_code, modecode())]; }
|
|
||||||
raceset& oghostset() { return official_race_ghosts[make_pair(track_code, modecode())]; }
|
|
||||||
|
|
||||||
int get_score_in_land(eLand l) {
|
|
||||||
auto& gh = ghostset();
|
|
||||||
if(!gh.count(l)) return 0;
|
|
||||||
auto& v = gh[l];
|
|
||||||
if(!isize(v)) return 0;
|
|
||||||
return v[0].result;
|
|
||||||
}
|
|
||||||
|
|
||||||
array<vector<ghostmoment>, MAXPLAYER> current_history;
|
array<vector<ghostmoment>, MAXPLAYER> current_history;
|
||||||
|
|
||||||
|
EX map<eLand, int> best_scores;
|
||||||
|
|
||||||
string ghost_prefix = "default";
|
string ghost_prefix = "default";
|
||||||
|
|
||||||
#if CAP_FILES
|
#if CAP_FILES
|
||||||
string ghost_filename(string seed, modecode_t mcode) {
|
|
||||||
if(ghost_prefix == "default") {
|
|
||||||
#ifdef FHS
|
|
||||||
if(getenv("HOME")) {
|
|
||||||
string s = getenv("HOME");
|
|
||||||
mkdir((s + "/.hyperrogue").c_str(), 0755);
|
|
||||||
mkdir((s + "/.hyperrogue/racing").c_str(), 0755);
|
|
||||||
ghost_prefix = s + "/.hyperrogue/racing/";
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#if WINDOWS
|
|
||||||
mkdir("racing");
|
|
||||||
#else
|
|
||||||
mkdir("racing", 0755);
|
|
||||||
#endif
|
|
||||||
ghost_prefix = "racing/";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return ghost_prefix + seed + "-" + itsh(mcode) + ".data";
|
|
||||||
}
|
|
||||||
|
|
||||||
void hread(hstream& hs, ghostmoment& m) {
|
void hread(hstream& hs, ghostmoment& m) {
|
||||||
hread(hs, m.step, m.where_id, m.alpha, m.distance, m.beta, m.footphase);
|
int id;
|
||||||
|
hread(hs, m.step, id, m.alpha, m.distance, m.beta, m.footphase);
|
||||||
|
m.where_cell = mapstream::cellbyid[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
void hwrite(hstream& hs, const ghostmoment& m) {
|
void hwrite(hstream& hs, const ghostmoment& m) {
|
||||||
hwrite(hs, m.step, m.where_id, m.alpha, m.distance, m.beta, m.footphase);
|
int id = mapstream::cellids[m.where_cell];
|
||||||
|
hwrite(hs, m.step, id, m.alpha, m.distance, m.beta, m.footphase);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hread(hstream& hs, ghost& gh) {
|
void hread(hstream& hs, ghost& gh) {
|
||||||
@ -141,37 +103,18 @@ void hwrite(hstream& hs, const ghost& gh) {
|
|||||||
hwrite(hs, gh.cs, gh.result, gh.timestamp, gh.checksum, gh.history);
|
hwrite(hs, gh.cs, gh.result, gh.timestamp, gh.checksum, gh.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_ghosts(string seed, modecode_t mcode) {
|
EX void save_ghosts(hstream& f) {
|
||||||
|
hwrite(f, ghostset);
|
||||||
if(seed == "OFFICIAL" && mcode == 2) {
|
|
||||||
fhstream f("officials.data", "rb");
|
|
||||||
if(f.f) {
|
|
||||||
hread(f, f.vernum);
|
|
||||||
hread(f, oghostset());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string fname = ghost_filename(seed, mcode);
|
EX void load_ghosts(hstream& f) {
|
||||||
println(hlog, "trying to read ghosts from: ", fname);
|
hread(f, ghostset);
|
||||||
fhstream f(fname, "rb");
|
|
||||||
if(!f.f) return false;
|
|
||||||
hread(f, f.vernum);
|
|
||||||
if(f.vernum <= 0xA600) return true; // scores removed due to the possibility of cheating
|
|
||||||
hread(f, ghostset());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_ghosts(string seed, int mcode) {
|
|
||||||
fhstream f;
|
|
||||||
f.f = fopen(ghost_filename(seed, mcode).c_str(), "wb");
|
|
||||||
if(!f.f) throw hstream_exception(); // ("failed to write the ghost file");
|
|
||||||
hwrite(f, f.vernum);
|
|
||||||
hwrite(f, ghostset());
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
shiftmatrix get_ghostmoment_matrix(ghostmoment& p) {
|
shiftmatrix get_ghostmoment_matrix(ghostmoment& p) {
|
||||||
cell *w = rti[p.where_id].c;
|
cell *w = p.where_cell;
|
||||||
transmatrix T = spin_uchar(p.alpha) * xpush(uchar_to_frac(p.distance) * distance_multiplier) * spin_uchar(p.beta);
|
transmatrix T = spin_uchar(p.alpha) * xpush(uchar_to_frac(p.distance) * distance_multiplier) * spin_uchar(p.beta);
|
||||||
return gmatrix[w] * T;
|
return gmatrix[w] * T;
|
||||||
}
|
}
|
||||||
@ -241,8 +184,6 @@ race_cellinfo& get_info(cell *c) {
|
|||||||
return rti[rti_id.at(c)];
|
return rti[rti_id.at(c)];
|
||||||
}
|
}
|
||||||
|
|
||||||
int race_checksum;
|
|
||||||
|
|
||||||
ld start_line_width;
|
ld start_line_width;
|
||||||
|
|
||||||
struct hr_track_failure : hr_exception {};
|
struct hr_track_failure : hr_exception {};
|
||||||
@ -427,16 +368,13 @@ EX bool bounded_track;
|
|||||||
|
|
||||||
EX void generate_track() {
|
EX void generate_track() {
|
||||||
|
|
||||||
|
official_race = false;
|
||||||
|
ghostset.clear();
|
||||||
TWIDTH = getDistLimit() - 1;
|
TWIDTH = getDistLimit() - 1;
|
||||||
if(TWIDTH == 1) TWIDTH = 2;
|
if(TWIDTH == 1) TWIDTH = 2;
|
||||||
if(sl2) TWIDTH = 2;
|
if(sl2) TWIDTH = 2;
|
||||||
TWIDTH += race_try / 8;
|
TWIDTH += race_try / 8;
|
||||||
|
|
||||||
#if CAP_FILES
|
|
||||||
if(ghostset().empty())
|
|
||||||
read_ghosts(track_code, modecode());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
track.clear();
|
track.clear();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -520,9 +458,25 @@ EX void generate_track() {
|
|||||||
if(WDIM == 3) dl = 7 - TWIDTH;
|
if(WDIM == 3) dl = 7 - TWIDTH;
|
||||||
for(cell *c:track) setdist(c, dl, NULL);
|
for(cell *c:track) setdist(c, dl, NULL);
|
||||||
|
|
||||||
|
configure_track(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EX vector<cell*> reachable_goals;
|
||||||
|
|
||||||
|
EX void restore_goals() {
|
||||||
|
for(auto c: reachable_goals)
|
||||||
|
c->wall = pick(waCloud, waMirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
EX void configure_track(bool gen) {
|
||||||
|
cell *s = track[0];
|
||||||
|
|
||||||
if(true) {
|
if(true) {
|
||||||
manual_celllister cl;
|
manual_celllister cl;
|
||||||
|
|
||||||
|
rti.clear();
|
||||||
|
rti_id.clear();
|
||||||
|
|
||||||
for(int i=0; i<isize(track); i++) {
|
for(int i=0; i<isize(track); i++) {
|
||||||
tie_info(track[i], 0, i);
|
tie_info(track[i], 0, i);
|
||||||
cl.add(track[i]);
|
cl.add(track[i]);
|
||||||
@ -537,7 +491,7 @@ EX void generate_track() {
|
|||||||
tie_info(c2, p.from_track+1, p.completion);
|
tie_info(c2, p.from_track+1, p.completion);
|
||||||
cl.add(c2);
|
cl.add(c2);
|
||||||
}
|
}
|
||||||
if(bounded_track) continue;
|
if(bounded_track || !gen) continue;
|
||||||
c->item = itNone;
|
c->item = itNone;
|
||||||
if(c->wall == waMirror || c->wall == waCloud) c->wall = waNone;
|
if(c->wall == waMirror || c->wall == waCloud) c->wall = waNone;
|
||||||
if(!isIvy(c))
|
if(!isIvy(c))
|
||||||
@ -567,7 +521,7 @@ EX void generate_track() {
|
|||||||
for(const auto s: rti) byat[s.from_track]++;
|
for(const auto s: rti) byat[s.from_track]++;
|
||||||
for(int a=0; a<16; a++) printf("%d: %d\n", a, byat[a]);
|
for(int a=0; a<16; a++) printf("%d: %d\n", a, byat[a]);
|
||||||
|
|
||||||
if(s->land == laCaves || (s->land == laCrossroads && !keep_to_crossroads())) {
|
if(gen) if(s->land == laCaves || (s->land == laCrossroads && !keep_to_crossroads())) {
|
||||||
set<unsigned> hash;
|
set<unsigned> hash;
|
||||||
while(true) {
|
while(true) {
|
||||||
unsigned hashval = 7;
|
unsigned hashval = 7;
|
||||||
@ -596,10 +550,11 @@ EX void generate_track() {
|
|||||||
hyperpoint h = straight * parabolic1(a) * C0;
|
hyperpoint h = straight * parabolic1(a) * C0;
|
||||||
cell *at = s;
|
cell *at = s;
|
||||||
virtualRebase(at, h);
|
virtualRebase(at, h);
|
||||||
if(!rti_id.count(at) || get_info(at).from_track >= TWIDTH) break;
|
if(!rti_id.count(at)) break;
|
||||||
|
if(at->land == laMemory) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(WDIM == 2 && !bounded_track) for(ld cleaner=0; cleaner<a*.75; cleaner += .2) for(int dir=-1; dir<=1; dir+=2) {
|
if(gen) if(WDIM == 2 && !bounded_track) for(ld cleaner=0; cleaner<a*.75; cleaner += .2) for(int dir=-1; dir<=1; dir+=2) {
|
||||||
transmatrix T = straight * parabolic1(cleaner * dir);
|
transmatrix T = straight * parabolic1(cleaner * dir);
|
||||||
cell *at = s;
|
cell *at = s;
|
||||||
virtualRebase(at, T);
|
virtualRebase(at, T);
|
||||||
@ -613,50 +568,24 @@ EX void generate_track() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto s: rti) if(s.c->monst == moIvyDead) s.c->monst = moNone;
|
if(gen) for(auto s: rti) if(s.c->monst == moIvyDead) s.c->monst = moNone;
|
||||||
|
|
||||||
for(int i=0; i<motypes; i++) kills[i] = 0;
|
for(int i=0; i<motypes; i++) kills[i] = 0;
|
||||||
|
|
||||||
vector<shiftmatrix> forbidden;
|
if(gen && bounded_track) track.back()->wall = waCloud;
|
||||||
for(auto& ghost: ghostset()[specialland])
|
|
||||||
forbidden.push_back(get_ghostmoment_matrix(ghost.history[0]));
|
|
||||||
for(auto& ghost: oghostset()[specialland])
|
|
||||||
forbidden.push_back(get_ghostmoment_matrix(ghost.history[0]));
|
|
||||||
|
|
||||||
for(int i=0; i<multi::players; i++) trophy[i] = 0;
|
|
||||||
|
|
||||||
for(int i=0; i<multi::players; i++) {
|
|
||||||
auto who = shmup::pc[i];
|
|
||||||
// this is intentionally not hrand
|
|
||||||
|
|
||||||
for(int j=0; j<100; j++) {
|
|
||||||
if(WDIM == 3 || bounded_track)
|
|
||||||
who->at = Id; // straight * cspin(0, 2, rand() % 360) * cspin(1, 2, rand() % 360);
|
|
||||||
else
|
|
||||||
who->at = straight * parabolic1(start_line_width * (rand() % 20000 - 10000) / 40000) * spin(rand() % 360);
|
|
||||||
who->base = s;
|
|
||||||
bool ok = true;
|
|
||||||
for(const shiftmatrix& t: forbidden) if(hdist(t*C0, shiftless(who->at) * C0) < 10. / (j+10)) ok = false;
|
|
||||||
if(ok) break;
|
|
||||||
}
|
|
||||||
virtualRebase(who);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bounded_track) track.back()->wall = waCloud;
|
|
||||||
|
|
||||||
if(1) {
|
if(1) {
|
||||||
manual_celllister cl;
|
manual_celllister cl;
|
||||||
cl.add(s);
|
cl.add(s);
|
||||||
bool goal = false;
|
|
||||||
for(int i=0; i<isize(cl.lst); i++) {
|
for(int i=0; i<isize(cl.lst); i++) {
|
||||||
cell *c = cl.lst[i];
|
cell *c = cl.lst[i];
|
||||||
forCellEx(c2, c) {
|
forCellEx(c2, c) {
|
||||||
if(among(c2->wall, waCloud, waMirror)) goal = true;
|
if(among(c2->wall, waCloud, waMirror)) reachable_goals.push_back(c2);
|
||||||
if(passable(c2, c, P_ISPLAYER))
|
if(passable(c2, c, P_ISPLAYER))
|
||||||
cl.add(c2);
|
cl.add(c2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!goal) {
|
if(reachable_goals.empty()) {
|
||||||
printf("error: goal unreachable\n");
|
printf("error: goal unreachable\n");
|
||||||
gamegen_failure = true;
|
gamegen_failure = true;
|
||||||
race_try++;
|
race_try++;
|
||||||
@ -729,24 +658,40 @@ EX void generate_track() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
track_ready = true;
|
track_ready = true;
|
||||||
race_checksum = hrand(1000000);
|
reset_race();
|
||||||
|
}
|
||||||
|
|
||||||
|
EX void reset_race() {
|
||||||
|
|
||||||
|
cell *s = track[0];
|
||||||
|
|
||||||
|
vector<shiftmatrix> forbidden;
|
||||||
|
for(auto& ghost: ghostset)
|
||||||
|
forbidden.push_back(get_ghostmoment_matrix(ghost.history[0]));
|
||||||
|
|
||||||
race_start_tick = 0;
|
race_start_tick = 0;
|
||||||
for(int i=0; i<MAXPLAYER; i++) race_finish_tick[i] = 0;
|
for(int i=0; i<MAXPLAYER; i++) race_finish_tick[i] = 0;
|
||||||
|
|
||||||
official_race = (track_code == "OFFICIAL" && modecode() == 2);
|
for(int i=0; i<multi::players; i++) trophy[i] = 0;
|
||||||
|
|
||||||
if(official_race && isize(oghostset() [specialland])) {
|
for(int i=0; i<multi::players; i++) {
|
||||||
auto& ghost_checksum = oghostset() [specialland] [0].checksum;
|
auto who = shmup::pc[i];
|
||||||
/* seems to work despire wrong checksum... */
|
if(!who) { println(hlog, "who missing"); continue; }
|
||||||
if(ghost_checksum == 418679) ghost_checksum = 522566;
|
// this is intentionally not hrand
|
||||||
if(race_checksum != ghost_checksum) {
|
|
||||||
println(hlog, "race_checksum = ", race_checksum);
|
for(int j=0; j<100; j++) {
|
||||||
println(hlog, "ghost = ", oghostset() [specialland] [0].checksum);
|
if(WDIM == 3 || bounded_track)
|
||||||
official_race = false;
|
who->at = Id; // straight * cspin(0, 2, rand() % 360) * cspin(1, 2, rand() % 360);
|
||||||
addMessage(XLAT("Race did not generate correctly for some reason -- not ranked"));
|
else
|
||||||
|
who->at = straight * parabolic1(start_line_width * (rand() % 20000 - 10000) / 40000) * spin(rand() % 360);
|
||||||
|
who->base = s;
|
||||||
|
bool ok = true;
|
||||||
|
for(const shiftmatrix& t: forbidden) if(hdist(t*C0, shiftless(who->at) * C0) < 10. / (j+10)) ok = false;
|
||||||
|
if(ok) break;
|
||||||
}
|
}
|
||||||
|
virtualRebase(who);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inrec = false;
|
bool inrec = false;
|
||||||
@ -789,7 +734,7 @@ EX bool set_view() {
|
|||||||
ld alpha = -atan2(T * C0);
|
ld alpha = -atan2(T * C0);
|
||||||
ld distance = hdist0(T * C0);
|
ld distance = hdist0(T * C0);
|
||||||
ld beta = -atan2(xpush(-distance) * spin(-alpha) * T * Cx1);
|
ld beta = -atan2(xpush(-distance) * spin(-alpha) * T * Cx1);
|
||||||
current_history[multi::cpid].emplace_back(ghostmoment{ticks - race_start_tick, rti_id[who->base],
|
current_history[multi::cpid].emplace_back(ghostmoment{ticks - race_start_tick, who->base,
|
||||||
angle_to_uchar(alpha),
|
angle_to_uchar(alpha),
|
||||||
frac_to_uchar(distance / distance_multiplier),
|
frac_to_uchar(distance / distance_multiplier),
|
||||||
angle_to_uchar(beta),
|
angle_to_uchar(beta),
|
||||||
@ -862,6 +807,10 @@ int readArgs() {
|
|||||||
stop_game();
|
stop_game();
|
||||||
switch_game_mode(rg::racing);
|
switch_game_mode(rg::racing);
|
||||||
}
|
}
|
||||||
|
else if(argis("-racing-official")) {
|
||||||
|
PHASEFROM(2);
|
||||||
|
load_official_track();
|
||||||
|
}
|
||||||
else if(argis("-rsc")) {
|
else if(argis("-rsc")) {
|
||||||
standard_centering = true;
|
standard_centering = true;
|
||||||
}
|
}
|
||||||
@ -890,6 +839,7 @@ auto hook =
|
|||||||
track.clear();
|
track.clear();
|
||||||
rti.clear();
|
rti.clear();
|
||||||
rti_id.clear();
|
rti_id.clear();
|
||||||
|
reachable_goals.clear();
|
||||||
for(auto &ch: current_history) ch.clear();
|
for(auto &ch: current_history) ch.clear();
|
||||||
})
|
})
|
||||||
+ addHook(hooks_configfile, 100, [] {
|
+ addHook(hooks_configfile, 100, [] {
|
||||||
@ -949,10 +899,27 @@ EX string racetimeformat(int t) {
|
|||||||
|
|
||||||
extern int playercfg;
|
extern int playercfg;
|
||||||
|
|
||||||
void track_chooser(string new_track) {
|
EX void load_official_track() {
|
||||||
|
fhstream f("officials.data", "rb");
|
||||||
|
hread(f, f.vernum);
|
||||||
|
map<eLand, string> tracks;
|
||||||
|
hread(f, tracks);
|
||||||
|
eLand l = specialland;
|
||||||
|
if(!tracks.count(l)) {
|
||||||
|
println(hlog, "ERROR: no official track in the database");
|
||||||
|
throw hstream_exception();
|
||||||
|
}
|
||||||
|
string s = decompress_string(tracks[l]);
|
||||||
|
shstream sf(s);
|
||||||
|
mapstream::loadMap(sf);
|
||||||
|
cheater = autocheat = 0;
|
||||||
|
official_race = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void track_chooser(bool official) {
|
||||||
cmode = 0;
|
cmode = 0;
|
||||||
gamescreen(2);
|
gamescreen(2);
|
||||||
dialog::init(XLAT("Racing"));
|
dialog::init(XLAT(official ? "Official tracks" : "Generate a racing track"));
|
||||||
|
|
||||||
map<char, eLand> landmap;
|
map<char, eLand> landmap;
|
||||||
|
|
||||||
@ -961,25 +928,23 @@ void track_chooser(string new_track) {
|
|||||||
|
|
||||||
char let = 'a';
|
char let = 'a';
|
||||||
for(eLand l: race_lands) {
|
for(eLand l: race_lands) {
|
||||||
auto& ghs = race_ghosts[make_pair(new_track, modecode())];
|
|
||||||
const int LOST = 3600000;
|
|
||||||
int best = LOST;
|
|
||||||
if(ghs.count(l)) {
|
|
||||||
auto& gh = ghs[l];
|
|
||||||
for(auto& gc: gh) best = min(best, gc.result);
|
|
||||||
}
|
|
||||||
string s = (best == LOST) ? "" : racetimeformat(best);
|
|
||||||
landmap[let] = l;
|
landmap[let] = l;
|
||||||
|
string s = "";
|
||||||
|
if(best_scores.count(l))
|
||||||
|
s = racetimeformat(best_scores[l]);
|
||||||
dialog::addSelItem(XLAT1(linf[l].name), s, let++);
|
dialog::addSelItem(XLAT1(linf[l].name), s, let++);
|
||||||
dialog::add_action([l, new_track] () {
|
dialog::add_action([l, official] () {
|
||||||
stop_game();
|
stop_game();
|
||||||
multi::players = playercfg;
|
multi::players = playercfg;
|
||||||
if(!racing::on) switch_game_mode(rg::racing);
|
|
||||||
track_code = new_track;
|
|
||||||
specialland = l;
|
specialland = l;
|
||||||
// because of an earlier issue, some races in the Official track start with race_try = 1
|
if(official) {
|
||||||
race_try = among(l, laCaves, laWildWest, laHell, laTerracotta, laElementalWall, laDryForest, laDeadCaves) ? 1 : 0;
|
racing::on = false;
|
||||||
|
load_official_track();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!racing::on) switch_game_mode(rg::racing);
|
||||||
start_game();
|
start_game();
|
||||||
|
}
|
||||||
popScreenAll();
|
popScreenAll();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -987,7 +952,7 @@ void track_chooser(string new_track) {
|
|||||||
dialog::addBack();
|
dialog::addBack();
|
||||||
dialog::display();
|
dialog::display();
|
||||||
|
|
||||||
if(landmap.count(getcstat) && new_track == "OFFICIAL" && modecode() == 2)
|
if(landmap.count(getcstat) && official)
|
||||||
displayScore(landmap[getcstat]);
|
displayScore(landmap[getcstat]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1090,31 +1055,10 @@ void race_projection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int playercfg;
|
int playercfg;
|
||||||
bool editing_track;
|
|
||||||
string new_track;
|
|
||||||
|
|
||||||
/* struct race_configurer { */
|
/* struct race_configurer { */
|
||||||
|
|
||||||
void set_race_configurer() { editing_track = false; new_track = track_code; playercfg = multi::players; }
|
void set_race_configurer() { playercfg = multi::players; }
|
||||||
|
|
||||||
static string random_track_name() {
|
|
||||||
string s = "";
|
|
||||||
for(int a = 0; a < 4; a++) {
|
|
||||||
int u = rand() % 2;
|
|
||||||
if(u == 0)
|
|
||||||
s += "AEIOUY" [ rand() % 6];
|
|
||||||
s += "BCDFGHJKLMNPRSTVWZ" [ rand() % 18];
|
|
||||||
if(u == 1)
|
|
||||||
s += "AEIOUY" [ rand() % 6];
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string racecheck(int sym, int uni) {
|
|
||||||
if(uni >= 'A' && uni <= 'Z') return string("") + char(uni);
|
|
||||||
if(uni >= 'a' && uni <= 'z') return string("") + char(uni - 32);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alternate = false;
|
bool alternate = false;
|
||||||
|
|
||||||
@ -1133,7 +1077,6 @@ void race_projection() {
|
|||||||
if(!racing::on) switch_game_mode(rg::racing);
|
if(!racing::on) switch_game_mode(rg::racing);
|
||||||
racing::standard_centering = true;
|
racing::standard_centering = true;
|
||||||
launcher();
|
launcher();
|
||||||
track_code = "OFFICIAL";
|
|
||||||
start_game();
|
start_game();
|
||||||
popScreenAll();
|
popScreenAll();
|
||||||
});
|
});
|
||||||
@ -1179,22 +1122,11 @@ void race_projection() {
|
|||||||
|
|
||||||
dialog::init(XLAT("Racing"));
|
dialog::init(XLAT("Racing"));
|
||||||
|
|
||||||
if(false)
|
dialog::addItem(XLAT("play on an official track"), 'O');
|
||||||
dialog::addInfo(XLAT("Racing available only in unbounded worlds."), 0xFF0000);
|
dialog::add_action_push([/*this*/] () { track_chooser(true); });
|
||||||
else {
|
|
||||||
dialog::addItem(XLAT("select the track and start!"), 's');
|
dialog::addItem(XLAT("generate a random track"), 's');
|
||||||
dialog::add_action([/*this*/] () {
|
dialog::add_action_push([/*this*/] () { track_chooser(false); });
|
||||||
dynamicval<bool> so(shmup::on, true);
|
|
||||||
dynamicval<bool> ro(racing::on, true);
|
|
||||||
#if CAP_FILES
|
|
||||||
if(race_ghosts[make_pair(new_track, modecode())].empty())
|
|
||||||
read_ghosts(new_track, modecode());
|
|
||||||
else
|
|
||||||
println(hlog, "known ghosts: ", isize(race_ghosts[make_pair(new_track, modecode())]));
|
|
||||||
#endif
|
|
||||||
pushScreen([/*this*/] () { track_chooser(new_track); });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog::addBreak(100);
|
dialog::addBreak(100);
|
||||||
|
|
||||||
@ -1229,18 +1161,6 @@ void race_projection() {
|
|||||||
|
|
||||||
dialog::addBreak(100);
|
dialog::addBreak(100);
|
||||||
|
|
||||||
dialog::addSelItem(XLAT("track seed"), editing_track ? dialog::view_edited_string() : new_track, '/');
|
|
||||||
dialog::add_action([/*this*/] () {
|
|
||||||
editing_track = !editing_track;
|
|
||||||
if(editing_track) dialog::start_editing(new_track);
|
|
||||||
});
|
|
||||||
dialog::addItem(XLAT("play the official seed"), 'o');
|
|
||||||
dialog::add_action([/*this*/] () { new_track = "OFFICIAL"; });
|
|
||||||
dialog::addItem(XLAT("play a random seed"), 'r');
|
|
||||||
dialog::add_action([/*this*/] () { new_track = random_track_name(); });
|
|
||||||
|
|
||||||
dialog::addBreak(100);
|
|
||||||
|
|
||||||
dialog::addSelItem(XLAT("best scores to show as ghosts"), its(ghosts_to_show), 'c');
|
dialog::addSelItem(XLAT("best scores to show as ghosts"), its(ghosts_to_show), 'c');
|
||||||
dialog::add_action([]() { dialog::editNumber(ghosts_to_show, 0, 100, 1, 5, "best scores to show as ghosts", ""); });
|
dialog::add_action([]() { dialog::editNumber(ghosts_to_show, 0, 100, 1, 5, "best scores to show as ghosts", ""); });
|
||||||
|
|
||||||
@ -1268,12 +1188,8 @@ void race_projection() {
|
|||||||
dialog::display();
|
dialog::display();
|
||||||
|
|
||||||
keyhandler = [/*this*/] (int sym, int uni) {
|
keyhandler = [/*this*/] (int sym, int uni) {
|
||||||
if(editing_track) {
|
|
||||||
if(sym == SDLK_RETURN) sym = uni = '/';
|
|
||||||
if(dialog::handle_edit_string(sym, uni, racecheck)) return;
|
|
||||||
}
|
|
||||||
dialog::handleNavigation(sym, uni);
|
dialog::handleNavigation(sym, uni);
|
||||||
if(doexiton(sym, uni)) { if(editing_track) editing_track = false; else popScreen(); }
|
if(doexiton(sym, uni)) popScreen();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1333,8 +1249,7 @@ EX void race_won() {
|
|||||||
int losers = 0;
|
int losers = 0;
|
||||||
int place = 1;
|
int place = 1;
|
||||||
for(int i=0; i<multi::players; i++) if(race_finish_tick[i]) place++; else losers = 0;
|
for(int i=0; i<multi::players; i++) if(race_finish_tick[i]) place++; else losers = 0;
|
||||||
for(auto& ghost: ghostset()[specialland]) if(ghost.result < result) place++; else losers++;
|
for(auto& ghost: ghostset) if(ghost.result < result) place++; else losers++;
|
||||||
for(auto& ghost: oghostset()[specialland]) if(ghost.result < result) place++; else losers++;
|
|
||||||
|
|
||||||
if(place == 1 && losers) trophy[multi::cpid] = 0xFFD500FF;
|
if(place == 1 && losers) trophy[multi::cpid] = 0xFFD500FF;
|
||||||
if(place == 2) trophy[multi::cpid] = 0xFFFFC0FF;
|
if(place == 2) trophy[multi::cpid] = 0xFFFFC0FF;
|
||||||
@ -1345,7 +1260,7 @@ EX void race_won() {
|
|||||||
else
|
else
|
||||||
addMessage(XLAT("Finished the race in time %1!", racetimeformat(result)));
|
addMessage(XLAT("Finished the race in time %1!", racetimeformat(result)));
|
||||||
|
|
||||||
if(place == 1 && losers && official_race && isize(oghostset()[specialland]))
|
if(place == 1 && losers && official_race && isize(ghostset))
|
||||||
achievement_gain("RACEWON", rg::racing);
|
achievement_gain("RACEWON", rg::racing);
|
||||||
|
|
||||||
race_finish_tick[multi::cpid] = ticks;
|
race_finish_tick[multi::cpid] = ticks;
|
||||||
@ -1356,33 +1271,23 @@ EX void race_won() {
|
|||||||
part(*x, 0) >>= 2;
|
part(*x, 0) >>= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &subtrack = ghostset() [specialland];
|
if(true) {
|
||||||
|
auto &subtrack = ghostset;
|
||||||
int ngh = 0;
|
subtrack.emplace_back(ghost{gcs, result, VERNUM_HEX, time(NULL), current_history[multi::cpid]});
|
||||||
for(int i=0; i<isize(subtrack); i++) {
|
|
||||||
if(subtrack[i].checksum != race_checksum) {
|
|
||||||
println(hlog, format("wrong checksum: %x, should be %x", subtrack[i].checksum, race_checksum));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(i != ngh)
|
|
||||||
subtrack[ngh] = move(subtrack[i]);
|
|
||||||
ngh++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subtrack.resize(ngh);
|
|
||||||
|
|
||||||
subtrack.emplace_back(ghost{gcs, result, race_checksum, time(NULL), current_history[multi::cpid]});
|
|
||||||
sort(subtrack.begin(), subtrack.end(), [] (const ghost &g1, const ghost &g2) { return g1.result < g2.result; });
|
sort(subtrack.begin(), subtrack.end(), [] (const ghost &g1, const ghost &g2) { return g1.result < g2.result; });
|
||||||
if(isize(subtrack) > ghosts_to_save && ghosts_to_save > 0)
|
if(isize(subtrack) > ghosts_to_save && ghosts_to_save > 0)
|
||||||
subtrack.resize(ghosts_to_save);
|
subtrack.resize(ghosts_to_save);
|
||||||
#if CAP_FILES
|
}
|
||||||
if(ghosts_to_save > 0)
|
|
||||||
write_ghosts(track_code, modecode());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
if(official_race && !cheater) {
|
||||||
|
if(!best_scores.count(specialland))
|
||||||
|
best_scores[specialland] = result;
|
||||||
|
best_scores[specialland] = min(best_scores[specialland], result);
|
||||||
|
saveStats();
|
||||||
if(official_race) uploadScore();
|
if(official_race) uploadScore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void draw_ghost_at(ghost& ghost, cell *w, const shiftmatrix& V, ghostmoment& p) {
|
void draw_ghost_at(ghost& ghost, cell *w, const shiftmatrix& V, ghostmoment& p) {
|
||||||
dynamicval<charstyle> x(getcs(), ghost.cs);
|
dynamicval<charstyle> x(getcs(), ghost.cs);
|
||||||
@ -1408,7 +1313,7 @@ ghostmoment& get_ghostmoment(ghost& ghost) {
|
|||||||
|
|
||||||
void draw_ghost(ghost& ghost) {
|
void draw_ghost(ghost& ghost) {
|
||||||
auto& p = get_ghostmoment(ghost);
|
auto& p = get_ghostmoment(ghost);
|
||||||
cell *w = rti[p.where_id].c;
|
cell *w = p.where_cell;
|
||||||
if(!gmatrix.count(w)) return;
|
if(!gmatrix.count(w)) return;
|
||||||
draw_ghost_at(ghost, w, get_ghostmoment_matrix(p), p);
|
draw_ghost_at(ghost, w, get_ghostmoment_matrix(p), p);
|
||||||
}
|
}
|
||||||
@ -1428,8 +1333,7 @@ EX int get_percentage(int i) {
|
|||||||
|
|
||||||
void draw_ghost_state(ghost& ghost) {
|
void draw_ghost_state(ghost& ghost) {
|
||||||
auto& p = get_ghostmoment(ghost);
|
auto& p = get_ghostmoment(ghost);
|
||||||
if(p.where_id >= isize(rti)) return;
|
cell *w = p.where_cell;
|
||||||
cell *w = rti[p.where_id].c;
|
|
||||||
ld result = ghost_finished(ghost) ? 100 : get_percentage(w);
|
ld result = ghost_finished(ghost) ? 100 : get_percentage(w);
|
||||||
draw_ghost_at(ghost, w, racerel(result), p);
|
draw_ghost_at(ghost, w, racerel(result), p);
|
||||||
}
|
}
|
||||||
@ -1450,8 +1354,7 @@ EX void drawStats() {
|
|||||||
}
|
}
|
||||||
queuecurve(shiftless(Id), 0xFFFFFFFF, 0, PPR::ZERO);
|
queuecurve(shiftless(Id), 0xFFFFFFFF, 0, PPR::ZERO);
|
||||||
|
|
||||||
for(auto& ghost: ghostset()[specialland]) draw_ghost_state(ghost);
|
for(auto& ghost: ghostset) draw_ghost_state(ghost);
|
||||||
for(auto& ghost: oghostset()[specialland]) draw_ghost_state(ghost);
|
|
||||||
|
|
||||||
for(int i=0; i<multi::players; i++) {
|
for(int i=0; i<multi::players; i++) {
|
||||||
dynamicval<int> d(multi::cpid, i);
|
dynamicval<int> d(multi::cpid, i);
|
||||||
@ -1489,14 +1392,8 @@ EX void markers() {
|
|||||||
its(cd), 0x10101 * int(128 - 100 * sintick(150)));
|
its(cd), 0x10101 * int(128 - 100 * sintick(150)));
|
||||||
addauraspecial(H, 0xFFD500, 0);
|
addauraspecial(H, 0xFFD500, 0);
|
||||||
}
|
}
|
||||||
int ghosts_left = ghosts_to_show;
|
|
||||||
for(auto& ghost: ghostset()[specialland]) {
|
|
||||||
if(!ghosts_left) break;
|
|
||||||
ghosts_left--;
|
|
||||||
draw_ghost(ghost);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto& ghost: oghostset()[specialland])
|
for(auto& ghost: ghostset)
|
||||||
draw_ghost(ghost);
|
draw_ghost(ghost);
|
||||||
|
|
||||||
if(gmatrix.count(track[0])) {
|
if(gmatrix.count(track[0])) {
|
||||||
|
Loading…
Reference in New Issue
Block a user