Merge branch 'zenorogue:master' into main

This commit is contained in:
Charlotte Peppers 2022-03-06 17:10:14 -07:00 committed by GitHub
commit 6117d626d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 746 additions and 168 deletions

View File

@ -307,7 +307,7 @@ EX void achievement_collection2(eItem it, int q) {
if(it == itHolyGrail) {
if(q == 1) achievement_gain("GRAIL2");
if(PURE && geometry == gNormal)
achievement_gain("GRAILH", rg::special_geometry);
achievement_gain("GRAILH", rg::special_geometry_nicewalls);
#if CAP_CRYSTAL
if(PURE && cryst && ginf[gCrystal].sides == 8 && ginf[gCrystal].vertex == 4 && !crystal::used_compass_inside)
achievement_gain("GRAIL4D", rg::special_geometry);

View File

@ -24,6 +24,11 @@ EX int newRoundTableRadius() {
}
#if CAP_COMPLEX2
/** should we generate 'Castle Anthrax' instead of Camelot (an infinite sequence of horocyclic Camelot-likes */
EX bool anthrax() {
return ls::single() && hyperbolic && !cryst;
}
EX int getAnthraxData(cell *c, bool b) {
int d = celldistAlt(c);
int rad = 28 + 3 * camelot::anthraxBonus;
@ -62,7 +67,7 @@ EX int celldistAltRelative(cell *c) {
return celldist(c) - 3;
}
#if CAP_COMPLEX2
if(ls::single()) return getAnthraxData(c, false);
if(anthrax()) return getAnthraxData(c, false);
#endif
return celldistAlt(c) - roundTableRadius(c);
}
@ -1777,7 +1782,7 @@ EX eMonster camelot_monster() {
EX void buildCamelot(cell *c) {
int d = celldistAltRelative(c);
if(ls::single() || (d <= 14 && roundTableRadius(c) > 20)) {
if(anthrax() || (d <= 14 && roundTableRadius(c) > 20)) {
gen_alt(c);
preventbarriers(c);
if(d == 10) {
@ -1817,7 +1822,8 @@ EX void buildCamelot(cell *c) {
}
}
if(d == 0) c->wall = waRoundTable;
if(celldistAlt(c) == 0 && !ls::single()) c->item = itHolyGrail;
if(celldistAlt(c) == 0 && !anthrax()) println(hlog, "placed Holy Grail on ", c);
if(celldistAlt(c) == 0 && !anthrax()) c->item = itHolyGrail;
if(d < 0 && hrand(7000) <= 10 + items[itHolyGrail] * 5)
c->monst = camelot_monster();
if(d == 1) {
@ -1829,12 +1835,12 @@ EX void buildCamelot(cell *c) {
if(c->move(i) && celldistAltRelative(c->move(i)) < d)
c->mondir = (i+3) % 6;
}
if(ls::single() && d >= 2 && d <= 8 && hrand(1000) < 10)
if(anthrax() && d >= 2 && d <= 8 && hrand(1000) < 10)
c->item = itOrbSafety;
if(d == 5 && ls::single())
if(d == 5 && anthrax())
c->item = itGreenStone;
if(d <= 10) c->land = laCamelot;
if(d > 10 && !eubinary && !ls::single()) {
if(d > 10 && !eubinary && !anthrax()) {
setland(c, eLand(altmap::orig_land(c->master->alt->alt)));
if(c->land == laNone) printf("Camelot\n"); // NONEDEBUG
}

View File

@ -1293,6 +1293,12 @@ EX vector<cell*> build_shortest_path(cell *c1, cell *c2) {
}
EX void clearCellMemory() {
#if MAXMDIM >= 4
if(intra::in) {
intra::erase_all_maps();
return;
}
#endif
for(int i=0; i<isize(allmaps); i++)
if(allmaps[i])
delete allmaps[i];

View File

@ -4553,3 +4553,25 @@ Other:
2021-12-13 22:32 Update 12.0j:
- fixed the victory leaderboards (turns/time to Orb of Yendor/Hyperstones)
- fixed the starting land selection feature for equidistant-based lands
2022-02-03 01:07 Update 12.0k:
Orb-related fixes:
- Lazurite Figurines no longer appear immediately after using an Orb of Safety
- Orb of Chaos is now forbidden in the Dungeon
- Stunning from the Orb of Chaos now destroys birds/shadows just like the other sources of stunning
- Shadows are now affected by the Orb of Beauty
- Stunning the Shadow now temporarily destroys it
- draining Orb of Choice now happens after gaining the Purity extras
- Orb of Slaying now works against big trees
- draw fishtails on friends with empathy
Achievement fixes:
- fixed racing (official tracks are generated again and acknowledged)
- Princess Challenge and Heptagonal Knight achievements should be fixed
Geometry:
- more distinct colors in 'eight domains'
- improvements to tree rule generator
- Goldberg-Coxeter improvements (larger limits supported, warn if outside of the supported limits, some are fixed)
- fixed fat edges in some H3 honeycombs
- fixed some bugs with Multi-dimensional Crystal quotient space

View File

@ -152,6 +152,7 @@ struct int_setting : public setting {
void show_edit_option(char key) override;
cld get_cld() override { return *value; }
int_setting *editable(int min_value, int max_value, ld step, string menu_item_name, string help_text, char key) {
this->is_editable = true;
this->min_value = min_value;
this->max_value = max_value;
this->menu_item_name = menu_item_name;
@ -2890,7 +2891,7 @@ void list_setting::show_edit_option(char key) {
int q = isize(options);
for(int i=0; i<q; i++) {
dialog::addBoolItem(XLAT(options[i].first), get_value() == i, 'a'+i);
dialog::add_action([this, i] { set_value(i); popScreen(); });
dialog::add_action([this, i] { set_value(i); if(reaction) reaction(); popScreen(); });
dialog::addBreak(100);
if(options[i].second != "") {
dialog::addHelp(XLAT(options[i].second));

View File

@ -392,11 +392,17 @@ EX void full_rotate_camera(int dir, ld val) {
else if(GDIM == 3) {
val *= camera_rot_speed;
if(third_person_rotation) shift_view(ctangent(2, -third_person_rotation)), didsomething = true, playermoved = false;
if(keep_vertical()) {
ld max_angle = quarter_circle - 1e-4;
if(walking::on && dir == 1) {
max_angle /= degree;
walking::eye_angle += val * walking::eye_angle_scale / degree;
if(walking::eye_angle > max_angle) walking::eye_angle = max_angle;
if(walking::eye_angle < -max_angle) walking::eye_angle = -max_angle;
}
else if(keep_vertical()) {
hyperpoint vv = vertical_vector();
ld alpha = -atan2(vv[2], vv[1]);
rotate_view(cspin(2, 1, alpha));
ld max_angle = quarter_circle - 1e-4;
if(dir == 1 && alpha + val > max_angle)
val = max_angle - alpha;
if(dir == 1 && alpha + val < -max_angle)

View File

@ -686,8 +686,8 @@ struct hrmap_crystal : hrmap_standard {
}
transmatrix relative_matrixh(heptagon *h2, heptagon *h1, const hyperpoint& hint) override {
if(!crystal3()) return hrmap::relative_matrixh(h2, h1, hint);
return relative_matrix(h2->c7, h1->c7, hint);
if(!crystal3()) return hrmap_standard::relative_matrixh(h2, h1, hint);
return relative_matrixc(h2->c7, h1->c7, hint);
}
#endif
};

View File

@ -373,12 +373,16 @@ EX namespace fake {
transmatrix ray_iadj(cell *c, int i) override {
if(WDIM == 2)
return to_other_side(get_corner(c, i), get_corner(c, i+1));
#if MAXMDIM >= 4
if(PURE) return iadj(c, i);
auto& v = get_cellshape(c).faces_local[i];
hyperpoint h =
project_on_triangle(v[0], v[1], v[2]);
transmatrix T = rspintox(h);
return T * xpush(-2*hdist0(h)) * spintox(h);
#else
return Id;
#endif
}
};

View File

@ -1270,6 +1270,7 @@ int read_geom_args() {
PHASEFROM(2);
set_variation(eVariation::warped);
}
#if MAXMDIM >= 4
else if(argis("-subcubes")) {
PHASEFROM(2);
stop_game();
@ -1301,6 +1302,7 @@ int read_geom_args() {
shift(); reg3::coxeter_param = argi();
}
#endif
#endif
#if CAP_FIELD
else if(argis("-fi")) {
fieldpattern::info();

View File

@ -744,10 +744,12 @@ void geometry_information::prepare_basics() {
base_distlimit = arb::current.range;
}
#if MAXMDIM >= 4
if(is_subcube_based(variation)) {
scalefactor /= reg3::subcube_count;
orbsize /= reg3::subcube_count;
}
#endif
if(scale_used()) {
scalefactor *= vid.creature_scale;
@ -1142,8 +1144,10 @@ EX string cgi_string() {
if(GOLDBERG_INV) V("GP", its(gp::param.first) + "," + its(gp::param.second));
if(IRREGULAR) V("IRR", its(irr::irrid));
#if MAXMDIM >= 4
if(is_subcube_based(variation)) V("SC", its(reg3::subcube_count));
if(variation == eVariation::coxeter) V("COX", its(reg3::coxeter_param));
#endif
#if CAP_ARCM
if(arcm::in()) V("ARCM", arcm::current.symbol);
@ -1198,6 +1202,12 @@ EX string cgi_string() {
return s;
}
#if MAXMDIM >= 4
#define IFINTRA(x,y) x
#else
#define IFINTRA(x,y) y
#endif
EX void check_cgi() {
string s = cgi_string();
@ -1207,7 +1217,7 @@ EX void check_cgi() {
if(fake::in()) fake::underlying_cgip->timestamp = ntimestamp;
if(arcm::alt_cgip) arcm::alt_cgip->timestamp = ntimestamp;
if(isize(cgis) > 4 && intra::data.empty()) {
if(isize(cgis) > 4 && IFINTRA(intra::data.empty(), true)) {
vector<pair<int, string>> timestamps;
for(auto& t: cgis) timestamps.emplace_back(-t.second.timestamp, t.first);
sort(timestamps.begin(), timestamps.end());

View File

@ -1162,6 +1162,7 @@ EX namespace gp {
return S3 == 3 ? XLAT("chamfered") : XLAT("expanded");
else if(GOLDBERG && param == loc(3, 0) && S3 == 3)
return XLAT("2x bitruncated");
#if MAXMDIM >= 4
else if(variation == eVariation::subcubes)
return XLAT("subcubed") + "(" + its(reg3::subcube_count) + ")";
else if(variation == eVariation::dual_subcubes)
@ -1170,6 +1171,7 @@ EX namespace gp {
return XLAT("bitruncated-subcubed") + "(" + its(reg3::subcube_count) + ")";
else if(variation == eVariation::coxeter)
return XLAT("subdivided") + "(" + its(reg3::coxeter_param) + ")";
#endif
else {
auto p = human_representation(param);
string s = "GP(" + its(p.first) + "," + its(p.second) + ")";

View File

@ -3808,10 +3808,12 @@ EX int get_darkval(cell *c, int d) {
const int darkval_kite[12] = {0, 2, 0, 2, 4, 4, 6, 6, 6, 6, 6, 6};
const int darkval_nil[8] = {6,6,0,3,6,6,0,3};
const int darkval_nih[11] = {0,2,0,2,4,6,6,6,6,6,6};
#if MAXMDIM >= 4
if(among(variation, eVariation::dual_subcubes, eVariation::bch, eVariation::bch_oct, eVariation::coxeter)) {
int v = reg3::get_face_vertex_count(c, d);
return v-3;
}
#endif
if(sphere) return darkval_s12[d];
if(euclid && S7 == 6) return darkval_e6[d];
if(euclid && S7 == 12) return darkval_e12[d];

View File

@ -13,8 +13,8 @@
#define _HYPER_H_
// version numbers
#define VER "12.0j"
#define VERNUM_HEX 0xA90A
#define VER "12.0k"
#define VERNUM_HEX 0xA90B
#include "sysconfig.h"

View File

@ -466,8 +466,8 @@ EX transmatrix to_other_side(hyperpoint h1, hyperpoint h2) {
/** @brief positive for a material vertex, 0 for ideal vertex, negative for ultra-ideal vertex */
EX ld material(const hyperpoint& h) {
if(sphere) return intval(h, Hypc);
else if(hyperbolic) return -intval(h, Hypc);
if(sphere || in_s2xe()) return intval(h, Hypc);
else if(hyperbolic || in_h2xe()) return -intval(h, Hypc);
else if(sl2) return h[2]*h[2] + h[3]*h[3] - h[0]*h[0] - h[1]*h[1];
else return h[LDIM];
}
@ -505,7 +505,7 @@ EX hyperpoint normalize(hyperpoint H) {
/** like normalize but makes (ultra)ideal points material */
EX hyperpoint ultra_normalize(hyperpoint H) {
if(material(H) <= 0) {
H[LDIM] = hypot_d(LDIM, H) + 1e-6;
H[LDIM] = hypot_d(LDIM, H) + 1e-10;
}
return normalize(H);
}
@ -760,7 +760,7 @@ EX hyperpoint parabolic13(hyperpoint h) {
EX transmatrix spintoc(const hyperpoint& H, int t, int f) {
transmatrix T = Id;
ld R = hypot(H[f], H[t]);
if(R >= 1e-12) {
if(R >= 1e-15) {
T[t][t] = +H[t]/R; T[t][f] = +H[f]/R;
T[f][t] = -H[f]/R; T[f][f] = +H[t]/R;
}
@ -774,7 +774,7 @@ EX transmatrix spintoc(const hyperpoint& H, int t, int f) {
EX transmatrix rspintoc(const hyperpoint& H, int t, int f) {
transmatrix T = Id;
ld R = hypot(H[f], H[t]);
if(R >= 1e-12) {
if(R >= 1e-15) {
T[t][t] = +H[t]/R; T[t][f] = -H[f]/R;
T[f][t] = +H[f]/R; T[f][f] = +H[t]/R;
}
@ -853,7 +853,7 @@ EX transmatrix ggpushxto0(const hyperpoint& H, ld co) {
return mscale(PIU(ggpushxto0(d.second, co)), d.first * co);
}
transmatrix res = Id;
if(sqhypot_d(GDIM, H) < 1e-12) return res;
if(sqhypot_d(GDIM, H) < 1e-16) return res;
ld fac = -curvature()/(H[LDIM]+1);
for(int i=0; i<GDIM; i++)
for(int j=0; j<GDIM; j++)

View File

@ -213,7 +213,7 @@ ld find_zlev(hyperpoint& H) {
if(spatial_graphics) {
ld zlev = zlevel(H);
if(zlev > 1-1e-6 && zlev < 1+1e-6) return 1;
if(zlev > 1-1e-9 && zlev < 1+1e-9) return 1;
H /= zlev;
return zlev;
}
@ -628,9 +628,9 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) {
ret[0] = -models::osin - H[0];
ld height = 0;
if(zlev != 1) {
if(abs(models::ocos) > 1e-5)
if(abs(models::ocos) > 1e-9)
height += H[1] * (pow(zlev, models::ocos) - 1);
if(abs(models::ocos) > 1e-5 && models::osin)
if(abs(models::ocos) > 1e-9 && models::osin)
height += H[0] * models::osin * (pow(zlev, models::ocos) - 1) / models::ocos;
else if(models::osin)
height += H[0] * models::osin * log(zlev);
@ -2013,6 +2013,8 @@ EX void optimizeview() {
fixmatrix(View);
callhooks(hooks_postoptimize);
walking::handle();
if(is_boundary(centerover))
centerover = c, View = oView;
else
@ -2756,7 +2758,7 @@ EX bool do_draw(cell *c) {
// do not display not fully generated cells, unless changing range allowed
if(c->mpdist > 7 && !allowChangeRange()) return false;
// in the Yendor Challenge, scrolling back is forbidden
if(c->cpdist > 7 && (yendor::on || isHaunted(cwt.at->land)) && !cheater && !autocheat) return false;
if(c->cpdist > get_sightrange() && (yendor::on || isHaunted(cwt.at->land)) && !cheater && !autocheat) return false;
return true;
}
@ -2907,7 +2909,9 @@ EX void shift_view(hyperpoint H) {
static bool recursive = false;
if(!recursive && intra::in) {
dynamicval<bool> r(recursive, true);
#if MAXMDIM >= 4
intra::shift_view_portal(H);
#endif
return;
}
View = get_shift_view_of(H, View);

308
intra.cpp
View File

@ -6,6 +6,7 @@ EX namespace intra {
EX bool in;
#if MAXMDIM >= 4
#if HDR
/** information per every space connected with intra-portals */
struct intra_data {
@ -68,9 +69,9 @@ hyperpoint portal_data::to_poco(hyperpoint h) const {
ld z = product_decompose(h).first;
h /= exp(z);
auto h1 = h;
h[0] = asin_auto(h1[1]);
h[2] = asin_auto_clamp(h1[0]);
h[1] = z;
h[2] = asin_auto_clamp(h1[0] / cos_auto(h[0]));
h[0] = asin_auto_clamp(h1[1] / cos_auto(h[2]));
h[3] = 1;
return h;
}
@ -90,6 +91,10 @@ hyperpoint portal_data::to_poco(hyperpoint h) const {
else {
h = T * h;
h /= h[3];
if(sphere)
h[2] /= sqrt(1+h[0]*h[0]+h[1]*h[1]);
if(hyperbolic)
h[2] /= sqrt(1-h[0]*h[0]-h[1]*h[1]);
return h;
}
}
@ -108,8 +113,8 @@ hyperpoint portal_data::from_poco(hyperpoint h) const {
}
else if(prod && kind == 0) {
auto h0 = h;
h[0] = sin_auto(h0[2]) * cos_auto(h0[0]);
h[1] = sin_auto(h0[0]);
h[0] = sin_auto(h0[2]);
h[1] = sin_auto(h0[0]) * cos_auto(h0[2]);
h[2] = cos_auto(h0[0]) * cos_auto(h0[2]);
h[3] = 1;
return iT * h * exp(h0[1]);
@ -125,13 +130,17 @@ hyperpoint portal_data::from_poco(hyperpoint h) const {
}
else {
h[3] = 1;
if(sphere)
h[2] *= sqrt(1+h[0]*h[0]+h[1]*h[1]);
if(hyperbolic)
h[2] *= sqrt(1-h[0]*h[0]-h[1]*h[1]);
return normalize(iT * h);
}
}
EX portal_data make_portal(cellwalker cw, int spin) {
if(debug_portal & 33)
println(hlog, "working in: ", full_geometry_name());
if(debug_portal & 289)
println(hlog, "working in: ", full_geometry_name(), " wall no ", cw.spin, "/", cw.at->type);
auto& ss = currentmap->get_cellshape(cw.at);
auto fac = ss.faces_local[cw.spin];
portal_data id;
@ -252,6 +261,23 @@ EX portal_data make_portal(cellwalker cw, int spin) {
println(hlog, "chosen edge is ", first, "--", second);
}
if(debug_portal & 256) {
println(hlog, "portal scale = ", id.scale);
auto res = [&] (ld x, ld y, ld z) {
hyperpoint h = hyperpoint(x, y, z, 1);
return id.from_poco(h);
};
for(int x=0; x<5; x++) {
println(hlog, "horizontal ", x, " = ", hdist(res(x*.1,0,0), res(x*.1+.001,0,0)));
println(hlog, "vertical ", x, " = ", hdist(res(x*.1,0,0), res(x*.1,0.001,0)));
println(hlog, "deep ", x, " = ", hdist(res(x*.1,0,0), res(x*.1,0,0.001)));
}
hyperpoint a = hyperpoint(.4, .2, .1, 1);
println(hlog, "a = ", a);
println(hlog, "b = ", id.from_poco(a));
println(hlog, "c = ", id.to_poco(id.from_poco(a)));
}
if(debug_portal & 1) {
for(auto p: fac) {
auto p2 = id.to_poco(p);
@ -497,7 +523,7 @@ EX void shift_view_portal(hyperpoint H) {
println(hlog, "maxv = ", maxv);
shift_view(H * maxv);
check_portal_movement();
shift_view(H * (1 - maxv));
shift_view_portal(H * (1 - maxv));
}
EX const connection_data* through_portal() {
@ -515,11 +541,9 @@ EX const connection_data* through_portal() {
EX void check_portal_movement() {
auto p = through_portal();
ld c = camera_speed;
if(p) {
ld eps = 1e-5;
c /= p->id1.scale;
anims::cycle_length /= p->id1.scale;
ld ss = pow(eps, -2);
array<hyperpoint, 4> ds; /* camera, forward, upward */
@ -579,9 +603,17 @@ EX void check_portal_movement() {
println(hlog, "goal: at = ", xds[0], " det = ", dsdet(xds), " bt = ", bt::minkowski_to_bt(xds[0]));
}
c *= p->id2.scale;
anims::cycle_length *= p->id2.scale;
camera_speed = c;
ld scale = p->id2.scale / p->id1.scale;
camera_speed *= scale;
anims::cycle_length *= scale;
#if CAP_VR
vrhr::absolute_unit_in_meters *= scale;
#endif
if(walking::eye_level != -1) walking::eye_level *= scale;
walking::floor_dir = -1;
walking::on_floor_of = nullptr;
}
}
@ -595,7 +627,7 @@ void erase_unconnected(cellwalker cw) {
int edit_spin;
void show_portals() {
EX void show_portals() {
gamescreen(1);
dialog::init(XLAT("manage portals"));
@ -665,6 +697,8 @@ void show_portals() {
}
}
walking::add_options();
dialog::display();
}
@ -729,6 +763,22 @@ EX void kill(int id) {
println(hlog, isize(to_remove), " connections and ", isize(to_erase_cell), " cells erased");
}
EX void erase_all_maps() {
println(hlog, "erase_all_maps called");
data[current].gd.storegame();
in = false;
for(int i=0; i<isize(data); i++) {
current = i;
ginf[gProduct] = data[i].gi;
data[i].gd.restoregame();
clearCellMemory();
}
intra_id.clear();
connections.clear();
data.clear();
full_sample_list.clear();
}
EX set<cell*> need_to_save;
EX void prepare_need_to_save() {
@ -764,8 +814,232 @@ auto hooks1 =
arg::shift(); int i = arg::argi(); be_ratio_edge(i);
})
+ arg::add3("-debug-portal", [] { arg::shift(); debug_portal = arg::argi(); });
#endif
EX }
}
EX namespace walking {
EX bool on;
EX bool auto_eyelevel;
EX int floor_dir = -1;
EX cell *on_floor_of = nullptr;
EX ld eye_level = 0.2174492;
EX ld eye_angle = 0;
EX ld eye_angle_scale = 1;
int ticks_end, ticks_last;
EX set<color_t> colors_of_floors;
EX bool isFloor(cell *c) {
if(!isWall(c)) return false;
if(colors_of_floors.empty()) return true;
if(c->wall != waWaxWall) return false;
return colors_of_floors.count(c->landparam);
}
EX void handle() {
if(!on) return;
if(floor_dir == -1 || on_floor_of != centerover) {
vector<int> choices;
for(int i=0; i<centerover->type; i++)
if(isFloor(centerover->cmove(i)))
choices.push_back(i);
if(sol && isize(choices) == 2) choices.pop_back();
if(isize(choices) == 1) {
on_floor_of = centerover;
floor_dir = choices[0];
}
else if(colors_of_floors.empty() && sn::in()) {
on_floor_of = centerover;
auto z = inverse(View) * C0;
switch(geometry) {
case gSol:
floor_dir = (z[2] > 0) ? 2 : 6;
return;
case gNIH:
floor_dir = (z[2] > 0) ? 5 : 4;
return;
case gSolN:
floor_dir = (z[2] > 0) ? 4 : 6;
return;
default: throw hr_exception("not solnihv");
}
}
else if(colors_of_floors.empty() && hyperbolic && bt::in()) {
auto z = bt::minkowski_to_bt(inverse(View) * C0);
on_floor_of = centerover;
floor_dir = z[2] > 0 ? bt::updir() : 0;
println(hlog, "set floor_dir to ", floor_dir);
}
else {
println(hlog, "there are ", isize(choices), " choices for floor_dir");
if(!on_floor_of) return;
}
}
struct face {
hyperpoint h0, hx, hy;
};
transmatrix ToOld = currentmap->relative_matrix(on_floor_of, centerover, C0);
auto& csh = currentmap->get_cellshape(on_floor_of);
face f;
f.h0 = ToOld * csh.faces_local[floor_dir][0];
f.hx = ToOld * csh.faces_local[floor_dir][1];
f.hy = ToOld * csh.faces_local[floor_dir][2];
auto find_nearest = [&] (const face& fac, hyperpoint at) {
if(sol) { at[2] = fac.h0[2]; return at; }
else if(hyperbolic && bt::in()) {
auto z = bt::minkowski_to_bt(at);
z[2] = bt::minkowski_to_bt(fac.h0)[2];
return bt::bt_to_minkowski(z);
}
else if(prod && bt::in()) {
auto dec = product_decompose(at);
hyperpoint dep = PIU( deparabolic13(dec.second) );
hyperpoint h = product_decompose(fac.h0).second;
h = PIU( deparabolic13(h) );
dep[0] = h[0];
return zshift(PIU(parabolic13(dep)), dec.first);
}
else {
transmatrix M = ray::mirrorize(currentmap->ray_iadj(on_floor_of, floor_dir));
M = ToOld * M * inverse(ToOld);
return mid(at, M * at);
}
};
hyperpoint at = tC0(inverse(View));
if(invalid_point(at)) {
println(hlog, "at is invalid!");
on = false;
return;
}
auto wallpt = find_nearest(f, at);
ld view_eps = 1e-5;
transmatrix spin_T;
bool use_T = false;
if(eye_angle) use_T = true, spin_T = cspin(1, 2, -eye_angle * degree);
#if CAP_VR
if(vrhr::active() && !vrhr::first && vrhr::hsm != vrhr::eHeadset::none) {
use_T = true;
spin_T = vrhr::hmd_ref_at;
// print(hlog, "HMD seems to be at altitude ", spin_T[1][3], " depth ", spin_T[2][3], " zeros are ", spin_T[3][1], " and ", spin_T[3][2]);
dynamicval<eGeometry> g(geometry, gCubeTiling);
spin_T = vrhr::sm * inverse(spin_T);
eye_level = -spin_T[1][3] / vrhr::absolute_unit_in_meters;
if(eye_level < .001) eye_level = 0.001;
vrhr::be_33(spin_T);
}
#endif
if(use_T) rotate_view(spin_T);
hyperpoint front = inverse(get_shift_view_of(ctangent(2, -view_eps), View)) * C0;
hyperpoint up = inverse(get_shift_view_of(ctangent(1, +view_eps), View)) * C0;
auto fwallpt = find_nearest(f, front);
transmatrix T = nonisotropic ? nisot::translate(wallpt, -1) : gpushxto0(wallpt);
hyperpoint dx = inverse_exp(shiftless(T * at));
transmatrix Tf = nonisotropic ? nisot::translate(fwallpt, -1) : gpushxto0(fwallpt);
hyperpoint dxf = inverse_exp(shiftless(Tf * front));
if(eye_level == -1) eye_level = hypot_d(3, dx);
auto smooth = [&] (hyperpoint h1, hyperpoint h2) {
if(ticks < ticks_end) {
ld last_t = ilerp(ticks_end-1000, ticks_end, ticks_last);
ld curr_t = ilerp(ticks_end-1000, ticks_end, ticks);
last_t = last_t * last_t * (3-2*last_t);
curr_t = curr_t * curr_t * (3-2*curr_t);
ld t = ilerp(last_t, 1, curr_t);
return lerp(h1, h2, t);
}
return h2;
};
auto oView = View;
set_view(
smooth(at, inverse(T) * direct_exp(dx / hypot_d(3, dx) * eye_level)),
smooth(front, inverse(Tf) * direct_exp(dxf / hypot_d(3, dxf) * eye_level)),
smooth(up, inverse(T) * direct_exp(dx / hypot_d(3, dx) * (eye_level + view_eps)))
);
if(use_T) rotate_view(inverse(spin_T));
playermoved = false;
auto nat = tC0(inverse(View));
if(invalid_point(nat)) {
println(hlog, "at is invalid after fixing!");
View = oView;
return;
}
ticks_last = ticks;
}
EX void add_options() {
dialog::addBoolItem("walking mode", on, 'w');
dialog::add_action([] {
on = !on;
if(on && auto_eyelevel) eye_level = -1;
floor_dir = -1;
on_floor_of = nullptr;
ticks_last = ticks;
ticks_end = ticks + 1000;
});
add_edit(eye_level);
add_edit(eye_angle);
if(point_direction >= 0 && point_direction < centerover->type) {
cell *c = centerover->move(point_direction);
if(c && c->wall == waWaxWall) {
color_t col = c->landparam;
dialog::addBoolItem("we are facing floor (color " + format("%06X", col) + ")", colors_of_floors.count(col), 'n');
dialog::add_action([col] {
if(colors_of_floors.count(col)) colors_of_floors.erase(col);
else colors_of_floors.insert(col);
});
}
}
}
auto a = addHook(hooks_configfile, 100, [] {
param_b(auto_eyelevel, "auto_eyelevel")
-> editable("keep eye level when walking enabled", 'L');
param_f(eye_level, "eye_level")
-> editable(0, 5, .1, "walking eye level",
"Distance from the floor to the eye in the walking mode, in absolute units. In VR this is adjusted automatically.",
'e')
->set_extra([] { add_edit(auto_eyelevel); });
param_f(eye_angle, "eye_angle")
-> editable(-90, 90, 15, "walking eye angle",
"0 = looking forward, 90 = looking upward. In VR this is adjusted automatically.",
'k')
->set_extra([] { add_edit(eye_angle_scale); });
param_f(eye_angle_scale, "eye_angle_scale")
-> editable(-2, 0, 2, "eye angle scale",
"1 = the angle can be changed with keyboard or mouse movements, 0 = the angle is fixed",
'k');
})
+ addHook(hooks_clearmemory, 40, [] { on_floor_of = nullptr; floor_dir = -1; })
+ arg::add3("-walk-on", [] {
on = true;
if(auto_eyelevel) eye_level = -1;
floor_dir = -1;
on_floor_of = nullptr;
ticks_last = ticks_end = ticks;
});
EX }
}

View File

@ -2588,7 +2588,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
bool locked = true;
forCellEx(c1, c) if(!c1->wall) locked = false;
if(locked) c->item = itEclectic;
if(locked && !safety) c->item = itEclectic;
if(c->wall == waNone && hrand_monster(2500) < 30 + items[itEclectic] + yendor::hardness() && !safety)
gen_eclectic_monster(c);

View File

@ -253,7 +253,7 @@ EX bool legacy_racing() {
EX bool rcheck(string which, int qty, int x) {
return hrand(qty) < x;
};
}
EX int wallchance_legacy(cell *c, bool deepOcean) {
eLand l = c->land;

View File

@ -449,12 +449,14 @@ EX namespace mapstream {
f.write(gp::param.second);
}
#endif
#if MAXMDIM >= 4
if(variation == eVariation::coxeter) {
f.write(reg3::coxeter_param);
}
if(is_subcube_based(variation )) {
f.write(reg3::subcube_count);
}
#endif
#if CAP_FIELD
if(geometry == gFieldQuotient) {
using namespace fieldpattern;
@ -541,12 +543,14 @@ EX namespace mapstream {
f.read(gp::param.second);
}
#endif
#if MAXMDIM >= 4
if(variation == eVariation::coxeter && vernum >= 0xA908) {
f.read(reg3::coxeter_param);
}
if(is_subcube_based(variation) && vernum >= 0xA908) {
f.read(reg3::subcube_count);
}
#endif
#if CAP_CRYSTAL
if(cryst && vernum >= 10504) {
int sides;
@ -702,7 +706,9 @@ EX namespace mapstream {
}
addToQueue(save_start());
#if MAXMDIM >= 4
if(intra::in) intra::prepare_need_to_save();
#endif
for(int i=0; i<isize(cellbyid); i++) {
cell *c = cellbyid[i];
if(i) {
@ -745,7 +751,9 @@ EX namespace mapstream {
f.write(c->wparam); f.write(c->landparam);
f.write_char(c->stuntime); f.write_char(c->hitpoints);
bool blocked = false;
#if MAXMDIM >= 4
if(intra::in && isWall3(c) && !intra::need_to_save.count(c)) blocked = true;
#endif
if(!blocked)
for(int j=0; j<c->type; j++) {
cell *c2 = c->move(j);
@ -756,6 +764,12 @@ EX namespace mapstream {
int32_t n = -1; f.write(n);
int32_t id = cellids.count(cwt.at) ? cellids[cwt.at] : -1;
f.write(id);
if(f.vernum >= 0xA90C) {
vector<color_t> v;
for(auto c: walking::colors_of_floors) v.push_back(c);
f.write(v);
}
save_drawing_tool(f);
@ -771,6 +785,7 @@ EX namespace mapstream {
for(int i=0; i<multi::players; i++)
f.write(cellids[multi::player[i].at]);
#if MAXMDIM >= 4
if(intra::in) {
for(int i=0; i<isize(intra::portals_to_save); i++) {
auto& p = intra::portals_to_save[i];
@ -789,6 +804,7 @@ EX namespace mapstream {
}
f.write<char>(0);
}
#endif
callhooks(hooks_savemap, f);
f.write<int>(0);
@ -970,6 +986,13 @@ EX namespace mapstream {
savecount = 0; savetime = 0;
cheater = 1;
if(f.vernum >= 0xA90C) {
vector<color_t> v;
f.read(v);
walking::colors_of_floors.clear();
for(auto c: v) walking::colors_of_floors.insert(c);
}
load_drawing_tool(f);
dynamicval<bool> a3(vid.always3, vid.always3);
@ -1002,6 +1025,7 @@ EX namespace mapstream {
}
}
#if MAXMDIM >= 4
if(intra::in) {
while(true) {
char k = f.get<char>();
@ -1015,6 +1039,7 @@ EX namespace mapstream {
cw.spin = fixspin(relspin[id], spin, cw.at->type, f.vernum);
}
}
#endif
if(f.vernum >= 0xA848) {
int i;
@ -1060,9 +1085,14 @@ EX namespace mapstream {
if(!f.f) return false;
f.write(f.vernum);
f.write(dual::state);
#if MAXMDIM >= 4
int q = intra::in ? isize(intra::data) : 0;
f.write(q);
#else
int q = 0;
#endif
if(q) {
#if MAXMDIM >= 4
intra::prepare_to_save();
int qp = isize(intra::portals_to_save);
f.write(qp);
@ -1072,6 +1102,7 @@ EX namespace mapstream {
}
intra::resetter ir;
for(int i=0; i<q; i++) { intra::switch_to(i); save_only_map(f); }
#endif
}
else {
// make sure we save in correct order
@ -1102,6 +1133,7 @@ EX namespace mapstream {
if(q) {
int qp;
f.read(qp);
#if MAXMDIM >= 4
intra::portals_to_save.resize(qp);
for(auto& ps: intra::portals_to_save) {
f.read(ps.spin);
@ -1110,12 +1142,14 @@ EX namespace mapstream {
}
for(int i=0; i<q; i++) {
intra::in = true; /* so that it knows to load portals */
game_active = false;
load_only_map(f);
intra::in = false;
intra::become();
}
intra::start();
intra::load_saved_portals();
#endif
}
else {
dual::split_or_do([&] { load_only_map(f); });

View File

@ -109,6 +109,22 @@ EX void moveEffect(const movei& mi, eMonster m) {
#endif
}
EX void check_beauty(cell *ct, cell *cf, eMonster m) {
bool adj = false;
if(ct->cpdist == 1 && (items[itOrb37] || !nonAdjacent(cf,ct)) && markOrb(itOrbBeauty) && !isFriendly(ct))
adj = true;
if(!adj && items[itOrbEmpathy] && items[itOrbBeauty] && !isFriendly(ct)) {
for(int i=0; i<ct->type; i++) if(ct->move(i) && isFriendly(ct->move(i)))
adj = true, markOrb(itOrbEmpathy), markOrb(itOrbBeauty);
}
if(adj && ct->stuntime == 0 && !isMimic(m)) {
ct->stuntime = 2;
checkStunKill(ct);
}
}
EX void moveMonster(const movei& mi) {
auto& cf = mi.s;
auto& ct = mi.t;
@ -254,20 +270,8 @@ EX void moveMonster(const movei& mi) {
if(m == moWitchFire) makeflame(cf, 10, false);
if(m == moFireElemental) { makeflame(cf, 20, false); if(cf->wparam < 20) cf->wparam = 20; }
bool adj = false;
if(ct->cpdist == 1 && (items[itOrb37] || !nonAdjacent(cf,ct)) && markOrb(itOrbBeauty) && !isFriendly(ct))
adj = true;
if(!adj && items[itOrbEmpathy] && items[itOrbBeauty] && !isFriendly(ct)) {
for(int i=0; i<ct->type; i++) if(ct->move(i) && isFriendly(ct->move(i)))
adj = true, markOrb(itOrbEmpathy), markOrb(itOrbBeauty);
}
check_beauty(ct, cf, m);
if(adj && ct->stuntime == 0 && !isMimic(m)) {
ct->stuntime = 2;
checkStunKill(ct);
}
if(!cellEdgeUnstable(ct)) {
if(isMetalBeast(m)) ct->stuntime += 2;
if(m == moTortoise) ct->stuntime += 3;
@ -1487,6 +1491,8 @@ EX void moveshadow() {
where->stuntime = 0;
// the Shadow sets off the mines and stuff
moveEffect(movei(where, where, NODIR), moShadow);
// Beauty kills the Shadow
check_beauty(where, where, moShadow);
}
}
}

View File

@ -64,7 +64,11 @@ void gamedata_all(gamedata& gd) {
gd.store(hybrid::underlying);
gd.store(hybrid::csteps);
gd.store(hybrid::underlying_cgip);
gd.store_ptr(vid);
gd.store_ptr(vid.projection_config);
gd.store_ptr(vid.rug_config);
gd.store(vid.yshift);
gd.store(vid.plevel_factor);
gd.store(vid.binary_width);
gd.store(sightrange_bonus);
gd.store(genrange_bonus);
gd.store(gamerange_bonus);
@ -179,8 +183,7 @@ EX namespace dual {
dynamicval<int> dm(dual::state, 2);
int cg = currently_loaded;
bool orbusedbak[ittypes];
for(int i=0; i<ittypes; i++) orbusedbak[i] = orbused[i];
auto orbusedbak = orbused;
if(d < 0) {
if(d == -2 && items[itGreenStone] < 2) {
@ -193,7 +196,7 @@ EX namespace dual {
for(int k=0; k<2; k++) {
switch_to(k);
ok = ok && movepcto(d, subdir, true);
for(int i=0; i<ittypes; i++) orbused[i] = orbusedbak[i];
orbused = orbusedbak;
}
if(ok && checkonly) {
switch_to(cg);

View File

@ -401,7 +401,7 @@ EX eOrbLandRelation getOLR(eItem it, eLand l) {
if(l == laDungeon) {
if(it == itOrbSafety || it == itOrbFrog ||
it == itOrbTeleport || it == itOrbMatter || it == itOrbNature ||
it == itOrbAether || it == itOrbSummon || it == itOrbStone)
it == itOrbAether || it == itOrbSummon || it == itOrbStone || it == itOrbChaos)
return olrForbidden;
}

View File

@ -6,9 +6,11 @@
*/
#include "hyper.h"
namespace hr {
EX bool orbused[ittypes], lastorbused[ittypes];
EX array<bool, ittypes> orbused;
EX array<bool, ittypes> lastorbused;
EX bool markOrb(eItem it) {
if(!items[it]) return false;
@ -314,6 +316,7 @@ EX bool distanceBound(cell *c1, cell *c2, int d) {
EX void checkFreedom(cell *cf) {
manual_celllister cl;
dynamicval<decltype(orbused)> d(orbused);
cl.add(cf);
for(int i=0; i<isize(cl.lst); i++) {
cell *c = cl.lst[i];
@ -1027,6 +1030,10 @@ EX void checkStunKill(cell *dest) {
return;
}
}
if(dest->monst == moShadow) {
addMessage(XLAT("%The1 is destroyed!", dest->monst));
killMonster(dest, moNone);
}
/* if(!isPermanentFlying(dest->monst) && cellEdgeUnstable(dest)) {
addMessage(XLAT("%The1 falls!", dest->monst));
fallMonster(dest);
@ -1598,6 +1605,15 @@ EX eItem targetRangedOrb(cell *c, orbAction a) {
return itNone;
}
bool isValentines() {
const time_t now = time(NULL);
const struct tm *datetime = localtime(&now);
// 0-indexed tm_mon, 1-indexed tm_mday
// So this is February (2nd month), and the 14th day.
return datetime->tm_mon == 1 && datetime->tm_mday == 14;
}
EX int orbcharges(eItem it) {
switch(it) {
case itRevolver: //pickup-key
@ -1607,6 +1623,7 @@ EX int orbcharges(eItem it) {
case itOrbDiscord:
return inv::on ? 46 : 23;
case itOrbLove:
return isValentines() ? 31 : 30;
case itOrbUndeath:
case itOrbSpeed: //"pickup-speed");
case itOrbInvis:

View File

@ -576,6 +576,9 @@ void apply_chaos() {
if (cb->wall == waStone) destroyTrapsAround(cb);
changes.ccell(ca);
changes.ccell(cb);
/* needs to be called separately for Shadows */
if(ca->monst == moShadow) checkStunKill(ca);
if(cb->monst == moShadow) checkStunKill(cb);
gcell coa = *ca;
gcell cob = *cb;
if(ca->monst != cb->monst)
@ -588,10 +591,14 @@ void apply_chaos() {
copy_metadata(cb, &coa);
if(!switch_lhu_in(ca->land)) ca->LHU = coa.LHU;
if(!switch_lhu_in(cb->land)) cb->LHU = cob.LHU;
if(ca->monst && !(isFriendly(ca) && markOrb(itOrbEmpathy)))
if(ca->monst && !(isFriendly(ca) && markOrb(itOrbEmpathy))) {
ca->stuntime = min(ca->stuntime + 3, 15), markOrb(itOrbChaos);
if(cb->monst && !(isFriendly(cb) && markOrb(itOrbEmpathy)))
checkStunKill(ca);
}
if(cb->monst && !(isFriendly(cb) && markOrb(itOrbEmpathy))) {
cb->stuntime = min(cb->stuntime + 3, 15), markOrb(itOrbChaos);
checkStunKill(cb);
}
ca->monmirror = !ca->monmirror;
cb->monmirror = !cb->monmirror;
ca->mondir = chaos_mirror_dir(ca->mondir, wb, wa);
@ -855,7 +862,7 @@ bool pcmove::after_escape() {
if(attackable && fmsAttack && !dont_attack && !items[itCurseWeakness]) {
if(checkNeedMove(checkonly, true)) return false;
nextmovetype = nm ? lmAttack : lmSkip;
if(c2->wall == waSmallTree) {
if(c2->wall == waSmallTree || (c2->wall == waBigTree && markOrb(itOrbSlaying))) {
drawParticles(c2, winf[c2->wall].color, 4);
addMessage(XLAT("You chop down the tree."));
playSound(c2, "hit-axe" + pick123());

View File

@ -451,6 +451,7 @@ void raygen::compute_which_and_dist(int flat1, int flat2) {
"mediump float zsgn = (Mt > 0. ? -sgn : sgn);\n"
"mediump float u = sqrt(b*b-c)*zsgn + b;\n"
"mediump float v = -(Mp*u-1.) / Mt;\n"
"if(a < 1e-5) v = (1.-Mp*Mp) / (2. * Mt);\n"
"mediump float d = asinh(v);\n";
if(prod) fmain += "d /= xspeed;\n";
fmain +=
@ -1575,22 +1576,28 @@ void raygen::add_functions() {
add_if("to_poco_h3",
"mediump vec4 to_poco_h3(mediump vec4 pos) {\n"
" return pos / pos[3];\n"
" pos = pos / pos[3];\n"
" pos[2] /= sqrt(1.-pos.x*pos.x-pos.y*pos.y);\n"
" return pos;\n"
" }\n\n");
add_if("from_poco_h3",
"mediump vec4 from_poco_h3(mediump vec4 pos) {\n"
" pos[2] *= sqrt(1.-pos.x*pos.x-pos.y*pos.y);\n"
" float s = 1. - dot(pos.xyz, pos.xyz);\n"
" return pos / sqrt(s);\n"
" }\n\n");
add_if("to_poco_s3",
"mediump vec4 to_poco_s3(mediump vec4 pos) {\n"
" return pos / pos[3];\n"
" pos = pos / pos[3];\n"
" pos[2] /= sqrt(1.+pos.x*pos.x+pos.y*pos.y);\n"
" return pos;\n"
" }\n\n");
add_if("from_poco_s3",
"mediump vec4 from_poco_s3(mediump vec4 pos) {\n"
" pos[2] *= sqrt(1.+pos.x*pos.x+pos.y*pos.y);\n"
" float s = 1. + dot(pos.xyz, pos.xyz);\n"
" return pos / sqrt(s);\n"
" }\n\n");
@ -1623,13 +1630,13 @@ void raygen::add_functions() {
add_if("from_poco_h2xr_e",
"mediump vec4 from_poco_h2xr_e(mediump vec4 pos) {\n"
" return vec4(sinh(pos[2]) * cosh(pos[0]), sinh(pos[0]), cosh(pos[0]) * cosh(pos[2]), 0);\n"
" return vec4(sinh(pos[2]), sinh(pos[0]) * cosh(pos[2]), cosh(pos[0]) * cosh(pos[2]), 0);\n"
" }\n\n");
add_if("to_poco_h2xr_e",
"mediump vec4 to_poco_h2xr_e(mediump vec4 pos) {\n"
" mediump float x = asinh(pos[1]);\n"
" return vec4(x, 0, asinh(pos[0] / cosh(x)), 1);\n"
" mediump float x = asinh(pos[0]);\n"
" return vec4(asinh(pos[1] / cosh(x)), 0, x, 1);\n"
" }\n\n");
add_if("from_poco_s2xr_s",
@ -1648,13 +1655,13 @@ void raygen::add_functions() {
add_if("from_poco_s2xr_e",
"mediump vec4 from_poco_s2xr_e(mediump vec4 pos) {\n"
" return vec4(sin(pos[2]) * cos(pos[0]), sin(pos[0]), cos(pos[0]) * cos(pos[2]), 0);\n"
" return vec4(sin(pos[2]), sin(pos[0]) * cos(pos[2]), cos(pos[0]) * cos(pos[2]), 0);\n"
" }\n\n");
add_if("to_poco_s2xr_e",
"mediump vec4 to_poco_s2xr_e(mediump vec4 pos) {\n"
" mediump float x = asin_clamp(pos[1]);\n"
" return vec4(x, 0, asin_clamp(pos[0] / cos(x)), 1);\n"
" mediump float x = asin_clamp(pos[0]);\n"
" return vec4(asin_clamp(pos[1] / cos(x)), 0, x, 1);\n"
" }\n\n");
add_if("deparabolic12",
@ -2059,7 +2066,7 @@ void uniform2(GLint id, array<float, 2> fl) {
color_t color_out_of_range = 0x0F0800FF;
transmatrix get_ms(cell *c, int a, bool mirror) {
EX transmatrix get_ms(cell *c, int a, bool mirror) {
int z = a ? 1 : -1;
if(c->type == 3) {
@ -2089,7 +2096,7 @@ transmatrix get_ms(cell *c, int a, bool mirror) {
int nesting;
transmatrix mirrorize(transmatrix T) {
EX transmatrix mirrorize(transmatrix T) {
T = inverse(T);
hyperpoint h = tC0(T);
ld d = hdist0(h);
@ -2486,24 +2493,27 @@ EX void cast() {
#if CAP_VR
if(o->uEyeShift != -1) {
dynamicval<eGeometry> g(geometry, gCubeTiling);
transmatrix T = vrhr::eyeshift;
if(nonisotropic)
T = inverse(NLP) * T;
glUniformMatrix4fv(o->uEyeShift, 1, 0, glhr::tmtogl_transpose3(T).as_array());
glUniformMatrix4fv(o->uEyeShift, 1, 0, glhr::tmtogl_transpose(T).as_array());
glUniform1f(o->uAbsUnit, vrhr::absolute_unit_in_meters);
}
if(vrhr::rendering_eye()) {
glUniformMatrix4fv(o->uProjection, 1, 0, glhr::tmtogl_transpose3(vrhr::eyeproj).as_array());
dynamicval<eGeometry> g(geometry, gCubeTiling);
glUniformMatrix4fv(o->uProjection, 1, 0, glhr::tmtogl_transpose(vrhr::eyeproj).as_array());
}
#else
if(0) ;
#endif
else {
dynamicval<eGeometry> g(geometry, gCubeTiling);
transmatrix proj = Id;
proj = eupush(-global_projection * d, 0) * proj;
proj = euscale(cd->tanfov / (vid.stereo_mode == sLR ? 2 : 1), cd->tanfov * cd->ysize / cd->xsize) * proj;
proj = eupush(-((cd->xcenter-cd->xtop)*2./cd->xsize - 1), -((cd->ycenter-cd->ytop)*2./cd->ysize - 1)) * proj;
glUniformMatrix4fv(o->uProjection, 1, 0, glhr::tmtogl_transpose3(proj).as_array());
glUniformMatrix4fv(o->uProjection, 1, 0, glhr::tmtogl_transpose(proj).as_array());
}
if(!callhandlers(false, hooks_rayset, o)) {

View File

@ -18,7 +18,7 @@
#include <aegis.hpp>
#endif
#include "../hyper.h"
#include "rogueviz.h"
namespace hr {
@ -44,6 +44,8 @@ int labels_value = 1;
vector<boarddata> history;
bool draw_go(cell *c, const shiftmatrix& V);
void init_go() {
ac = currentmap->allcells();
current.taken.resize(isize(ac), 2);
@ -52,6 +54,7 @@ void init_go() {
current.captures[1] = 0;
for(int i=0; i<isize(ac); i++)
indices[ac[i]] = i;
rogueviz::addHook(hooks_drawcell, 100, draw_go);
}
void hwrite(hstream& hs, const boarddata& b) {
@ -602,7 +605,6 @@ int rugArgs() {
auto gobot_hook =
addHook(hooks_args, 100, rugArgs) +
addHook(hooks_drawcell, 100, draw_go) +
addHook(shmup::hooks_turn, 100, [] (int t) {
if(shot_state == 1) {
shot::take("go-temp.png");

View File

@ -468,8 +468,62 @@ auto hooks =
// generate binary-tiling H2xE with floors to the current scene, runs automatically
+ arg::add3("-intra-bxe", create_intra_bxe)
// generate Sol with floors to the current scene, runs autimatically
+ arg::add3("-intra-sol", create_intra_sol);
+ arg::add3("-intra-sol", create_intra_sol)
//+ arg::add3("-intra-more", create_intra_more);
+ arg::add3("-intra-demo-floors", [] {
walking::colors_of_floors = {
0xFFFF40, 0xD0D000,
0xC0FFC0, 0x80C080,
0xC0FFFF, 0x40FFFF,
0x8080FF, 0x0000FF,
0xFF80FF, 0xFF00FF,
0x64BF95, 0xA4FFD5,
0xFFFDD0, 0xFFD080
};
})
+ addHook_rvslides(10, ([] (string s, vector<tour::slide>& v) {
println(hlog, "called with s='", s, "'");
if(s != "portal") return;
using namespace tour;
auto load = [] (string s, ld x, int y) {
return [s, x, y] {
slide_backup(vid.cells_drawn_limit, 100);
slide_backup(smooth_scrolling, true);
slide_backup(ray::max_cells, 999999);
slide_backup(walking::on, true);
slide_backup(walking::eye_level, x);
mapstream::loadMap(s);
slide_backup(ray::fixed_map, true);
slide_backup(ray::max_iter_intra, y);
};
};
auto add = [&] (string s, string desc, string youtube, string twitter, reaction_t loader) {
v.push_back(tour::slide{
s, 10, tour::LEGAL::NONE | tour::QUICKSKIP | tour::QUICKGEO, desc,
[=] (tour::presmode mode) {
setCanvas(mode, '0');
if(youtube != "")
slide_url(mode, 'y', "YouTube link", youtube);
if(twitter != "")
slide_url(mode, 't', "Twitter link", twitter);
slide_action(mode, 'r', "run this visualization", loader);
if(mode == tour::pmKey) pushScreen(intra::show_portals);
}
});
};
add("inter-geometric portals",
"In this world we can find portals between six different geometries. The camera is in 'walking mode' i.e. restricted to keep close to the floor (this can be disabled with '5').",
"https://youtu.be/yqUv2JO2BCs", "https://twitter.com/ZenoRogue/status/1496867204419452935",
load("portalscene3.lev", 0.2174492, 600)
);
add("curved landscape",
"Here we create portals between Solv and H3 geometries, resulting in a scene looking a bit like a curved landscape.",
"", "https://twitter.com/ZenoRogue/status/1446127100516130826",
load("solv-h3-scene.lev", 0.05, 3000));
}));
}

View File

@ -1417,6 +1417,85 @@ void nk_launch() {
#endif
}
void portal_slideshow(tour::ss::slideshow_callback cb) {
using namespace rogueviz::pres;
using namespace tour;
static vector<slide> portal_slides;
if(portal_slides.empty()) {
portal_slides.emplace_back(slide{"portal collection", 100, LEGAL::NONE | QUICKSKIP,
"This is a collection of portals. We start with knotted portals in Euclidean geometry, "
"then we visit portals in other geometries, and finally, we explore portals between different "
"geometries.\n\nLoading these may take some time, so you need to press 'r' to run them. In most slides you can also press '5' to change various parameters.",
[] (presmode mode) {}
});
auto add = [&] (string s, string text, string youtube, reaction_t act) {
portal_slides.emplace_back(
tour::slide{s, 100, LEGAL::NONE | QUICKGEO | QUICKSKIP, text,
[=] (presmode mode) {
setCanvas(mode, '0');
if(youtube != "")
slide_url(mode, 'y', "YouTube link", youtube);
slide_action(mode, 'r', "run", [=] {
slide_backup(margin);
slide_backup(mapeditor::drawplayer);
slide_backup(firstland);
slide_backup(specialland);
slide_backup(ray::max_cells, 600000);
slide_backup(smooth_scrolling, 1);
slide_backup(camera_speed, 10);
slide_backup(ray::exp_decay_poly, 30);
slide_backup(ray::fixed_map, true);
slide_backup(ray::max_iter_iso, 80);
#if CAP_VR
slide_backup(vrhr::hsm);
slide_backup(vrhr::eyes);
slide_backup(vrhr::cscr);
slide_backup(vrhr::absolute_unit_in_meters);
#endif
on_restore([] { nilv::set_flags(); asonov::set_flags(); });
slide_backup(nilv::nilwidth);
slide_backup(nilv::nilperiod);
slide_backup(vid.binary_width);
slide_backup(asonov::period_xy);
slide_backup(asonov::period_z);
act();
start_game();
loop = 2;
});
if(mode == tour::pmKey) pushScreen(show);
}});
};
auto launch_euc_with = [] (bool b) {
return [b] {
self_hiding = b;
launch_euc();
};
};
add("knotted portal", "This is a knotted portal in Euclidean space.", "https://www.youtube.com/watch?v=eb2DhCcGH7U", launch_euc_with(false));
add("self-hiding portal", "This knotted portal is 'self-hiding'. It appears that the portal enters itself and disappears!", "https://www.youtube.com/watch?v=vFLZ2NGtuGw", launch_euc_with(true));
add("non-Euclidean portal in Nil", "A portal in Nil geometry.", "https://www.youtube.com/watch?v=2K-v8tK68AE", launch_nil);
add("spherical portal", "A portal in spherical geometry. Such a portal lets us create a space with spherical geometry that has more volume than the sphere.", "https://www.youtube.com/watch?v=PerPeQFu5gw", launch_sphereknot);
add("Cat Portal in Solv", "A portal in Solv geometry. The honeycomb is based on the mapping torus of Arnold's cat mapping.", "https://www.youtube.com/watch?v=CGiSxC9B6i0", launch_solv);
callhooks(rogueviz::pres::hooks_build_rvtour, "portal", portal_slides);
add_end(portal_slides);
}
cb(XLAT("portal collection"), &portal_slides[0], 'p');
}
auto shot_hooks = addHook(hooks_initialize, 100, create_notknot)
+ addHook(hooks_welcome_message, 100, [] {
if(geometry == gNotKnot) {
@ -1496,59 +1575,9 @@ auto shot_hooks = addHook(hooks_initialize, 100, create_notknot)
param_i(loop_any, "nk_loopany");
})
#ifndef NOTKNOT
+ addHook_rvslides(180, [] (string s, vector<tour::slide>& v) {
if(s != "mixed") return;
v.push_back(tour::slide{
"weird portals", 10, tour::LEGAL::NONE | tour::QUICKSKIP,
"Some experiments with weird portals. Press '5' to change between available experiments.\n"
,
[] (tour::presmode mode) {
slide_url(mode, 'k', "knotted portal (YouTube)", "https://www.youtube.com/watch?v=eb2DhCcGH7U");
slide_url(mode, 'h', "self-hiding knot portal (YouTube)", "https://www.youtube.com/watch?v=vFLZ2NGtuGw");
slide_url(mode, 'n', "non-Euclidean portal in Nil (YouTube)", "https://www.youtube.com/watch?v=2K-v8tK68AE");
slide_url(mode, 's', "spherical portal (YouTube)", "https://www.youtube.com/watch?v=PerPeQFu5gw");
slide_url(mode, 'c', "Cat Portal in Solv (YouTube)", "https://www.youtube.com/watch?v=CGiSxC9B6i0");
setCanvas(mode, '0');
using namespace tour;
if(mode == pmStart) {
slide_backup(margin);
slide_backup(mapeditor::drawplayer);
slide_backup(firstland);
slide_backup(specialland);
slide_backup(ray::max_cells);
slide_backup(smooth_scrolling);
slide_backup(camera_speed);
slide_backup(ray::exp_decay_poly);
slide_backup(ray::fixed_map);
slide_backup(ray::max_iter_iso);
#if CAP_VR
slide_backup(vrhr::hsm);
slide_backup(vrhr::eyes);
slide_backup(vrhr::cscr);
slide_backup(vrhr::absolute_unit_in_meters);
#endif
on_restore([] { nilv::set_flags(); asonov::set_flags(); });
slide_backup(nilv::nilwidth);
slide_backup(nilv::nilperiod);
slide_backup(vid.binary_width);
slide_backup(asonov::period_xy);
slide_backup(asonov::period_z);
nk_launch();
start_game();
loop = 2;
}
if(mode == tour::pmKey) {
pushScreen(show);
}
}
});
})
+ addHook_slideshows(120, portal_slideshow)
#endif
;
;
#ifdef NOTKNOT
auto hook1=

View File

@ -13,6 +13,7 @@ g -- generate the current room (buggy)
m -- see the map (toggle)
p -- pause (toggle)
z -- screenshot menu
v -- HyperRogue settings
q -- quit
s -- save to platformer.lev
1 -- place a small block under the mouse
@ -151,7 +152,6 @@ struct room {
void initial() {
int ylev = where->master->distance;
println(hlog, "ylev = ", ylev);
if(ylev <= 0)
for(int y=room_y-6; y<room_y; y++)
for(int x=0; x<room_x; x++)
@ -550,7 +550,7 @@ bool draw_room_on_map(cell *c, const shiftmatrix& V) {
p.tinf = &roomtinf;
p.offset_texture = 0;
p.texture_id = r.room_texture->textureid;
println(hlog, "offset = ", p.offset, " texture_offset = ", p.offset_texture);
// println(hlog, "offset = ", p.offset, " texture_offset = ", p.offset_texture);
auto render_at = [&] (GLuint texid, double px0, double py0, double px1, double py1,
double tx0, double ty0, double tx1, double ty1) {
@ -632,6 +632,7 @@ void render_room(room *r) {
template<class R> void render_room_objects(room *r, R render_at) {
auto pb = get_pixel_bbox();
if(r != current_room) return;
create_sprite_texture();
render_at(sprite_texture->textureid, pb.minx, pb.miny, pb.maxx, pb.maxy, 0, 0, man_x/256., man_y/256.);
}
@ -704,14 +705,29 @@ void run() {
dialog::add_key_action('m', [] {
map_on = !map_on;
});
dialog::add_key_action('v', [] {
pushScreen(showSettings);
});
dialog::add_key_action('p', [] {
paused = !paused;
});
dialog::add_key_action('z', [] {
pushScreen(shot::menu);
});
dialog::add_key_action('q', [] { exit(0); });
dialog::add_key_action('s', [] {
dialog::add_key_action('q', [] {
if(tour::on) tour::next_slide();
else exit(0);
});
dialog::add_key_action('o', [] {
if(tour::on) tour::next_slide();
});
dialog::add_key_action(SDLK_ESCAPE, [] {
if(tour::on) tour::next_slide();
});
dialog::add_key_action(SDLK_F10, [] {
if(tour::on) tour::next_slide();
});
dialog::add_key_action('s', [] {
mapstream::saveMap("platformer.lev");
});
@ -801,6 +817,24 @@ void add_platf_hooks() {
}
auto chk = arg::add3("-platformer", enable)
+ addHook_rvslides(195, [] (string s, vector<tour::slide>& v) {
if(s != "mixed") return;
v.push_back(tour::slide{
"platformer", 10, tour::LEGAL::NONE | tour::QUICKSKIP | tour::QUICKGEO,
"A non-Euclidean platformer.\n\nPress up/left/right to move the guy.\n\nM to see the map\n\nP to pause\n\nV to change HyperRogue settings.\n\nPress Q when you are done.\n"
,
[] (tour::presmode mode) {
slide_url(mode, 'y', "non-Euclidean platformer (YouTube)", "https://www.youtube.com/watch?v=eb2DhCcGH7U");
slide_url(mode, 't', "non-Euclidean platformer (Twitter)", "https://twitter.com/ZenoRogue/status/1467233150380089345");
slide_url(mode, 'g', "how to edit this", "https://github.com/zenorogue/hyperrogue/blob/master/rogueviz/platformer.cpp");
setCanvas(mode, '0');
using namespace tour;
if(mode == pmStart) {
mapstream::loadMap("platformer.lev");
}
}
});
})
+ addHook(mapstream::hooks_loadmap, 100, [] (fhstream& f, int id) {
if(id == 66) {
println(hlog, "loading platformer");

View File

@ -59,6 +59,7 @@
#include "highdim-demo.cpp"
#include "horo63.cpp"
#include "platformer.cpp"
#include "intra-demos.cpp"
#include "gobot.cpp"
#include "kohonen.cpp"

View File

@ -488,6 +488,7 @@ void launch() {
showstartmenu = false;
mapeditor::drawplayer = false;
rv::hook(hooks_drawcell, 100, draw_star);
}
#if CAP_COMMANDLINE
@ -515,7 +516,6 @@ int rugArgs() {
auto starbattle_hook =
addHook(hooks_args, 100, rugArgs) +
addHook(hooks_drawcell, 100, draw_star) +
addHook(mapstream::hooks_savemap, 100, [] (fhstream& f) {
f.write<int>(isize(sdata));
for(auto& sd: sdata) {

View File

@ -103,6 +103,11 @@ EX void slide_url(presmode mode, char key, string text, string url) {
}});
}
EX void slide_action(presmode mode, char key, string text, reaction_t act) {
if(mode == pmHelpEx)
help_extensions.push_back(help_extension{key, text, act});
}
/** \brief an auxiliary function to enable a visualization in the Canvas land */
EX void setCanvas(presmode mode, char canv) {
if(mode == pmStart) {
@ -206,25 +211,29 @@ void return_geometry() {
addMessage(XLAT("Returned to your game."));
}
EX bool next_slide() {
flagtype flags = slides[currentslide].flags;
popScreenAll();
if(gamestack::pushed()) {
return_geometry();
if(!(flags & QUICKGEO)) return true;
}
if(flags & FINALSLIDE) return true;
presentation(pmStop);
slide_restore_all();
currentslide++;
presentation(pmStart);
slidehelp();
return true;
}
bool handleKeyTour(int sym, int uni) {
if(!tour::on) return false;
if(!(cmode & sm::DOTOUR)) return false;
bool inhelp = cmode & sm::HELP;
flagtype flags = slides[currentslide].flags;
if((sym == SDLK_RETURN || sym == SDLK_KP_ENTER) && (!inhelp || (flags & QUICKSKIP))) {
popScreenAll();
if(gamestack::pushed()) {
return_geometry();
if(!(flags & QUICKGEO)) return true;
}
if(flags & FINALSLIDE) return true;
presentation(pmStop);
slide_restore_all();
currentslide++;
presentation(pmStart);
slidehelp();
return true;
}
if((sym == SDLK_RETURN || sym == SDLK_KP_ENTER) && (!inhelp || (flags & QUICKSKIP)))
return next_slide();
if(sym == SDLK_BACKSPACE) {
if(gamestack::pushed()) {
gamestack::pop();

View File

@ -732,6 +732,11 @@ EX void open_url(string s) {
const char *urlhex = "0123456789ABCDEF";
EX void open_wiki(const char *title) {
// Since "Crossroads" is ambiguous, we use the direct link to Crossroads I.
if (!strcmp(title, "Crossroads")) {
title = "Crossroads (Land)";
}
string url = "https://hyperrogue.miraheze.org/wiki/";
unsigned char c;
for (size_t i = 0; (c = title[i]); ++i) {

58
vr.cpp
View File

@ -794,6 +794,24 @@ EX void track_actions() {
}
}
EX void get_eyes() {
for(int a=0; a<2; a++) {
auto eye = vr::EVREye(a);
E4;
vrdata.proj[a] =
vr_to_hr(vrdata.vr->GetProjectionMatrix(eye, 0.01, 300));
vrdata.iproj[a] = MirrorZ * inverse(vrdata.proj[a]);
// println(hlog, "projection = ", vrdata.proj[a]);
vrdata.eyepos[a] =
vr_to_hr(vrdata.vr->GetEyeToHeadTransform(eye));
// println(hlog, "eye-to-head = ", vrdata.eyepos[a]);
}
}
EX void start_vr() {
if(true) { sm = Id; sm[1][1] = sm[2][2] = -1; }
@ -830,23 +848,12 @@ EX void start_vr() {
println(hlog, "recommended size: ", int(vrdata.xsize), " x ", int(vrdata.ysize));
for(int a=0; a<2; a++) {
auto eye = vr::EVREye(a);
vrdata.eyes[a] = new vr_framebuffer(vrdata.xsize, vrdata.ysize);
println(hlog, "eye ", a, " : ", vrdata.eyes[a]->ok ? "OK" : "Error");
vrdata.proj[a] =
vr_to_hr(vrdata.vr->GetProjectionMatrix(eye, 0.01, 300));
vrdata.iproj[a] = MirrorZ * inverse(vrdata.proj[a]);
println(hlog, "projection = ", vrdata.proj[a]);
vrdata.eyepos[a] =
vr_to_hr(vrdata.vr->GetEyeToHeadTransform(eye));
println(hlog, "eye-to-head = ", vrdata.eyepos[a]);
}
get_eyes();
//CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, leftEyeDesc );
//CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, rightEyeDesc );
@ -1021,7 +1028,8 @@ EX void gen_mv() {
EX shiftmatrix master_cview;
EX void render() {
track_poses();
track_poses();
get_eyes();
resetbuffer rb;
state = 2;
vrhr::frusta.clear();
@ -1033,11 +1041,31 @@ EX void render() {
if(1) {
make_actual_view();
master_cview = cview();
/* unfortunately we need to backup everything that could change by shift_view... */
dynamicval<transmatrix> tN(NLP, NLP);
dynamicval<transmatrix> tV(View, View);
dynamicval<transmatrix> tC(current_display->which_copy, current_display->which_copy);
dynamicval<transmatrix> trt(radar_transform);
/* changed in intra */
dynamicval<ld> tcs(camera_speed);
dynamicval<ld> tcl(anims::cycle_length);
dynamicval<ld> tau(vrhr::absolute_unit_in_meters);
dynamicval<ld> tel(walking::eye_level);
dynamicval<int> tfd(walking::floor_dir);
dynamicval<cell*> tof(walking::on_floor_of);
int id = intra::current;
cell *co = centerover;
finalizer fin([&] {
if(intra::current != id) {
println(hlog, "rendering via portal");
intra::switch_to(id);
centerover = co;
}
});
if(hsm == eHeadset::rotation_only) {
transmatrix T = hmd_at;
be_33(T);