1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-01 03:46:16 +00:00

Bringris 1.3

This commit is contained in:
Zeno Rogue 2020-11-01 11:36:02 +01:00
parent 9f5da212a7
commit 50474abae1

View File

@ -3,7 +3,7 @@
#ifdef BRINGRIS #ifdef BRINGRIS
#define CUSTOM_CAPTION "Bringris 1.2" #define CUSTOM_CAPTION "Bringris 1.3"
#define MAXMDIM 4 #define MAXMDIM 4
@ -41,6 +41,9 @@
#else #else
#include "../hyper.cpp" #include "../hyper.cpp"
#endif #endif
#include "subquotient.cpp"
#define SUBQ
#endif #endif
#include "../hyper.h" #include "../hyper.h"
@ -49,11 +52,19 @@
namespace hr { namespace hr {
namespace subquotient {
#ifndef SUBQ
void create_subquotient(int qty = -1, int id = 0);
#endif
extern eGeometry gSubquotient;
}
namespace bringris { namespace bringris {
struct bgeometry { struct bgeometry {
string name; string name;
string cap; string cap;
flagtype flags;
reaction_t create; reaction_t create;
}; };
@ -84,6 +95,8 @@ vector<cell*> level;
vector<cell*> out_level; vector<cell*> out_level;
map<cell*, int> center_distance;
bool pro_game; bool pro_game;
int well_size = 10; int well_size = 10;
@ -114,8 +127,25 @@ enum eState {
eState state = tsPreGame; eState state = tsPreGame;
constexpr flagtype HYPERBOLIC = 1;
constexpr flagtype ORBIFOLD = 2;
constexpr flagtype SECRET = 4;
constexpr flagtype NONORIENTABLE = 8;
constexpr flagtype SPHERICAL = 16;
constexpr flagtype EUCLIDEAN = 32;
constexpr flagtype SUBQUOTIENT = 64;
constexpr flagtype HDUAL = 128;
constexpr flagtype BOUNDED_WELL = 256;
constexpr flagtype ASYMMETRIC_ONLY = 512;
constexpr flagtype FLAT_ONLY = 1024;
cell *get_center();
cell *shift_block_target(int dir);
void shift_block(int dir, bool camera_only = false);
void rotate_block(int dir, bool camera_only = false);
vector<bgeometry> bgeoms = { vector<bgeometry> bgeoms = {
{"Bring surface", "the original Bringris geometry", [] { {"Bring surface", "the original Bringris geometry", HYPERBOLIC, [] {
using namespace fieldpattern; using namespace fieldpattern;
current_extra = 2; current_extra = 2;
auto& gxcur = fgeomextras[current_extra]; auto& gxcur = fgeomextras[current_extra];
@ -132,7 +162,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = false; rotate_allowed = false;
}}, }},
{"torus", "Euclidean level geometry", [] { {"torus", "Euclidean level geometry", EUCLIDEAN, [] {
auto& T0 = euc::eu_input.user_axes; auto& T0 = euc::eu_input.user_axes;
T0[0][0] = 5; T0[0][0] = 5;
T0[0][1] = 0; T0[0][1] = 0;
@ -146,7 +176,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = true; rotate_allowed = true;
}}, }},
{"Cube", "spherical level geometry", [] { {"Cube", "spherical level geometry", SPHERICAL, [] {
set_geometry(gSmallSphere); set_geometry(gSmallSphere);
set_variation(eVariation::pure); set_variation(eVariation::pure);
set_geometry(gProduct); set_geometry(gProduct);
@ -154,7 +184,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = false; rotate_allowed = false;
}}, }},
{"Klein bottle", "non-orientable manifold", [] { {"Klein bottle", "non-orientable manifold", EUCLIDEAN | NONORIENTABLE, [] {
auto& T0 = euc::eu_input.user_axes; auto& T0 = euc::eu_input.user_axes;
T0[0][0] = 5; T0[0][0] = 5;
T0[0][1] = 0; T0[0][1] = 0;
@ -168,7 +198,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = true; rotate_allowed = true;
}}, }},
{"pentagons", "different tiles on the Bring surface", [] { {"pentagons", "different tiles on the Bring surface", HYPERBOLIC | HDUAL, [] {
using namespace fieldpattern; using namespace fieldpattern;
current_extra = 2; current_extra = 2;
auto& gxcur = fgeomextras[current_extra]; auto& gxcur = fgeomextras[current_extra];
@ -182,7 +212,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = false; rotate_allowed = false;
}}, }},
{"double cube", "six squares around a vertex", [] { {"double cube", "six squares around a vertex", HYPERBOLIC, [] {
using namespace fieldpattern; using namespace fieldpattern;
current_extra = 3; current_extra = 3;
auto& gxcur = fgeomextras[current_extra]; auto& gxcur = fgeomextras[current_extra];
@ -199,7 +229,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = true; rotate_allowed = true;
}}, }},
{"30/6", "six squares around a vertex", [] { {"30/6", "six squares around a vertex", HYPERBOLIC, [] {
using namespace fieldpattern; using namespace fieldpattern;
current_extra = 3; current_extra = 3;
auto& gxcur = fgeomextras[current_extra]; auto& gxcur = fgeomextras[current_extra];
@ -216,7 +246,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = true; rotate_allowed = true;
}}, }},
{"42", "seven squares around a vertex", [] { {"42", "seven squares around a vertex", HYPERBOLIC, [] {
using namespace fieldpattern; using namespace fieldpattern;
current_extra = 4; current_extra = 4;
auto& gxcur = fgeomextras[current_extra]; auto& gxcur = fgeomextras[current_extra];
@ -233,7 +263,7 @@ vector<bgeometry> bgeoms = {
rotate_allowed = false; rotate_allowed = false;
}}, }},
{"bounded well", "five squares around a vertex", [] { {"bounded well", "five squares around a vertex", BOUNDED_WELL, [] {
set_geometry(g45); set_geometry(g45);
gp::param = gp::loc(1, 1); gp::param = gp::loc(1, 1);
set_variation(eVariation::unrectified); set_variation(eVariation::unrectified);
@ -243,7 +273,18 @@ vector<bgeometry> bgeoms = {
rotate_allowed = false; rotate_allowed = false;
}}, }},
{"giant", "like Bring's but much larger", [] { {"mirrored Bring", "hyperbolic and non-orientable", HYPERBOLIC | NONORIENTABLE | ASYMMETRIC_ONLY, [] {
set_geometry(gBring);
gp::param = gp::loc(1, 1);
set_variation(eVariation::unrectified);
start_game();
subquotient::create_subquotient(2);
set_geometry(gProduct);
max_piece = 4;
rotate_allowed = false;
}},
{"giant", "like mirrored Bring but much larger", HYPERBOLIC | NONORIENTABLE, [] {
using namespace fieldpattern; using namespace fieldpattern;
current_extra = 2; current_extra = 2;
auto& gxcur = fgeomextras[current_extra]; auto& gxcur = fgeomextras[current_extra];
@ -254,14 +295,33 @@ vector<bgeometry> bgeoms = {
gp::param = gp::loc(1, 1); gp::param = gp::loc(1, 1);
set_variation(eVariation::unrectified); set_variation(eVariation::unrectified);
subquotient::create_subquotient(2);
set_geometry(gProduct); set_geometry(gProduct);
max_piece = 5; max_piece = 5;
rotate_allowed = false; rotate_allowed = false;
well_size = 5; well_size = 6;
}}, }},
{"torus: shear", "Nil geometry: are you sure you want this?", [] { {"orbifold", "one fifth of the giant", HYPERBOLIC | NONORIENTABLE | ORBIFOLD, [] {
using namespace fieldpattern;
current_extra = 2;
auto& gxcur = fgeomextras[current_extra];
while(isize(gxcur.primes) < 1) nextPrime(gxcur);
fgeomextras[current_extra].current_prime_id = 1;
enableFieldChange();
set_geometry(gFieldQuotient);
gp::param = gp::loc(1, 1);
set_variation(eVariation::unrectified);
subquotient::create_subquotient(10);
set_geometry(gProduct);
max_piece = 4;
rotate_allowed = false;
}},
{"torus: shear", "Nil geometry: are you sure you want this?", SECRET, [] {
nilv::nilperiod = make_array(5, 0, 5); nilv::nilperiod = make_array(5, 0, 5);
// nilv::set_flags(); // nilv::set_flags();
set_geometry(gNil); set_geometry(gNil);
@ -269,7 +329,8 @@ vector<bgeometry> bgeoms = {
rotate_allowed = false; rotate_allowed = false;
}}, }},
{"torus: Arnold's Cat", "Solv geometry: flat shapes are crazy enough", [] { #if CAP_SOLV
{"torus: Arnold's Cat", "Solv geometry: flat shapes are crazy enough", SECRET | FLAT_ONLY, [] {
asonov::period_xy = 5; asonov::period_xy = 5;
asonov::period_z = 0; asonov::period_z = 0;
asonov::set_flags(); asonov::set_flags();
@ -277,6 +338,7 @@ vector<bgeometry> bgeoms = {
max_piece = 2; max_piece = 2;
rotate_allowed = false; rotate_allowed = false;
}}, }},
#endif
}; };
void create_game(); void create_game();
@ -295,6 +357,8 @@ void enable_bgeom(int b) {
enable_bgeom(); enable_bgeom();
} }
flagtype bflags() { return bgeoms[bgeom].flags; }
using code_t = vector<pair<int, int>>; using code_t = vector<pair<int, int>>;
struct piecedata { struct piecedata {
@ -380,6 +444,20 @@ cellwalker flatspin(cellwalker cw, int i) {
return cw; return cw;
} }
int add_dir(cellwalker orig, int i, int sym = 0) {
if(prod) {
if(i >= orig.at->type-2) {
if(sym&2) i ^= (orig.at->type-1) ^ (orig.at->type-2);
return i;
}
else {
if(sym&1) i = -i;
return flatspin(orig, i).spin;
}
}
return i;
}
cellwalker add(cellwalker orig, int i, int sym = 0) { cellwalker add(cellwalker orig, int i, int sym = 0) {
if(prod) { if(prod) {
if(i >= orig.at->type-2) { if(i >= orig.at->type-2) {
@ -402,21 +480,14 @@ vector<cellwalker> build_from(const code_t& code, cellwalker start, int sym = 0)
return all; return all;
} }
vector<transmatrix> build_shape_matrices(const vector<cellwalker>& cells) { vector<transmatrix> build_shape_matrices(const code_t& code, cellwalker start, int sym = 0) {
vector<transmatrix> all = {Id}; vector<cellwalker> all = {start};
all.reserve(isize(cells)); vector<transmatrix> allm = {Id};
for(int i=1; i<isize(cells); i++) { for(auto c: code) {
for(int j=0; j<i; j++) { all.push_back(add(all[c.first], c.second, sym));
int nei = neighborId(cells[j].at, cells[i].at); allm.push_back(allm[c.first] * currentmap->adj(all[c.first].at, add_dir(all[c.first], c.second, sym)));
if(nei != -1) {
transmatrix T = all[j];
T = T * currentmap->adj(cells[j].at, nei);
all.push_back(T);
break;
}
}
} }
return all; return allm;
} }
int penalty(const vector<cellwalker>& shape, const code_t& code) { int penalty(const vector<cellwalker>& shape, const code_t& code) {
@ -477,7 +548,13 @@ void generate_shapes_rec(vector<cellwalker>& sofar, code_t& code, int cnt) {
id++; id++;
} }
int syms = 0; int syms = 0;
for(int i: {0,1,2,3}) if(builds(sofar, code, i)) syms++; bool invalid = false;
for(int i: {0,1,2,3}) if(builds(sofar, code, i)) {
syms++;
if((bflags() & ASYMMETRIC_ONLY) && i == 1 && cnt >= 4)
invalid = true;
}
if(invalid) return;
bool vertical = true; bool vertical = true;
for(auto c: code) if(c.second < 4) vertical = false; for(auto c: code) if(c.second < 4) vertical = false;
if(vertical) syms *= 2; if(vertical) syms *= 2;
@ -499,7 +576,7 @@ void generate_shapes_rec(vector<cellwalker>& sofar, code_t& code, int cnt) {
} }
void generate_shapes(int cnt) { void generate_shapes(int cnt) {
vector<cellwalker> cws = { cwt }; vector<cellwalker> cws = { get_at(get_center(), -well_size - 1) };
code_t co = {}; code_t co = {};
generate_shapes_rec(cws, co, cnt); generate_shapes_rec(cws, co, cnt);
} }
@ -556,6 +633,11 @@ void remove_shape() {
bool shape_conflict(cellwalker cw) { bool shape_conflict(cellwalker cw) {
auto shape = build_from(piecelist[shape_id].code, cw); auto shape = build_from(piecelist[shape_id].code, cw);
/* self-conflict possible in the orbifold */
for(int i=0; i<isize(shape); i++) for(int j=0; j<i; j++)
if(shape[i].at == shape[j].at)
return true;
for(auto c: shape) for(auto c: shape)
if(c.at->wall) if(c.at->wall)
return true; return true;
@ -593,13 +675,26 @@ int choose_piece() {
} }
void reset_view(); void reset_view();
void set_tview(transmatrix T);
void new_piece() { void new_piece() {
at.at = get_at(well_center ? well_center : get_where(at.at).first, -well_size - 1); if(well_center && true) {
if(well_center) { again:
at.spin = 0; if(get_where(at.at).first != well_center) {
reset_view(); at.at = get_at(get_where(at.at).first, -well_size - 1);
int d = center_distance[get_where(at.at).first];
for(int i=0; i<4; i++) {
auto mov = get_where(shift_block_target(i)).first;
if(center_distance.count(mov) && center_distance[mov] < d) {
shift_block(i, true);
goto again;
}
}
println(hlog, "failed to recenter");
}
while(at.spin) rotate_block(1, true);
} }
at.at = get_at(get_where(at.at).first, -well_size - 1);
shape_id = next_shape_id; shape_id = next_shape_id;
next_shape_id = choose_piece(); next_shape_id = choose_piece();
if(shape_conflict(at)) { if(shape_conflict(at)) {
@ -765,7 +860,7 @@ ld move_dist;
void set_view() { void set_view() {
move_dist = hdist0(currentmap->adj(cwt.at, 0) * C0); move_dist = hdist0(currentmap->adj(cwt.at, 0) * C0);
if(in_h2xe() && PURE) { if(in_h2xe() && PURE && S3 == 4) {
ld dist = PIU(hdist0(get_corner_position(currentmap->gamestart(), 0))); ld dist = PIU(hdist0(get_corner_position(currentmap->gamestart(), 0)));
dist -= 1e-4; dist -= 1e-4;
move_dist = PIU(hdist(get_corner_position(currentmap->gamestart(), 0), get_corner_position(currentmap->gamestart(), 1))); move_dist = PIU(hdist(get_corner_position(currentmap->gamestart(), 0), get_corner_position(currentmap->gamestart(), 1)));
@ -774,6 +869,8 @@ void set_view() {
} }
if(in_h2xe() && UNRECTIFIED) if(in_h2xe() && UNRECTIFIED)
tView = spin(135*degree) * tView; tView = spin(135*degree) * tView;
if(in_h2xe() && S7 == 4)
tView = spin(90*degree) * tView;
if(in_s2xe()) if(in_s2xe())
tView = spin(90*degree) * tView; tView = spin(90*degree) * tView;
if(in_e2xe()) if(in_e2xe())
@ -804,19 +901,19 @@ void set_tview(transmatrix T) {
when_t = ticks + turn_animation; when_t = ticks + turn_animation;
} }
void rotate_block(int d) { void rotate_block(int d, bool camera_only) {
if(!rotate_allowed) { if(!rotate_allowed && !camera_only) {
playSound(cwt.at, "hit-crush3"); playSound(cwt.at, "hit-crush3");
return; return;
} }
remove_shape(); if(!camera_only) remove_shape();
cellwalker at1 = flatspin(at, d); cellwalker at1 = flatspin(at, d);
if(!shape_conflict(at1)) { if(camera_only || !shape_conflict(at1)) {
at = at1; at = at1;
set_tview(spin(d*90*degree)); set_tview(spin(d*90*degree));
} }
else playSound(cwt.at, "hit-crush3"); else playSound(cwt.at, "hit-crush3");
draw_shape(); if(!camera_only) draw_shape();
} }
int nilmap(int dir) { int nilmap(int dir) {
@ -831,11 +928,15 @@ int nilmap(int dir) {
exit(1); exit(1);
} }
void shift_block(int dir) { cell *shift_block_target(int dir) {
return flatspin(at, dir).cpeek();
}
void shift_block(int dir, bool camera_only) {
int t = currentmap->gamestart()->type; int t = currentmap->gamestart()->type;
if(prod) t -= 2; if(prod) t -= 2;
remove_shape(); if(!camera_only) remove_shape();
cellwalker at1; cellwalker at1;
@ -860,7 +961,7 @@ void shift_block(int dir) {
ld angle = dir * 90 * degree; ld angle = dir * 90 * degree;
if(!shape_conflict(at1)) { if(camera_only || !shape_conflict(at1)) {
// playSound(cwt.at, "hit-crush1"); // playSound(cwt.at, "hit-crush1");
at = at1; at = at1;
if(solnil) { if(solnil) {
@ -869,6 +970,7 @@ void shift_block(int dir) {
} }
else else
set_tview(spin(-angle) * ypush(-move_dist) * spin(angle)); set_tview(spin(-angle) * ypush(-move_dist) * spin(angle));
if(!camera_only) draw_shape();
} }
else playSound(cwt.at, "hit-crush3"); else playSound(cwt.at, "hit-crush3");
} }
@ -943,7 +1045,7 @@ void draw_piece(int zlev, int id) {
sightranges[geometry] *= 100; sightranges[geometry] *= 100;
initquickqueue(); initquickqueue();
auto shape = build_from(piecelist[id].code, at); auto shape = build_from(piecelist[id].code, at);
auto matrices = build_shape_matrices(shape); auto matrices = build_shape_matrices(piecelist[id].code, at);
auto where_at = get_where(at.at); auto where_at = get_where(at.at);
@ -1126,7 +1228,8 @@ void geometry_menu() {
dialog::addInfo(bgeoms[i].cap); dialog::addInfo(bgeoms[i].cap);
dialog::items.back().key = 'a' + i; dialog::items.back().key = 'a' + i;
dialog::addBreak(50); dialog::addBreak(50);
if(i >= isize(bgeoms) && bgeom != i) { if(i == bgeom) bgeoms[i].flags &= ~SECRET;
if(bgeoms[i].flags & SECRET) {
dialog::items.pop_back(); dialog::items.pop_back();
dialog::items.pop_back(); dialog::items.pop_back();
dialog::items.pop_back(); dialog::items.pop_back();
@ -1140,6 +1243,13 @@ void geometry_menu() {
create_game(); create_game();
state = tsPreGame; state = tsPreGame;
}); });
if(bflags() & ASYMMETRIC_ONLY)
dialog::addInfo("(only asymmetric large pieces)");
else if(bflags() & FLAT_ONLY)
dialog::addInfo("(only flat pieces)");
else
dialog::addBreak(100);
dialog::addBreak(100); dialog::addBreak(100);
dialog::addBack(); dialog::addBack();
dialog::display(); dialog::display();
@ -1249,6 +1359,7 @@ void run() {
ld alpha = atan2(Tspin*xpush0(1)); ld alpha = atan2(Tspin*xpush0(1));
pView = spin(alpha * part) * gpushxto0(direct_exp(vec*part)) * pView; pView = spin(alpha * part) * gpushxto0(direct_exp(vec*part)) * pView;
fixmatrix(pView); fixmatrix(pView);
View = tView;
smooth = inverse(pView) * cview().T; smooth = inverse(pView) * cview().T;
// println(hlog, "smooth = ", smooth); // println(hlog, "smooth = ", smooth);
} }
@ -1480,8 +1591,7 @@ void start_new_game() {
} }
void get_level() { void get_level() {
well_center = nullptr; if(bflags() & BOUNDED_WELL) {
if(geometry == g45) {
set<cell*> all; set<cell*> all;
well_center = currentmap->gamestart(); well_center = currentmap->gamestart();
all.insert(well_center); all.insert(well_center);
@ -1516,17 +1626,49 @@ void get_level() {
for(auto c: all_ext) for(auto c: all_ext)
out_level.push_back(c); out_level.push_back(c);
} }
else else {
level = currentmap->allcells(); level = currentmap->allcells();
if(bflags() & ORBIFOLD) {
vector<cell*> clist;
set<cell*> visited;
auto visit = [&] (cell *c) {
if(!visited.count(c))
visited.insert(c),
clist.push_back(c);
};
for(auto c: level) if(isNeighbor(c, c)) visit(c);
for(int i=0; i<isize(clist); i++)
for(int j=0; j<clist[i]->type; j++)
visit(clist[i]->cmove(j));
well_center = clist.back();
}
}
if(well_center) {
vector<cell*> visited;
set<cell*> all;
for(auto l: level) all.insert(l);
auto visit = [&] (cell *c, int d) {
if(all.count(c) && !center_distance.count(c))
center_distance[c] = d,
visited.push_back(c);
};
visit(well_center, 0);
for(int i=0; i<isize(visited); i++)
for(int j=0; j<visited[i]->type; j++)
visit(visited[i]->move(j), center_distance[visited[i]] + 1);
}
} }
void create_game() { void create_game() {
level.clear();
out_level.clear();
well_center = nullptr;
if(!prod && !solnil) { if(!prod && !solnil) {
println(hlog, "need product or Solnil geometry"); println(hlog, "need product or Solnil geometry");
exit(1); exit(1);
} }
if(nil) { if(nil) {
level.clear();
for(int x=0; x<5; x++) for(int x=0; x<5; x++)
for(int y=0; y<5; y++) for(int y=0; y<5; y++)
level.push_back(nilv::get_heptagon_at(nilv::mvec(x, 0, y))->c7); level.push_back(nilv::get_heptagon_at(nilv::mvec(x, 0, y))->c7);
@ -1565,6 +1707,8 @@ void create_game() {
start_new_game(); start_new_game();
state = tsPreGame; state = tsPreGame;
vid.axes3 = false;
} }
void init_all() { void init_all() {