1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-04 21:26:17 +00:00
hyperrogue/control.cpp

1569 lines
45 KiB
C++
Raw Normal View History

// Hyperbolic Rogue -- control
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
/** \file control.cpp
* \brief Routines related to controlling the game
*/
#include "hyper.h"
namespace hr {
EX int frames;
EX bool outoffocus = false;
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX int mousex, mousey;
EX shiftpoint mouseh, mouseoh;
2017-07-10 18:47:38 +00:00
2020-04-19 20:43:16 +00:00
EX bool pandora_leftclick, pandora_rightclick;
EX bool lshiftclick, rshiftclick, lctrlclick, rctrlclick, anyshiftclick, anyctrlclick, wheelclick;
EX bool targetclick, hiliteclick, forcetarget, numlock_on;
2019-08-09 19:00:52 +00:00
EX bool gtouched;
2017-07-10 18:47:38 +00:00
2019-08-09 21:39:36 +00:00
EX bool holdmouse;
2019-08-09 19:00:52 +00:00
EX int getcstat, lgetcstat;
2019-08-09 21:39:36 +00:00
EX ld getcshift;
2019-08-09 19:00:52 +00:00
EX bool inslider;
2022-10-21 16:49:33 +00:00
EX bool invslider;
2020-11-19 17:20:06 +00:00
EX int slider_x;
2017-07-10 18:47:38 +00:00
2019-08-09 20:07:03 +00:00
EX function <void(int sym, int uni)> keyhandler = [] (int sym, int uni) {};
EX function <bool(SDL_Event &ev)> joyhandler = [] (SDL_Event &ev) {return false;};
2019-08-09 21:39:36 +00:00
#if HDR
// what part of the compass does 'skip turn'
static constexpr auto SKIPFAC = .4;
2019-08-09 21:39:36 +00:00
#endif
2017-07-10 18:47:38 +00:00
// is the player using mouse? (used for auto-cross)
2019-08-09 19:00:52 +00:00
EX bool mousing = true;
2017-07-10 18:47:38 +00:00
/** /brief 0 for the system pointer, or VR controller ID */
EX int which_pointer = 0;
2017-07-10 18:47:38 +00:00
// is the mouse button pressed?
2019-08-09 19:00:52 +00:00
EX bool mousepressed = false;
EX bool mousemoved = false;
EX bool actonrelease = false;
2017-07-10 18:47:38 +00:00
EX bool mousepan, oldmousepan;
2019-06-28 07:47:10 +00:00
#if CAP_MOUSEGRAB
2019-08-09 19:00:52 +00:00
EX ld mouseaim_x, mouseaim_y;
2019-06-28 07:47:10 +00:00
#endif
2019-08-09 19:00:52 +00:00
EX ld mouseaim_sensitivity = 0.01;
2019-08-09 19:00:52 +00:00
EX int timetowait;
2017-07-22 23:33:27 +00:00
#if CAP_SDLJOY
2019-08-09 20:07:03 +00:00
EX int joyx, joyy, panjoyx, panjoyy;
EX movedir joydir;
2017-07-22 23:33:27 +00:00
#endif
2019-08-09 20:07:03 +00:00
EX movedir mousedest;
EX ld shiftmul = 1;
2017-07-10 18:47:38 +00:00
2021-05-27 10:59:21 +00:00
EX cell *mouseover, *mouseover2, *lmouseover, *lmouseover_distant;
EX ld modist, modist2;
2017-07-10 18:47:38 +00:00
EX int lastt;
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX bool mouseout() {
2017-07-10 18:47:38 +00:00
if((getcstat != '-' && getcstat) || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh.h);
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool mouseout2() {
2017-07-10 18:47:38 +00:00
if((getcstat && getcstat != '-') || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh.h) || outofmap(mouseoh.h);
2017-07-10 18:47:38 +00:00
}
2019-08-17 23:31:37 +00:00
EX movedir vectodir(hyperpoint P) {
2017-07-10 18:47:38 +00:00
transmatrix U = unshift(ggmatrix(cwt.at));
if(embedded_plane && cgi.emb->is_same_in_same()) U = current_display->radar_transform_post * current_display->radar_transform * U;
2019-08-17 23:31:37 +00:00
2020-04-11 13:08:24 +00:00
P = direct_exp(lp_iapply(P));
2018-07-16 18:06:09 +00:00
hyperpoint H = sphereflip * tC0(U);
transmatrix Centered = sphereflip * rgpushxto0(H);
2018-07-16 18:06:09 +00:00
2017-07-10 18:47:38 +00:00
ld binv = 99;
2020-01-18 15:03:32 +00:00
vector<ld> dirdist(cwt.at->type);
2018-07-16 18:06:09 +00:00
2022-12-08 18:38:06 +00:00
auto TC0 = tile_center();
for(int i=0; i<cwt.at->type; i++) {
transmatrix T = currentmap->adj(cwt.at, (cwt + i).spin);
2022-12-08 18:38:06 +00:00
ld d1 = geo_dist(U * T * TC0, Centered * P);
2022-12-13 22:51:28 +00:00
ld d2 = geo_dist(U * T * TC0, Centered * C0);
dirdist[i] = d1 - d2;
2017-07-10 18:47:38 +00:00
}
movedir res;
res.d = -1;
for(int i=0; i<cwt.at->type; i++) {
2017-07-10 18:47:38 +00:00
if(dirdist[i] < binv) {
binv = dirdist[i];
res.d = i;
res.subdir = dirdist[(i+1)%cwt.at->type] < dirdist[(i+cwt.at->type-1)%cwt.at->type] ? 1 : -1;
2017-07-10 18:47:38 +00:00
}
}
2017-07-10 18:47:38 +00:00
// if(euclid) bdir = (bdir + 3) % 6;
return res;
}
2019-08-09 19:00:52 +00:00
EX void remission() {
if(!canmove && (cmode & sm::NORMAL) && !game_keys_scroll) showMissionScreen();
}
2019-02-26 10:57:50 +00:00
2019-08-09 19:00:52 +00:00
EX hyperpoint move_destination_vec(int d) {
2023-01-26 23:27:10 +00:00
if(WDIM == 2 && (!embedded_plane || cgi.emb->is_same_in_same())) return spin(-d * 45._deg) * smalltangent();
else if(d&1) return cspin(0, 1, d > 4 ? 45._deg : -45._deg) * smalltangent();
else return cspin(0, 2, d * 45._deg) * smalltangent();
2019-02-26 10:57:50 +00:00
}
2019-08-09 19:00:52 +00:00
EX void movepckeydir(int d) {
2019-05-12 23:57:40 +00:00
DEBB(DF_GRAPH, ("movepckeydir\n"));
2017-07-10 18:47:38 +00:00
// EUCLIDEAN
2019-02-26 10:57:50 +00:00
2019-06-06 17:37:17 +00:00
if(protect_memory()) return;
2019-02-26 10:57:50 +00:00
movedir md = vectodir(move_destination_vec(d));
2017-07-10 18:47:38 +00:00
if(!canmove) movepcto(md), remission(); else movepcto(md);
2017-07-10 18:47:38 +00:00
}
2020-11-19 17:20:06 +00:00
EX void movevrdir(hyperpoint vec) {
movedir md = vectodir(vec);
if(!canmove) movepcto(md), remission(); else movepcto(md);
}
2019-08-09 19:00:52 +00:00
EX void calcMousedest() {
2017-07-10 18:47:38 +00:00
if(mouseout()) return;
if(vid.revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
ld mousedist = hdist(mouseh, tC0(ggmatrix(cwt.at)));
2017-07-10 18:47:38 +00:00
mousedest.d = -1;
cellwalker bcwt = cwt;
2020-01-18 15:03:32 +00:00
vector<ld> dists(cwt.at->type);
2017-07-10 18:47:38 +00:00
shiftmatrix U = ggmatrix(cwt.at);
2018-07-16 18:06:09 +00:00
for(int i=0; i<cwt.at->type; i++) {
2019-11-22 15:37:21 +00:00
transmatrix T = currentmap->adj(cwt.at, i);
dists[i] = hdist(mouseh, U * T * C0);
2018-07-16 18:06:09 +00:00
}
2017-07-10 18:47:38 +00:00
for(int i=0; i<cwt.at->type; i++) if(dists[i] < mousedist) {
2017-07-10 18:47:38 +00:00
mousedist = dists[i];
2019-08-09 12:39:21 +00:00
mousedest.d = cwt.at->c.fix(i - cwt.spin);
2017-07-10 18:47:38 +00:00
mousedest.subdir =
2019-08-09 12:39:21 +00:00
dists[cwt.at->c.fix(i+1)] < dists[cwt.at->c.fix(i-1)] ? 1 : -1;
2017-07-10 18:47:38 +00:00
if(cwt.mirrored)
2019-08-09 12:39:21 +00:00
mousedest.d = cwt.at->c.fix(-mousedest.d),
2017-07-10 18:47:38 +00:00
mousedest.subdir = -mousedest.subdir;
}
if(vid.revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
cwt = bcwt;
}
2019-08-09 19:00:52 +00:00
EX void mousemovement() {
2020-12-30 17:34:13 +00:00
#if CAP_VR
if(WDIM == 3 && vrhr::active() && which_pointer) {
movevrdir(vrhr::vr_direction);
return;
}
#endif
if(GDIM == 3 && !which_pointer) {
if(WDIM == 2) {
if(View[2][2] < -0.75)
movepcto(MD_DROP, 1);
else if(View[2][2] > 0.75)
movepcto(-1, 1);
else
movepckeydir(6);
return;
}
movepckeydir(6);
return;
}
2019-06-06 17:37:17 +00:00
if(protect_memory()) return;
2017-07-10 18:47:38 +00:00
calcMousedest();
if(!canmove) movepcto(mousedest), remission(); else movepcto(mousedest);
2017-07-10 18:47:38 +00:00
lmouseover = NULL;
}
2017-07-22 23:33:27 +00:00
#if CAP_SDLJOY
2019-08-09 20:07:03 +00:00
EX SDL_Joystick* sticks[8];
EX int numsticks;
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX void initJoysticks() {
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
{
printf("Failed to initialize joysticks.\n");
numsticks = 0;
return;
}
2019-05-12 23:57:40 +00:00
DEBB(DF_INIT, ("init joysticks"));
2017-07-10 18:47:38 +00:00
numsticks = SDL_NumJoysticks();
if(numsticks > 8) numsticks = 8;
for(int i=0; i<numsticks; i++) {
sticks[i] = SDL_JoystickOpen(i);
2017-10-16 16:57:14 +00:00
/* printf("axes = %d, balls = %d, buttons = %d, hats = %d\n",
2017-07-10 18:47:38 +00:00
SDL_JoystickNumAxes(sticks[i]),
SDL_JoystickNumBalls(sticks[i]),
SDL_JoystickNumButtons(sticks[i]),
SDL_JoystickNumHats(sticks[i])
2017-10-16 16:57:14 +00:00
); */
2017-07-10 18:47:38 +00:00
}
}
2019-08-09 19:00:52 +00:00
EX void closeJoysticks() {
2019-05-12 23:57:40 +00:00
DEBB(DF_INIT, ("close joysticks"));
2017-07-10 18:47:38 +00:00
for(int i=0; i<numsticks; i++) {
SDL_JoystickClose(sticks[i]), sticks[i] = NULL;
}
numsticks = 0;
}
int joytime;
EX bool joy_ignore_next = false;
2019-08-09 19:00:52 +00:00
EX void checkjoy() {
2019-05-12 23:57:40 +00:00
DEBB(DF_GRAPH, ("check joy"));
2017-07-10 18:47:38 +00:00
if(!DEFAULTCONTROL) return;
ld joyvalue1 = sqr(vid.joyvalue);
ld joyvalue2 = sqr(vid.joyvalue2);
ld jx = joyx;
ld jy = joyy;
ld sq = jx*jx+jy*jy;
static int laststate = 0;
int curstate = sq < joyvalue1 ? 0 : sq < joyvalue2 ? 1 : 2;
if(curstate != laststate) flashMessages(), laststate = curstate;
static int lt = ticks;
int delta = ticks - lt;
lt = ticks;
2017-07-10 18:47:38 +00:00
if(autojoy) {
if(sq < joyvalue1) { if(joydir.d >= 0 && !joy_ignore_next) movepcto(joydir); joydir.d = -1; joytime = 0; joy_ignore_next = false; return; }
2017-07-10 18:47:38 +00:00
if(sq < joyvalue2 && joydir.d == -1) return;
}
else {
if(sq < joyvalue1) { joydir.d = -1; return; }
}
auto new_joydir = vectodir(tangent_length(point2(jx, jy), 0.01));
if(new_joydir.d == joydir.d && new_joydir.subdir == joydir.subdir) {
joytime += delta;
if(joytime > vid.joysmooth) joytime = vid.joysmooth;
}
else {
joytime -= delta;
if(joytime < 0) joytime = -joytime, joydir = new_joydir;
}
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX void checkpanjoy(double t) {
2017-07-10 18:47:38 +00:00
if(shmup::on) return;
if(vid.joypanspeed < 1e-7) return;
if(sqr(panjoyx) + sqr(panjoyy) < sqr(vid.joypanthreshold))
return;
ld jx = panjoyx * t * vid.joypanspeed;
ld jy = panjoyy * t * vid.joypanspeed;
playermoved = false;
View = gpushxto0(hpxy(jx, jy)) * View;
}
#endif
2019-08-10 00:16:48 +00:00
EX bool quitmainloop = false;
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX bool doexiton(int sym, int uni) {
2017-07-10 18:47:38 +00:00
if(sym == SDLK_ESCAPE) return true;
if(sym == SDLK_F10) return true;
2020-11-19 17:20:06 +00:00
if(sym == PSEUDOKEY_EXIT) return true;
if(sym == PSEUDOKEY_RELEASE) return false;
2018-12-15 14:17:06 +00:00
#ifndef FAKE_SDL
if(sym == SDLK_LSHIFT) return false;
if(sym == SDLK_RSHIFT) return false;
if(sym == SDLK_LCTRL) return false;
if(sym == SDLK_RCTRL) return false;
if(sym == SDLK_LALT) return false;
if(sym == SDLK_RALT) return false;
2018-12-15 14:17:06 +00:00
#endif
2017-07-10 18:47:38 +00:00
if(uni != 0) return true;
return false;
}
2019-08-09 20:07:03 +00:00
EX bool didsomething;
2017-07-10 18:47:38 +00:00
typedef SDL_Event eventtype;
2019-08-09 19:00:52 +00:00
EX bool smooth_scrolling = false;
2017-07-10 18:47:38 +00:00
2019-08-24 09:55:45 +00:00
transmatrix zforward_push(ld z) {
if(!sl2) return zpush(z);
transmatrix T = Id;
T[2][3] = z;
return T;
}
2020-04-19 20:42:19 +00:00
EX void zoom_or_fov(ld t) {
2020-11-08 11:09:42 +00:00
if(in_perspective_v()) {
vid.fov *= 180 / max_fov_angle();
2020-04-19 20:42:19 +00:00
auto tanfov = tan(vid.fov * degree / 2);
tanfov *= t;
vid.fov = atan(tanfov) * 2 / degree;
vid.fov *= max_fov_angle() / 180;
2020-04-19 20:42:19 +00:00
}
else
vpconf.scale *= t;
2020-04-19 20:42:19 +00:00
}
2020-09-15 17:12:25 +00:00
EX ld camera_speed = 1;
EX ld camera_rot_speed = 1;
2020-04-19 13:20:56 +00:00
EX void full_forward_camera(ld t) {
2020-04-19 20:42:19 +00:00
if(anyshiftclick)
zoom_or_fov(exp(-t/10.));
else if(GDIM == 3) {
2020-09-15 17:12:25 +00:00
shift_view(ctangent(2, t * camera_speed));
2020-04-19 13:20:56 +00:00
didsomething = true;
playermoved = false;
}
}
2022-08-05 17:20:25 +00:00
EX void full_cstrafe_camera(int dir, ld t) {
2020-11-19 17:20:06 +00:00
if(GDIM == 3) {
2022-08-05 17:20:25 +00:00
shift_view(ctangent(dir, t * camera_speed));
2020-11-19 17:20:06 +00:00
didsomething = true;
playermoved = false;
}
}
2022-08-05 17:20:25 +00:00
EX void full_strafe_camera(ld t) { full_cstrafe_camera(0, t); }
EX void full_ystrafe_camera(ld t) {
if(walking::on) { walking::eye_level += t; if(walking::eye_level < 1e-3) walking::eye_level = 1e-3; }
2022-08-05 17:20:25 +00:00
else full_cstrafe_camera(1, t);
}
2021-11-30 19:56:36 +00:00
EX ld third_person_rotation = 0;
2020-04-19 13:20:56 +00:00
EX void full_rotate_camera(int dir, ld val) {
if(rug::rug_control() && lshiftclick) {
val *= camera_rot_speed;
hyperpoint h;
if(nonisotropic) {
2020-09-16 03:57:05 +00:00
transmatrix T2 = eupush( tC0(view_inverse(View)) );
transmatrix nlp = View * T2;
2020-09-16 03:57:05 +00:00
auto rV = ortho_inverse(nlp) * View;
h = nlp * inverse_exp(shiftless(tC0(rV)));
}
else h = inverse_exp(shiftless(tC0(View)));
shift_view(-h);
rotate_view(cspin(dir, 2, val));
shift_view(h);
}
else if(history::on)
2020-04-19 13:16:43 +00:00
history::lvspeed += (dir?1:-1) * val / 2;
else if(GDIM == 3 && rshiftclick)
2020-09-15 17:12:25 +00:00
shift_view(ctangent(dir, -val * camera_speed)), didsomething = true, playermoved = false; /* -val because shift reverses */
2020-07-03 12:42:33 +00:00
#if CAP_CRYSTAL && CAP_RUG
2020-04-19 13:16:43 +00:00
else if(rug::rug_control() && rug::in_crystal())
2020-09-15 17:12:25 +00:00
crystal::apply_rotation(cspin(dir, 2, val * camera_rot_speed));
2020-04-19 13:16:43 +00:00
#endif
else if(GDIM == 3) {
val *= camera_rot_speed;
2021-11-30 19:56:36 +00:00
if(third_person_rotation) shift_view(ctangent(2, -third_person_rotation)), didsomething = true, playermoved = false;
ld max_angle = 90._deg - 1e-4;
2023-02-21 17:53:42 +00:00
ld max_angle1 = 90._deg - 0.5e-4;
2022-02-26 09:37:18 +00:00
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));
2023-02-21 17:53:42 +00:00
if(dir == 1 && alpha <= max_angle1 && alpha + val > max_angle)
val = max_angle - alpha;
2023-02-21 17:53:42 +00:00
if(dir == 1 && alpha >= -max_angle1 && alpha + val < -max_angle)
val = -max_angle - alpha;
rotate_view(cspin(dir, 2, val));
rotate_view(cspin(1, 2, alpha));
}
else
rotate_view(cspin(dir, 2, val));
2021-11-30 19:56:36 +00:00
if(third_person_rotation) shift_view(ctangent(2, third_person_rotation));
if(!rug::rug_control()) didsomething = true;
}
2020-04-19 13:16:43 +00:00
else
2020-09-15 17:12:25 +00:00
shift_view(ctangent(dir, val * camera_speed)), playermoved = false, didsomething = true;
2020-04-22 11:45:28 +00:00
}
2020-04-19 13:16:43 +00:00
2020-04-19 13:20:56 +00:00
EX void full_rotate_view(ld h, ld v) {
2020-04-19 13:16:43 +00:00
if(history::on && !rug::rug_control())
models::rotation = spin(h * camera_rot_speed) * models::rotation;
2020-04-19 13:16:43 +00:00
else {
2022-12-29 19:50:17 +00:00
rotate_view(cspin(0, 1, v * camera_rot_speed));
2020-04-19 13:16:43 +00:00
didsomething = true;
if(isGravityLand(cwt.at->land) && !rug::rug_control())
playermoved = false;
}
}
2019-08-09 19:00:52 +00:00
EX void handlePanning(int sym, int uni) {
if(mousepan && dual::split([=] { handlePanning(sym, uni); })) return;
2019-08-15 13:05:43 +00:00
if(GDIM == 3) {
2020-09-15 17:12:25 +00:00
if(sym == PSEUDOKEY_WHEELUP) shift_view(ztangent(-0.05*shiftmul) * camera_speed), didsomething = true, playermoved = false;
if(sym == PSEUDOKEY_WHEELDOWN) shift_view(ztangent(0.05*shiftmul) * camera_speed), didsomething = true, playermoved = false;
}
2020-07-03 12:42:33 +00:00
#if CAP_RUG
rug::using_rugview urv;
2020-07-03 12:42:33 +00:00
#endif
2019-08-17 23:31:37 +00:00
2017-07-22 23:33:27 +00:00
#if !ISPANDORA
if(!smooth_scrolling) {
if(sym == SDLK_END) full_forward_camera(-0.2*shiftmul);
if(sym == SDLK_HOME) full_forward_camera(0.2*shiftmul);
if(sym == SDLK_RIGHT) full_rotate_camera(0, -0.2*shiftmul);
if(sym == SDLK_LEFT) full_rotate_camera(0, 0.2*shiftmul);
if(sym == SDLK_UP) full_rotate_camera(1, 0.2*shiftmul);
if(sym == SDLK_DOWN) full_rotate_camera(1, -0.2*shiftmul);
}
2017-07-10 18:47:38 +00:00
#endif
if(!smooth_scrolling) {
if(sym == SDLK_PAGEUP) full_rotate_view(1, cgi.S_step*shiftmul);
if(sym == SDLK_PAGEDOWN) full_rotate_view(-1, -cgi.S_step*shiftmul);
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
if(isGravityLand(cwt.at->land) && !rug::rug_control()) playermoved = false;
}
2017-07-10 18:47:38 +00:00
2019-08-15 13:05:43 +00:00
if(sym == PSEUDOKEY_WHEELUP && GDIM == 2) {
ld jx = (mousex - current_display->xcenter - .0) / current_display->radius / 10;
ld jy = (mousey - current_display->ycenter - .0) / current_display->radius / 10;
2017-07-10 18:47:38 +00:00
playermoved = false;
rotate_view(gpushxto0(hpxy(jx * camera_speed, jy * camera_speed)));
2017-07-10 18:47:38 +00:00
sym = 1;
}
}
2017-10-28 23:57:34 +00:00
#ifdef SCALETUNER
double tunev = .1;
bool handleTune(int sym, int uni) {
if(uni == 'q') { tunev *= .5; return true; }
else if(uni == 'e') { tunev *= 2; return true; }
else if(uni == 'w') bscale7 += tunev*shiftmul;
else if(uni == 's') bscale7 -= tunev*shiftmul;
else if(uni == 'a') brot7 -= tunev*shiftmul;
else if(uni == 'd') brot7 += tunev*shiftmul;
else if(uni == 'i') bscale6 += tunev*shiftmul;
else if(uni == 'k') bscale6 -= tunev*shiftmul;
else if(uni == 'j') brot6 -= tunev*shiftmul;
else if(uni == 'l') brot6 += tunev*shiftmul;
else if(uni == 'z')
bscale7 = bscale6 = 1, brot7 = brot6 = 0;
else return false;
println(hlog, spaced(bscale7, brot7, bscale6, brot6));
2017-10-28 23:57:34 +00:00
return true;
}
#endif
2017-12-01 23:20:35 +00:00
2019-08-09 19:00:52 +00:00
EX purehookset hooks_fixticks;
2020-05-22 12:51:54 +00:00
EX array<int, 8> keys_vi = {{'l', 'n', 'j', 'b', 'h', 'y', 'k', 'u'}};
EX array<int, 8> keys_wasd = {{'d', 'c', 'x', 'z', 'a', 'q', 'w', 'e'}};
EX array<int, 8> keys_numpad = {{SDLK_KP6, SDLK_KP3, SDLK_KP2, SDLK_KP1, SDLK_KP4, SDLK_KP7, SDLK_KP8, SDLK_KP9}};
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX void handleKeyNormal(int sym, int uni) {
2017-07-10 18:47:38 +00:00
2018-02-12 15:20:47 +00:00
if(cheater && sym < 256 && sym > 0) {
if(applyCheat(uni))
uni = sym = 0;
2017-07-10 18:47:38 +00:00
}
2018-12-15 14:20:27 +00:00
#if CAP_SHOT
if(uni == 'A') { pushScreen(shot::menu); uni = sym = 0; }
2018-12-15 14:20:27 +00:00
#endif
2018-12-13 12:33:08 +00:00
2017-07-10 18:47:38 +00:00
if(DEFAULTNOR(sym)) handlePanning(sym, uni);
2017-10-28 23:57:34 +00:00
#ifdef SCALETUNER
if(handleTune(sym, uni)) return;
#endif
2017-07-10 18:47:38 +00:00
2022-08-05 17:20:25 +00:00
if(!(uni >= 'A' && uni <= 'Z') && DEFAULTCONTROL && !game_keys_scroll) {
for(int i=0; i<8; i++)
if(among(sym, keys_vi[i], keys_wasd[i], keys_numpad[i]))
movepckeydir(i);
2017-07-10 18:47:38 +00:00
}
2017-07-24 22:21:36 +00:00
#if ISPANDORA
2017-07-10 18:47:38 +00:00
if(DEFAULTCONTROL) {
if(sym == SDLK_RIGHT) movepckeydir(0);
if(sym == SDLK_LEFT) movepckeydir(4);
2020-04-19 20:43:16 +00:00
if(sym == SDLK_DOWN) movepckeydir(2 + (pandora_leftclick?1:0) - (pandora_rightclick?1:0));
if(sym == SDLK_UP) movepckeydir(6 - (pandora_leftclick?1:0) + (pandora_rightclick?1:0));
2017-07-10 18:47:38 +00:00
}
#endif
2020-10-15 14:33:52 +00:00
#if CAP_COMPLEX2
if(DEFAULTNOR(sym)) {
2017-07-10 18:47:38 +00:00
gmodekeys(sym, uni);
if(uni == 'm' && canmove && (centerover == cwt.at ? mouseover : centerover))
mine::performMarkCommand(mouseover);
2017-07-10 18:47:38 +00:00
}
2020-10-15 14:33:52 +00:00
#endif
2017-07-10 18:47:38 +00:00
2022-08-05 17:20:25 +00:00
if(DEFAULTCONTROL && !game_keys_scroll) {
2017-07-10 18:47:38 +00:00
if(sym == '.' || sym == 's') movepcto(-1, 1);
if((sym == SDLK_DELETE || sym == SDLK_KP_PERIOD || sym == 'g') && uni != 'G' && uni != 'G'-64)
movepcto(MD_DROP, 1);
2023-10-26 10:32:09 +00:00
if(sym == 't' && uni != 'T' && uni != 'T'-64 && canmove) {
cell *target = GDIM == 3 ? mouseover : centerover;
2017-11-03 19:55:18 +00:00
if(playermoved && items[itStrongWind]) {
cell *c = whirlwind::jumpDestination(cwt.at);
if(c) target = c;
2017-07-10 18:47:38 +00:00
}
2023-10-26 10:32:09 +00:00
if(bow::fire_mode) bow::add_fire(target);
else targetRangedOrb(target, roKeyboard);
2017-07-10 18:47:38 +00:00
sym = 0; uni = 0;
}
2023-10-26 10:32:09 +00:00
if(sym == 'f') bow::switch_fire_mode();
2017-07-10 18:47:38 +00:00
}
2022-08-05 17:20:25 +00:00
if(sym == SDLK_KP5 && DEFAULTCONTROL && !game_keys_scroll) movepcto(-1, 1);
2017-07-10 18:47:38 +00:00
if(sym == SDLK_F5) {
2018-05-26 23:06:54 +00:00
#if CAP_DAILY
if(daily::on) daily::handleQuit(1);
else
#endif
2017-07-10 18:47:38 +00:00
if(needConfirmation())
pushScreen(showGameMenu);
else restart_game();
2017-07-10 18:47:38 +00:00
}
2018-05-03 11:14:43 +00:00
if(sym == SDLK_ESCAPE) {
if(viewdists)
viewdists = false;
else
showMissionScreen();
}
2017-07-10 18:47:38 +00:00
if(sym == SDLK_F10) {
2018-05-26 23:06:54 +00:00
#if CAP_DAILY
if(daily::on) daily::handleQuit(2);
else
#endif
if(needConfirmation()) pushScreen(showGameMenu);
2017-07-10 18:47:38 +00:00
else quitmainloop = true;
}
if(uni == 'o' && DEFAULTNOR(sym)) get_o_key().second();
2017-07-22 23:33:27 +00:00
#if CAP_INV
2022-08-05 17:20:25 +00:00
if(uni == 'i' && DEFAULTNOR(sym) && inv::on && !game_keys_scroll)
2017-07-10 18:47:38 +00:00
pushScreen(inv::show);
#endif
2020-04-19 13:30:45 +00:00
if((sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) {
if(rug::rug_control())
rug::reset_view();
else
fullcenter();
}
2017-07-10 18:47:38 +00:00
if(sym == 'v' && DEFAULTNOR(sym))
showMissionScreen();
2020-11-19 17:20:06 +00:00
if(sym == PSEUDOKEY_MENU)
showMissionScreen();
2017-07-10 18:47:38 +00:00
2022-03-27 17:12:10 +00:00
if(sym == PSEUDOKEY_NOHINT)
no_find_player = true;
2017-07-10 18:47:38 +00:00
if(sym == '-' || sym == PSEUDOKEY_WHEELDOWN) {
actonrelease = false;
2019-09-06 06:17:38 +00:00
multi::cpid = 0;
2023-10-26 10:32:09 +00:00
if(bow::fire_mode) {
if(mouseover) bow::add_fire(mouseover);
}
else if(mouseover &&
2019-09-27 15:06:12 +00:00
targetclick && (shmup::on ? numplayers() == 1 && !shmup::pc[0]->dead : true) && targetRangedOrb(mouseover, forcetarget ? roMouseForce : roMouse)) {
2017-07-10 18:47:38 +00:00
}
2023-12-02 10:30:51 +00:00
else if(bow::fire_on_mouse(mouseover)) ;
2017-07-10 18:47:38 +00:00
else if(forcetarget)
;
else if(rug::rugged && rug::renderonce)
;
2017-07-10 18:47:38 +00:00
else if(!DEFAULTCONTROL) {
if(!shmup::on)
multi::mousemovement(mouseover);
}
else if(handleCompass()) ;
else
mousemovement();
2017-07-10 18:47:38 +00:00
}
if(sym == SDLK_F1) gotoHelp(help);
2019-06-06 17:37:17 +00:00
if(sym == PSEUDOKEY_MEMORY) pushScreen(show_memory_menu);
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool need_mouseh = false;
2019-08-09 19:00:52 +00:00
EX void fix_mouseh() {
if(0) ;
2020-12-31 13:43:49 +00:00
#if CAP_RUG
else if(rug::rugged) {
if(need_mouseh || (vrhr::active() && which_pointer))
mouseh = rug::gethyper(mousex, mousey);
}
#endif
#if CAP_VR
else if(vrhr::active() && which_pointer && !vrhr::targeting_menu)
vrhr::compute_point(which_pointer, mouseh, mouseover, vrhr::pointer_distance);
#endif
2019-05-28 23:09:38 +00:00
else {
if(dual::state) {
if(cmode & (sm::NORMAL | sm::DRAW | sm::MAP)) {
dual::main_side = (mousex >= current_display->xcenter);
dual::switch_to(dual::main_side);
}
dual::in_subscreen([=] () { calcparam(); mouseh = gethyper(mousex, mousey); });
}
else mouseh = gethyper(mousex, mousey);
}
2017-12-27 13:09:39 +00:00
need_mouseh = false;
}
2019-08-09 19:00:52 +00:00
EX void handlekey(int sym, int uni) {
2017-07-10 18:47:38 +00:00
if(callhandlers(false, hooks_handleKey, sym, uni)) return;
keyhandler(sym, uni);
}
2019-08-09 20:07:03 +00:00
EX void resize_screen_to(int x, int y);
2017-07-22 23:33:27 +00:00
#if !CAP_SDL
2019-08-09 19:00:52 +00:00
EX void mainloopiter() { printf("(compiled without SDL -- no action)\n"); quitmainloop = true; }
2019-08-09 20:07:03 +00:00
#endif
#if CAP_SDL
2017-07-10 18:47:38 +00:00
// Warning: a very long function! todo: refactor
int cframelimit = 1000;
2019-08-09 19:00:52 +00:00
EX void resize_screen_to(int x, int y) {
dual::split_or_do([&] {
vid.killreduction = 0;
if(vid.want_fullscreen) return;
if(vid.relative_window_size) {
vid.window_rel_x = x * 1. / vid.xscr;
vid.window_rel_y = y * 1. / vid.yscr;
}
else {
vid.window_x = x;
vid.window_y = y;
}
});
2021-03-09 15:04:02 +00:00
apply_screen_settings();
}
int lastframe;
2022-08-05 17:20:25 +00:00
EX int sc_ticks, sc_ticks2;
2020-09-15 17:12:54 +00:00
EX bool mouseaiming(bool shmupon) {
return
(GDIM == 3 && !shmupon) || (rug::rugged && (lctrlclick ^ rug::mouse_control_rug)) || (cmode & sm::MOUSEAIM);
2020-09-15 17:12:54 +00:00
}
2022-08-05 17:20:25 +00:00
/* visualization only -- the HyperRogue movement keys should move the camera */
EX bool game_keys_scroll;
2023-08-06 09:47:21 +00:00
EX purehookset hooks_control;
2019-08-09 19:00:52 +00:00
EX void mainloopiter() {
2020-11-08 10:49:19 +00:00
GLWRAP;
2019-05-12 23:57:40 +00:00
DEBB(DF_GRAPH, ("main loop\n"));
2017-07-10 18:47:38 +00:00
2017-07-22 23:33:27 +00:00
#if !CAP_SDLGFX && !CAP_GL
2017-07-10 18:47:38 +00:00
vid.wallmode = 0;
vid.monmode = 0;
#endif
2020-11-19 17:20:06 +00:00
#if CAP_VR
vrhr::vr_shift();
#endif
check_cgi();
cgi.require_basics();
2020-11-19 17:20:06 +00:00
2017-07-10 18:47:38 +00:00
optimizeview();
models::configure();
2018-10-23 14:58:19 +00:00
lastt = ticks;
2017-07-10 18:47:38 +00:00
ticks = SDL_GetTicks();
2017-12-01 23:20:35 +00:00
callhooks(hooks_fixticks);
2017-07-10 18:47:38 +00:00
timetowait = lastframe + 1000 / cframelimit - ticks;
2017-07-10 18:47:38 +00:00
cframelimit = vid.framelimit;
if(outoffocus && cframelimit > 10) cframelimit = 10;
2017-07-12 17:50:39 +00:00
bool normal = cmode & sm::NORMAL;
2018-07-09 18:38:20 +00:00
shmup::turn(ticks - lastt);
2017-07-10 18:47:38 +00:00
2018-07-09 18:38:20 +00:00
if(!shmup::on && (multi::alwaysuse || multi::players > 1) && normal)
2017-07-10 18:47:38 +00:00
timetowait = 0, multi::handleMulti(ticks - lastt);
2019-05-05 15:31:55 +00:00
if(vid.sspeed >= 5 && gmatrix.count(cwt.at) && !elliptic && !shmup::on) {
cwtV = gmatrix[cwt.at] * ddspin(cwt.at, cwt.spin);
2017-07-10 18:47:38 +00:00
if(cwt.mirrored) playerV = playerV * Mirror;
}
2019-02-26 01:35:30 +00:00
mousepan = cmode & sm::NORMAL;
if((cmode & sm::PANNING) && !hiliteclick) mousepan = true;
if(cmode & sm::MOUSEAIM) mousepan = true;
2022-04-30 10:02:05 +00:00
if(cmode & sm::SHOWCURSOR) mousepan = false;
2020-09-15 17:12:54 +00:00
mousepan = mousepan && mouseaiming(false) && mouseaim_sensitivity;
2019-02-26 01:35:30 +00:00
if(mousepan != oldmousepan) {
oldmousepan = mousepan;
2019-06-28 07:47:10 +00:00
#if CAP_MOUSEGRAB
2019-02-26 01:35:30 +00:00
if(mousepan) {
#if CAP_SDL2
SDL_SetRelativeMouseMode(SDL_TRUE);
#else
2019-02-26 01:35:30 +00:00
SDL_WM_GrabInput(SDL_GRAB_ON);
SDL_ShowCursor(SDL_DISABLE);
#endif
2019-02-26 01:35:30 +00:00
mouseaim_x = mouseaim_y = 0;
}
else {
#if CAP_SDL2
SDL_SetRelativeMouseMode(SDL_FALSE);
#else
2019-02-26 01:35:30 +00:00
SDL_WM_GrabInput( SDL_GRAB_OFF );
SDL_ShowCursor(SDL_ENABLE);
SDL_WarpMouse(vid.xres/2, vid.yres/2);
#endif
mouseaim_x = mouseaim_y = 0;
2019-02-26 01:35:30 +00:00
}
2019-06-28 07:47:10 +00:00
#endif
2019-02-26 01:35:30 +00:00
}
2017-07-10 18:47:38 +00:00
2018-09-05 22:48:11 +00:00
#if ISWEB
timetowait = 0;
#endif
2017-07-10 18:47:38 +00:00
if(timetowait > 0)
SDL_Delay(timetowait);
else {
ors::check_orientation();
2017-07-12 17:50:39 +00:00
if(cmode & sm::CENTER) {
ld aspd = (ticks - lastt) / 1000.0 * exp(vid.sspeed);
2017-07-10 18:47:38 +00:00
if(playermoved && vid.sspeed > -4.99 && !outoffocus)
centerpc(aspd);
2019-08-15 13:05:43 +00:00
else if(GDIM == 3)
spinEdge(aspd);
2018-09-04 21:27:27 +00:00
#if CAP_SDLJOY
2017-07-10 18:47:38 +00:00
if(panjoyx || panjoyy)
checkpanjoy((ticks - lastt) / 1000.0);
2018-09-04 21:27:27 +00:00
#endif
2017-07-10 18:47:38 +00:00
}
tortoise::updateVals(ticks - lastt);
frames++;
if(!outoffocus) {
drawscreen();
2020-09-11 09:14:32 +00:00
need_refresh = false;
2017-07-10 18:47:38 +00:00
}
lastframe = ticks;
2017-07-10 18:47:38 +00:00
}
wheelclick = false;
getcshift = 1;
#if CAP_SDL2
const Uint8 *keystate = SDL_GetKeyboardState(NULL);
pandora_rightclick = keystate[SDL_SCANCODE_RCTRL];
pandora_leftclick = keystate[SDL_SCANCODE_RSHIFT];
lshiftclick = keystate[SDL_SCANCODE_LSHIFT];
rshiftclick = keystate[SDL_SCANCODE_RSHIFT];
lctrlclick = keystate[SDL_SCANCODE_LCTRL];
rctrlclick = keystate[SDL_SCANCODE_RCTRL];
hiliteclick = keystate[SDL_SCANCODE_LALT] | keystate[SDL_SCANCODE_RALT];
if(keystate[SDL_SCANCODE_LSHIFT] || keystate[SDL_SCANCODE_RSHIFT]) getcshift = -1;
if(keystate[SDL_SCANCODE_LCTRL] || keystate[SDL_SCANCODE_RCTRL]) getcshift /= 10;
if(keystate[SDL_SCANCODE_LALT] || keystate[SDL_SCANCODE_RALT]) getcshift *= 10;
#else
2017-07-10 18:47:38 +00:00
Uint8 *keystate = SDL_GetKeyState(NULL);
2020-04-19 20:43:16 +00:00
pandora_rightclick = keystate[SDLK_RCTRL];
pandora_leftclick = keystate[SDLK_RSHIFT];
2017-07-10 18:47:38 +00:00
lshiftclick = keystate[SDLK_LSHIFT];
2020-04-19 20:43:16 +00:00
rshiftclick = keystate[SDLK_RSHIFT];
lctrlclick = keystate[SDLK_LCTRL];
rctrlclick = keystate[SDLK_RCTRL];
2017-07-10 18:47:38 +00:00
hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT];
2017-07-10 18:47:38 +00:00
if(keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) getcshift = -1;
if(keystate[SDLK_LCTRL] || keystate[SDLK_RCTRL]) getcshift /= 10;
if(keystate[SDLK_LALT] || keystate[SDLK_RALT]) getcshift *= 10;
#endif
anyshiftclick = lshiftclick | rshiftclick;
anyctrlclick = lctrlclick | rctrlclick;
forcetarget = anyshiftclick;
2017-07-10 18:47:38 +00:00
didsomething = false;
if(vid.shifttarget&1) {
2020-04-19 20:43:16 +00:00
#if ISPANDORA
targetclick = pandora_leftclick | pandora_rightclick;
pandora_leftclick = pandora_rightclick = 0;
#else
2017-07-10 18:47:38 +00:00
targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT];
2020-04-19 20:43:16 +00:00
#endif
2017-07-10 18:47:38 +00:00
}
else {
targetclick = true;
}
2017-07-22 23:33:27 +00:00
#if CAP_SDLAUDIO
2017-07-10 18:47:38 +00:00
if(audio) handlemusic();
#endif
2019-06-06 17:37:17 +00:00
apply_memory_reserve();
2017-07-10 18:47:38 +00:00
SDL_Event ev;
2019-05-12 23:57:40 +00:00
DEBB(DF_GRAPH, ("polling for events\n"));
2017-07-10 18:47:38 +00:00
2020-11-19 17:20:06 +00:00
#if CAP_VR
if(vrhr::active() && !shmup::on) {
2020-12-31 17:01:38 +00:00
static int lastticks = ticks;
ld t = (ticks - lastticks) * shiftmul / 400;
lastticks = ticks;
2020-11-19 17:20:06 +00:00
rug::using_rugview urv;
dynamicval<bool> ds(didsomething, didsomething);
using namespace vrhr;
if(vrhr::hsm == vrhr::eHeadset::model_viewing) {
E4;
transmatrix T = hmd_at * inverse(hmd_ref_at);
T =
2020-12-31 17:01:38 +00:00
cspin(0, 2, -vraim_x * camera_speed * t) *
cspin(1, 2, vraim_y * camera_speed * t) *
cpush(0, -vrgo_x * camera_speed * t) *
cpush(2, -vrgo_y * camera_speed * t) *
T;
hmd_ref_at = inverse(T) * hmd_at;
}
else if(in_perspective_v()) {
2020-12-31 17:01:38 +00:00
if(vraim_x) full_rotate_camera(0, -vraim_x * t);
if(vraim_y) full_rotate_camera(1, vraim_y * t);
if(vrgo_y) full_forward_camera(-vrgo_y * t);
if(vrgo_x) full_strafe_camera(-vrgo_x * t);
}
2020-11-19 17:20:06 +00:00
}
#endif
if(mouseaiming(shmup::on) && !(cmode & sm::MOUSEAIM)) {
2019-06-28 07:47:10 +00:00
#if CAP_MOUSEGRAB
2020-04-21 23:44:53 +00:00
rug::using_rugview urv;
dynamicval<bool> ds(didsomething, didsomething);
2020-04-19 20:02:22 +00:00
full_rotate_camera(0, -mouseaim_x);
full_rotate_camera(1, -mouseaim_y);
2019-02-26 01:35:30 +00:00
mouseaim_x = mouseaim_y = 0;
2019-06-28 07:47:10 +00:00
#endif
2019-02-26 01:35:30 +00:00
}
if(smooth_scrolling && !shmup::on && (cmode & (sm::NORMAL | sm::PANNING))) {
rug::using_rugview urv;
auto& lastticks = sc_ticks;
ld t = (ticks - lastticks) * shiftmul / 1000.;
lastticks = ticks;
#if CAP_SDL2
if(keystate[SDL_SCANCODE_END] && GDIM == 3 && DEFAULTNOR(SDL_SCANCODE_END)) full_forward_camera(-t);
if(keystate[SDL_SCANCODE_HOME] && GDIM == 3 && DEFAULTNOR(SDL_SCANCODE_HOME)) full_forward_camera(t);
if(keystate[SDL_SCANCODE_RIGHT] && DEFAULTNOR(SDL_SCANCODE_RIGHT)) full_rotate_camera(0, -t);
if(keystate[SDL_SCANCODE_LEFT] && DEFAULTNOR(SDL_SCANCODE_LEFT)) full_rotate_camera(0, t);
if(keystate[SDL_SCANCODE_UP] && DEFAULTNOR(SDL_SCANCODE_UP)) full_rotate_camera(1, t);
if(keystate[SDL_SCANCODE_DOWN] && DEFAULTNOR(SDL_SCANCODE_DOWN)) full_rotate_camera(1, -t);
if(keystate[SDL_SCANCODE_PAGEUP] && DEFAULTNOR(SDL_SCANCODE_PAGEUP)) full_rotate_view(t / degree, t);
if(keystate[SDL_SCANCODE_PAGEDOWN] && DEFAULTNOR(SDL_SCANCODE_PAGEDOWN)) full_rotate_view(-t / degree, -t);
#else
2020-04-19 13:20:56 +00:00
if(keystate[SDLK_END] && GDIM == 3 && DEFAULTNOR(SDLK_END)) full_forward_camera(-t);
if(keystate[SDLK_HOME] && GDIM == 3 && DEFAULTNOR(SDLK_HOME)) full_forward_camera(t);
if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) full_rotate_camera(0, -t);
if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) full_rotate_camera(0, t);
if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) full_rotate_camera(1, t);
if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN)) full_rotate_camera(1, -t);
if(keystate[SDLK_PAGEUP] && DEFAULTNOR(SDLK_PAGEUP)) full_rotate_view(t / degree, t);
if(keystate[SDLK_PAGEDOWN] && DEFAULTNOR(SDLK_PAGEDOWN)) full_rotate_view(-t / degree, -t);
#endif
}
else sc_ticks = ticks;
if(game_keys_scroll && !shmup::on && (cmode & sm::NORMAL) && !keystate[SDLK_LALT] && !keystate[SDLK_RALT]) {
2022-08-05 17:20:25 +00:00
rug::using_rugview urv;
auto& lastticks = sc_ticks2;
ld t = (ticks - lastticks) * shiftmul / 1000.;
lastticks = ticks;
#define dkey(x) keystate[int(x)] && DEFAULTNOR(x)
if(dkey('d')) full_rotate_camera(0, -t);
if(dkey('a')) full_rotate_camera(0, t);
if(dkey('w')) full_rotate_camera(1, t);
if(dkey('s')) full_rotate_camera(1, -t);
if(dkey('q')) full_rotate_view(t / degree, t);
if(dkey('e')) full_rotate_view(-t / degree, -t);
if(GDIM == 3 && dkey('i')) full_forward_camera(-t);
if(GDIM == 3 && dkey('k')) full_forward_camera(t);
if(GDIM == 3 && dkey('l')) full_strafe_camera(-t);
if(GDIM == 3 && dkey('j')) full_strafe_camera(t);
if(GDIM == 3 && dkey('h')) full_ystrafe_camera(-t);
if(GDIM == 3 && dkey('y')) full_ystrafe_camera(t);
#undef dkey
2022-08-05 17:20:25 +00:00
}
2020-11-19 17:20:06 +00:00
#if CAP_VR
vrhr::vr_control();
#endif
achievement_pump();
2020-11-19 17:20:06 +00:00
2023-08-06 09:47:21 +00:00
callhooks(hooks_control);
2020-11-19 17:20:06 +00:00
for(auto d: dialog::key_queue) {
println(hlog, "handling key ", d);
handlekey(d, d);
2022-04-26 11:27:09 +00:00
dialog::key_queue.erase(dialog::key_queue.begin());
break;
2020-11-19 17:20:06 +00:00
}
while(SDL_PollEvent(&ev)) handle_event(ev);
fix_mouseh();
#if CAP_SDLJOY
if(joydir.d != -1) checkjoy();
#endif
}
2020-09-11 09:14:32 +00:00
EX bool need_refresh;
2019-08-09 19:00:52 +00:00
EX void handle_event(SDL_Event& ev) {
bool normal = cmode & sm::NORMAL;
2019-05-12 23:57:40 +00:00
DEBB(DF_GRAPH, ("got event type #%d\n", ev.type));
2017-07-10 18:47:38 +00:00
int sym = 0;
int uni = 0;
shiftmul = 1;
/* if(ev.type == SDL_JOYDEVICEADDED || ev.type == SDL_JOYDEVICEREMOVED) {
joyx = joyy = 0;
panjoyx = panjoyy = 0;
closeJoysticks();
initJoysticks();
2017-10-15 23:22:24 +00:00
} */
2017-07-10 18:47:38 +00:00
#if CAP_SDL2
if(ev.type == SDL_WINDOWEVENT) {
auto w = ev.window.event;
if(w == SDL_WINDOWEVENT_ENTER)
outoffocus = false;
if(w == SDL_WINDOWEVENT_LEAVE)
outoffocus = true;
if(w == SDL_WINDOWEVENT_EXPOSED)
drawscreen();
if(w == SDL_WINDOWEVENT_RESIZED)
resize_screen_to(ev.window.data1, ev.window.data2);
}
#else
2017-07-10 18:47:38 +00:00
if(ev.type == SDL_ACTIVEEVENT) {
if(ev.active.state & SDL_APPINPUTFOCUS) {
if(ev.active.gain) {
outoffocus = false;
}
else {
outoffocus = true;
}
}
}
if(ev.type == SDL_VIDEORESIZE)
resize_screen_to(ev.resize.w, ev.resize.h);
2017-07-10 18:47:38 +00:00
if(ev.type == SDL_VIDEOEXPOSE) {
drawscreen();
}
#endif
2017-10-15 23:22:24 +00:00
#if CAP_SDLJOY
2017-07-16 21:00:55 +00:00
if(ev.type == SDL_JOYAXISMOTION && normal && DEFAULTCONTROL) {
2017-07-10 18:47:38 +00:00
if(ev.jaxis.which == 0) {
if(ev.jaxis.axis == 0)
joyx = ev.jaxis.value;
else if(ev.jaxis.axis == 1)
joyy = ev.jaxis.value;
else if(ev.jaxis.axis == 3)
panjoyx = ev.jaxis.value;
else if(ev.jaxis.axis == 4)
panjoyy = ev.jaxis.value;
checkjoy();
// printf("panjoy = %d,%d\n", panjoyx, panjoyy);
}
else {
if(ev.jaxis.axis == 0)
panjoyx = ev.jaxis.value;
else
panjoyy = ev.jaxis.value;
}
}
2017-07-12 17:50:39 +00:00
if(joyhandler && joyhandler(ev)) ;
2017-07-10 18:47:38 +00:00
2017-07-16 21:00:55 +00:00
else if(ev.type == SDL_JOYHATMOTION && !normal) {
if(ev.jhat.value == SDL_HAT_UP) sym = SDLK_UP;
if(ev.jhat.value == SDL_HAT_DOWN) sym = SDLK_DOWN;
if(ev.jhat.value == SDL_HAT_LEFT) sym = SDLK_LEFT;
if(ev.jhat.value == SDL_HAT_RIGHT) sym = SDLK_RIGHT;
}
else if(ev.type == SDL_JOYBUTTONDOWN && normal && DEFAULTCONTROL) {
2017-07-10 18:47:38 +00:00
flashMessages();
movepcto(joydir);
joy_ignore_next = true;
2017-07-10 18:47:38 +00:00
checkjoy();
}
2017-07-16 21:00:55 +00:00
else if(ev.type == SDL_JOYBUTTONDOWN && !normal) {
sym = uni = SDLK_RETURN;
}
#endif
2017-07-16 21:00:55 +00:00
2017-07-10 18:47:38 +00:00
if(ev.type == SDL_KEYDOWN) {
flashMessages();
mousing = false;
sym = ev.key.keysym.sym;
#if CAP_SDL2
uni = ev.key.keysym.sym;
if(uni >= 'a' && uni <= 'z') {
if(ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) uni -= 32;
else if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) uni -= 96;
}
#else
2017-07-10 18:47:38 +00:00
uni = ev.key.keysym.unicode;
2022-06-23 07:34:21 +00:00
if(uni == 0 && (sym >= 'a' && sym <= 'z')) {
if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) uni = sym - 96;
}
2017-07-10 18:47:38 +00:00
if(ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) shiftmul = -1;
if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) shiftmul /= 10;
#endif
numlock_on = ev.key.keysym.mod & KMOD_NUM;
2017-07-10 18:47:38 +00:00
if(sym == SDLK_RETURN && (ev.key.keysym.mod & (KMOD_LALT | KMOD_RALT))) {
sym = 0; uni = 0;
vid.want_fullscreen = !vid.want_fullscreen;
apply_screen_settings();
2017-07-10 18:47:38 +00:00
}
}
dialog::handleZooming(ev);
2017-07-12 17:50:39 +00:00
if(sym == SDLK_F1 && normal && playermoved)
2017-07-10 18:47:38 +00:00
help = "@";
2017-07-12 17:50:39 +00:00
bool rollchange = (cmode & sm::OVERVIEW) && getcstat >= 2000 && cheater;
2017-07-10 18:47:38 +00:00
if(ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP SDL12(, || ev.type == SDL_MOUSEWHEEL)) {
2017-08-06 12:50:16 +00:00
mousepressed = ev.type == SDL_MOUSEBUTTONDOWN;
if(mousepressed) flashMessages();
2017-07-10 18:47:38 +00:00
mousing = true;
which_pointer = 0;
bool was_holdmouse = holdmouse;
holdmouse = false;
2022-10-21 16:49:33 +00:00
invslider = false;
2017-07-10 18:47:38 +00:00
bool down = ev.type == SDL_MOUSEBUTTONDOWN SDL12(, || ev.type == SDL_MOUSEWHEEL);
bool up = ev.type == SDL_MOUSEBUTTONUP;
2017-08-06 12:50:16 +00:00
bool act = false;
if(vid.quickmouse || getcstat == PSEUDOKEY_LIST_SLIDER) {
act = down;
2017-07-10 18:47:38 +00:00
}
2017-08-06 12:50:16 +00:00
else {
act = actonrelease && up;
actonrelease = down;
2017-12-30 16:00:35 +00:00
}
fix_mouseh();
if(was_holdmouse && up)
sym = uni = PSEUDOKEY_RELEASE;
2020-04-19 20:43:16 +00:00
/* simulate RMB and MMB for Mac users etc. */
if(ev.button.button == SDL_BUTTON_LEFT) {
if(ISPANDORA ? pandora_rightclick : lctrlclick)
ev.button.button = SDL_BUTTON_MIDDLE;
else if((ISPANDORA ? pandora_leftclick : lshiftclick) && !(vid.shifttarget&1) && !(cmode & sm::MAP))
2020-04-19 20:43:16 +00:00
ev.button.button = SDL_BUTTON_RIGHT;
}
2017-08-06 12:50:16 +00:00
if(!act) ;
2017-07-10 18:47:38 +00:00
2020-04-19 20:43:16 +00:00
else if(ev.button.button==SDL_BUTTON_RIGHT)
2017-07-10 18:47:38 +00:00
sym = SDLK_F1;
2020-04-19 20:43:16 +00:00
else if(ev.button.button==SDL_BUTTON_MIDDLE)
2017-07-10 18:47:38 +00:00
sym = 1, didsomething = true;
2017-08-06 12:50:16 +00:00
else if(ev.button.button == SDL_BUTTON_LEFT) {
2017-07-10 18:47:38 +00:00
sym = getcstat, uni = getcstat, shiftmul = getcshift;
}
2017-08-06 12:50:16 +00:00
else if(SDL12(ev.button.button==SDL_BUTTON_WHEELDOWN || ev.button.button == SDL_BUTTON_WHEELUP, ev.type == SDL_MOUSEWHEEL)) {
#if CAP_SDL2
ld dir = ev.wheel.y * 0.25;
#else
2020-04-19 20:43:16 +00:00
ld dir = ev.button.button == SDL_BUTTON_WHEELUP ? 0.25 : -0.25;
#endif
2020-04-19 20:43:16 +00:00
if(lshiftclick && rshiftclick && !rug::rugged && GDIM == 2) {
mapeditor::scaleall(pow(2, dir), lctrlclick);
pconf.alpha *= pow(2, dir);
}
2020-04-19 21:01:09 +00:00
else if(lshiftclick && GDIM == 2)
2020-04-19 20:43:16 +00:00
mapeditor::scaleall(pow(2, dir), lctrlclick);
else if(rshiftclick && !rug::rugged && GDIM == 2)
pconf.alpha -= dir;
else if(lctrlclick) {
if(dir>0) {
pconf.xposition += (.0 + mousex - current_display->xcenter) / vpconf.scale / current_display->scrsize;
pconf.yposition += (.0 + mousey - current_display->ycenter) / vpconf.scale / current_display->scrsize;
}
else
pconf.xposition = pconf.yposition = 0;
}
else if(rollchange) {
2020-04-19 20:43:16 +00:00
sym = getcstat, uni = getcstat, shiftmul = -dir*4*getcshift, wheelclick = true;
2017-08-06 12:50:16 +00:00
}
else {
2020-04-19 20:43:16 +00:00
sym = uni = dir > 0 ? PSEUDOKEY_WHEELUP : PSEUDOKEY_WHEELDOWN;
2017-08-06 12:50:16 +00:00
}
2017-07-10 18:47:38 +00:00
}
}
if(ev.type == SDL_MOUSEMOTION) {
mouseoh = mouseh;
2017-12-30 16:00:35 +00:00
int lmousex = mousex, lmousey = mousey;
2017-07-10 18:47:38 +00:00
mousing = true;
which_pointer = 0;
2017-07-10 18:47:38 +00:00
mousemoved = true;
mousex = ev.motion.x;
mousey = ev.motion.y;
2019-02-26 01:35:30 +00:00
if(mousepan) {
mousex = vid.xres/2;
mousey = vid.yres/2;
mouseaim_x += ev.motion.xrel * mouseaim_sensitivity;
mouseaim_y += ev.motion.yrel * mouseaim_sensitivity;
}
need_mouseh = true;
2017-07-10 18:47:38 +00:00
2017-12-30 16:00:35 +00:00
if(holdmouse && getcstat == '-') sym = uni = getcstat, fix_mouseh();
2017-07-10 18:47:38 +00:00
2020-04-19 20:43:16 +00:00
if(((SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2()) {
2017-12-30 16:00:35 +00:00
fix_mouseh();
2020-04-19 20:43:16 +00:00
if(lctrlclick) {
pconf.xposition += (mousex - lmousex) * 1. / current_display->scrsize,
pconf.yposition += (mousey - lmousey) * 1. / current_display->scrsize;
2017-12-30 16:00:35 +00:00
}
else if(mouseh[LDIM] < 50 && mouseoh[LDIM] < 50) {
2017-12-30 16:00:35 +00:00
panning(mouseoh, mouseh);
}
2017-07-10 18:47:38 +00:00
}
#ifdef SIMULATE_JOYSTICK
// pretend that both joysticks are present
stick = panstick = (SDL_Joystick*) (&vid);
panjoyx = 20 * (mousex - current_display->xcenter);
panjoyy = 20 * (mousey - current_display->ycenter);
2017-07-10 18:47:38 +00:00
checkjoy();
#endif
if(mousepressed && inslider) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
}
}
if(ev.type == SDL_QUIT) {
2018-05-26 23:06:54 +00:00
#if CAP_DAILY
if(daily::on) daily::handleQuit(3);
else
#endif
2017-07-12 17:50:39 +00:00
if(needConfirmation() && !(cmode & sm::MISSION)) showMissionScreen();
2017-07-10 18:47:38 +00:00
else quitmainloop = true;
}
2020-04-19 20:43:16 +00:00
if(sym == SDLK_F4 && anyshiftclick) {
nomap = !nomap;
sym = 0;
}
2020-04-19 20:43:16 +00:00
if(sym == SDLK_F2 && anyshiftclick) {
nohud = !nohud;
sym = 0;
}
2020-04-19 20:43:16 +00:00
if(sym == SDLK_F3 && anyshiftclick) {
nofps = !nofps;
sym = 0;
}
2020-09-11 09:14:32 +00:00
if(sym || uni) {
if(need_refresh) {
just_refreshing = true;
screens.back()();
just_refreshing = false;
}
need_refresh = true;
}
2017-12-30 16:00:35 +00:00
2017-07-10 18:47:38 +00:00
handlekey(sym, uni);
}
#endif
2019-08-09 19:00:52 +00:00
EX void mainloop() {
2017-08-17 23:15:48 +00:00
if(noGUI) return;
2017-07-10 18:47:38 +00:00
lastt = 0;
2017-07-22 23:33:27 +00:00
#if ISWEB
2017-07-10 18:47:38 +00:00
initweb();
emscripten_set_main_loop(mainloopiter, 0, true);
#else
while(!quitmainloop) mainloopiter();
#endif
}
#if ISMOBILE
2019-08-09 19:00:52 +00:00
EX void displayabutton(int px, int py, string s, int col) {
2017-07-10 18:47:38 +00:00
// TMP
int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2;
2017-11-07 12:40:56 +00:00
int rad = (int) realradius();
if(vid.stereo_mode == sLR) rad = 99999;
2017-11-07 12:40:56 +00:00
int vrx = min(rad, vid.xres/2 - 40);
int vry = min(rad, min(current_display->ycenter, vid.yres - current_display->ycenter) - 20);
int x = current_display->xcenter + px * vrx;
int y = current_display->ycenter + py * (vry - siz/2);
2017-07-10 18:47:38 +00:00
int vrr = int(hypot(vrx, vry) * sqrt(2.));
if(gtouched && !mouseover
&& abs(mousex - current_display->xcenter) < vrr
&& abs(mousey - current_display->ycenter) < vrr
&& hypot(mousex-current_display->xcenter, mousey-current_display->ycenter) > vrr
&& px == (mousex > current_display->xcenter ? 1 : -1)
&& py == (mousey > current_display->ycenter ? 1 : -1)
2017-07-10 18:47:38 +00:00
) col = 0xFF0000;
if(displayfr(x, y, 0, siz, s, col, 8+8*px))
buttonclicked = true;
}
#endif
2019-08-09 19:00:52 +00:00
EX bool interpret_as_direction(int sym, int uni) {
2018-12-15 14:17:06 +00:00
#ifdef FAKE_SDL
return false;
#else
return (sym >= SDLK_KP0 && sym <= SDLK_KP9 && !numlock_on);
2018-12-15 14:17:06 +00:00
#endif
}
2019-08-09 19:00:52 +00:00
EX int get_direction_key(int sym, int uni) {
if(interpret_as_direction(sym, uni)) {
2018-12-15 14:17:06 +00:00
#ifndef FAKE_SDL
if(sym == SDLK_KP1) return SDLK_END;
if(sym == SDLK_KP2) return SDLK_DOWN;
if(sym == SDLK_KP3) return SDLK_PAGEDOWN;
if(sym == SDLK_KP4) return SDLK_LEFT;
if(sym == SDLK_KP6) return SDLK_RIGHT;
if(sym == SDLK_KP7) return SDLK_HOME;
if(sym == SDLK_KP8) return SDLK_UP;
if(sym == SDLK_KP8) return SDLK_PAGEUP;
2018-12-15 14:17:06 +00:00
#endif
return 0;
}
return sym;
}
2019-08-09 19:00:52 +00:00
EX bool gmodekeys(int sym, int uni) {
#if CAP_RUG
2019-03-30 22:45:28 +00:00
if(rug::rugged && rug::handlekeys(sym, uni)) return true;
#endif
2019-03-30 22:45:28 +00:00
if(NUMBERKEY == '6') { vid.grid = !vid.grid; return true; }
if(NUMBERKEY == '7') { vid.darkhepta = !vid.darkhepta; return true; }
2021-02-01 10:20:22 +00:00
if(NUMBERKEY == '1')
pushScreen(models::quick_model);
2019-08-15 13:05:43 +00:00
if(GDIM == 2) {
2021-02-01 10:20:22 +00:00
if(NUMBERKEY == '5') { vid.wallmode += 60 + (shiftmul > 0 ? 1 : -1); vid.wallmode %= 7; }
2020-09-15 18:39:15 +00:00
else if((NUMBERKEY == '8' && hiliteclick) || NUMBERKEY == 508) {
vid.highlightmode += 60 + (shiftmul > 0 ? 1 : -1); vid.highlightmode %= 3;
}
else if(NUMBERKEY == '8') {
vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 4;
}
2019-03-30 22:45:28 +00:00
else if(uni == '%') {
if(vid.wallmode == 0) vid.wallmode = 6;
vid.wallmode--;
}
2019-03-30 22:45:28 +00:00
else return false;
return true;
}
2019-02-27 12:30:09 +00:00
else {
2021-02-01 10:20:22 +00:00
if(NUMBERKEY == '5') { vid.wallmode = vid.wallmode == 5 ? 4 : 5; }
else if(NUMBERKEY == '8') {
if(vid.monmode == 0) vid.monmode = 1;
else if(vid.monmode == 1) vid.monmode = 3;
else vid.monmode = 0;
}
2019-03-30 22:45:28 +00:00
else return false;
return true;
2019-02-27 12:30:09 +00:00
}
2017-07-12 16:03:53 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool haveMobileCompass() {
#if ISMOBILE
if(longclick) return false;
#else
if(forcetarget) return false;
#endif
2019-08-15 13:05:43 +00:00
if(GDIM == 3) return false;
2018-06-22 12:47:24 +00:00
return canmove && !shmup::on && vid.mobilecompasssize > 0 && isize(screens) == 1;
}
2019-08-09 19:00:52 +00:00
EX bool handleCompass() {
if(!haveMobileCompass()) return false;
using namespace shmupballs;
int dx = mousex - xmove;
int dy = mousey - yb;
int h = hypot(dx, dy);
if(h < rad) {
if(h < rad*SKIPFAC) movepcto(MD_WAIT);
else {
hyperpoint param = tangent_length(point2(dx, dy), .01);
2018-07-23 14:08:08 +00:00
movedir md = vectodir(param);
if(!canmove) movepcto(md), remission(); else movepcto(md);
}
getcstat = 0;
return true;
}
return false;
}
2019-09-13 01:10:26 +00:00
#if CAP_ORIENTATION
EX transmatrix getOrientation();
#endif
// orientation sensitivity
2019-08-09 19:00:52 +00:00
EX namespace ors {
EX int mode;
double sensitivity = 1;
int when_enabled;
transmatrix last_orientation;
transmatrix relative_matrix = Id;
2019-09-13 01:10:26 +00:00
EX string choices[3] = {"OFF", "relative", "absolute"};
#if CAP_ORIENTATION
2019-09-13 01:10:26 +00:00
EX transmatrix getOrientation() {
2019-07-03 02:55:45 +00:00
return MirrorX * hr::getOrientation() * MirrorX;
}
#endif
2019-08-09 19:00:52 +00:00
EX void reset() {
#if CAP_ORIENTATION
if(mode) last_orientation = getOrientation();
relative_matrix = Id;
#endif
}
void delayed_reset() {
#if CAP_ORIENTATION
relative_matrix = Id; when_enabled = ticks;
#endif
}
2019-08-09 19:00:52 +00:00
EX void show() {
#if CAP_ORIENTATION
2018-12-13 16:02:10 +00:00
cmode = sm::SIDE | sm::MAYDARK;
gamescreen();
dialog::init(XLAT("scrolling by device rotation"));
dialog::addHelp(XLAT(
"This lets you scroll the map by rotating your device. It can be e.g. used to "
"play the spherical mode of HyperRogue in mobile VR goggles -- the \"spherical VR\" "
"button configures this; this VR mode can be disabled by touching the screen for 1 second."));
dialog::addSelItem(XLAT("mode"), choices[mode], 'm');
dialog::add_action([] () { int m = (mode + 1) % 3; mode = 0; fullcenter(); mode = m; delayed_reset(); });
dialog::addSelItem(XLAT("sensitivity"), fts(sensitivity), 's');
dialog::add_action([] () {
dialog::editNumber(sensitivity, -10, 10, 1, 1, XLAT("sensitivity"),
XLAT("1 means that rotating the device by 1 radian corresponds to scrolling by 1 unit. In spherical geometry, 1 unit = 1 radian."));
});
dialog::addBreak(100);
dialog::addItem(XLAT("stereo vision config"), 'e');
dialog::add_action_push(showStereo);
dialog::addItem(XLAT("experiment with geometry"), 'g');
dialog::add_action([] () { runGeometryExperiments(); });
dialog::addSelItem(XLAT("projection"), fts(vpconf.alpha), 'p');
dialog::add_action([] () { projectionDialog(); });
dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z');
dialog::add_action([] () { editScale(); });
dialog::addItem(XLAT("spherical VR"), 'v');
dialog::add_action([] () {
if(!sphere) set_geometry(gSphere), start_game();
mode = 0; fullcenter();
mode = 2; sensitivity = 1;
vid.stereo_mode = sLR; vid.ipd = 0.2;
vpconf.alpha = 0; vpconf.scale = 1;
});
dialog::addBreak(100);
dialog::addBack();
dialog::display();
#endif
}
void relative_apply() {
if(ors::mode == 1) View = relative_matrix * View;
}
void relative_unapply() {
if(ors::mode == 1) View = inverse(relative_matrix) * View;
}
transmatrix change_geometry(const transmatrix& T) {
if(sphere && sensitivity == 1) return T;
ld alpha, beta, push;
{
dynamicval<eGeometry> g(geometry, gSphere);
hyperpoint h = T * C0;
push = hdist0(h);
alpha = atan2(h[1], h[0]);
if(push == 0) alpha = 0;
2018-08-19 14:28:36 +00:00
hyperpoint spinpoint = gpushxto0(h) * T * xpush0(1);
beta = atan2(spinpoint[1], spinpoint[0]);
}
// gpushxto0(h) * T * xpush(1) * C0 == spin(beta) * xpush(1) * C0
// gpushxto0(h) * T == spin(beta)
// T = rgpushxto0(h) * spin(beta)
transmatrix U = spin(-alpha) * xpush(push * sensitivity) * spin(-beta+alpha);
return U;
}
EX void unrotate(transmatrix& T) {
if(mode == 1) T = inverse(relative_matrix) * T;
}
EX void rerotate(transmatrix& T) {
if(mode == 1) T = (relative_matrix) * T;
}
2019-07-03 02:55:45 +00:00
int first_check, last_check;
2019-08-09 19:00:52 +00:00
EX void check_orientation() {
#if CAP_ORIENTATION
if(!mode) return;
2019-07-03 02:55:45 +00:00
if(ticks > last_check + 2000) first_check = ticks;
last_check = ticks;
if(ticks < first_check + 500) {
last_orientation = getOrientation();
return;
}
transmatrix next_orientation = MirrorX * getOrientation();
transmatrix T = inverse(next_orientation) * last_orientation;
2020-07-30 00:29:59 +00:00
if(mode == 1) unrotate(View), unrotate(cwtV.T);
relative_matrix = change_geometry(T);
2020-07-30 00:29:59 +00:00
if(mode == 1) rerotate(View), rerotate(cwtV.T);
if(mode == 2) View = relative_matrix * View, last_orientation = next_orientation;
#endif
}
2019-08-09 19:00:52 +00:00
EX }
}