mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-12-24 01:00:25 +00:00
multigame support, including dualmode
This commit is contained in:
parent
587652e540
commit
8295251cdc
@ -942,7 +942,9 @@ ld realradius() {
|
||||
void drawmessage(const string& s, int& y, color_t col) {
|
||||
int rrad = (int) realradius();
|
||||
int space;
|
||||
if(y > current_display->ycenter + rrad * vid.stretch)
|
||||
if(dual::state)
|
||||
space = vid.xres;
|
||||
else if(y > current_display->ycenter + rrad * vid.stretch)
|
||||
space = vid.xres;
|
||||
else if(y > current_display->ycenter)
|
||||
space = current_display->xcenter - rhypot(rrad, (y-current_display->ycenter) / vid.stretch);
|
||||
|
14
complex.cpp
14
complex.cpp
@ -3202,6 +3202,20 @@ auto ccm = addHook(clearmemory, 0, [] () {
|
||||
#endif
|
||||
mirror::clearcache();
|
||||
}) +
|
||||
addHook(hooks_gamedata, 0, [] (gamedata* gd) {
|
||||
gd->store(heat::offscreen_heat);
|
||||
gd->store(heat::offscreen_fire);
|
||||
gd->store(princess::infos);
|
||||
gd->store(mirror::mirrors);
|
||||
gd->store(clearing::bpdata);
|
||||
gd->store(tortoise::emap);
|
||||
gd->store(tortoise::babymap);
|
||||
gd->store(prairie::lasttreasure);
|
||||
gd->store(prairie::enter);
|
||||
gd->store(prairie::tchoices);
|
||||
gd->store(prairie::beaststogen);
|
||||
gd->store(sword::angle);
|
||||
}) +
|
||||
addHook(hooks_removecells, 0, [] () {
|
||||
eliminate_if(heat::offscreen_heat, is_cell_removed);
|
||||
eliminate_if(heat::offscreen_fire, is_cell_removed);
|
||||
|
@ -515,7 +515,10 @@ void reset_graph_settings() {
|
||||
}
|
||||
|
||||
void resetModes(char leave) {
|
||||
popAllGames();
|
||||
while(game_active || gamestack::pushed()) {
|
||||
if(game_active) stop_game();
|
||||
if(gamestack::pushed()) gamestack::pop();
|
||||
}
|
||||
if(shmup::on != (leave == rg::shmup)) stop_game_and_switch_mode(rg::shmup);
|
||||
if(inv::on != (leave == rg::inv)) stop_game_and_switch_mode(rg::inv);
|
||||
if(chaosmode != (leave == rg::chaos)) stop_game_and_switch_mode(rg::chaos);
|
||||
|
13
control.cpp
13
control.cpp
@ -247,6 +247,7 @@ typedef SDL_Event eventtype;
|
||||
bool smooth_scrolling = false;
|
||||
|
||||
void handlePanning(int sym, int uni) {
|
||||
if(dual::split([=] { handlePanning(sym, uni); })) return;
|
||||
if(DIM == 3) {
|
||||
if(sym == PSEUDOKEY_WHEELUP) View = cpush(2, -0.05*shiftmul) * View, didsomething = true, playermoved = false;
|
||||
if(sym == PSEUDOKEY_WHEELDOWN) View = cpush(2, 0.05*shiftmul) * View, didsomething = true, playermoved = false;
|
||||
@ -473,8 +474,16 @@ void fix_mouseh() {
|
||||
else if(rug::rugged)
|
||||
mouseh = rug::gethyper(mousex, mousey);
|
||||
#endif
|
||||
else
|
||||
mouseh = gethyper(mousex, mousey);
|
||||
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);
|
||||
}
|
||||
need_mouseh = false;
|
||||
}
|
||||
|
||||
|
76
game.cpp
76
game.cpp
@ -15,8 +15,8 @@ int gamerange_bonus = 0;
|
||||
int gamerange() { return getDistLimit() + gamerange_bonus; }
|
||||
|
||||
cell *lastmove;
|
||||
enum eLastmovetype {lmSkip, lmMove, lmAttack, lmSpecial, lmPush, lmTree};
|
||||
eLastmovetype lastmovetype;
|
||||
eLastmovetype lastmovetype, nextmovetype;
|
||||
eForcemovetype forcedmovetype;
|
||||
|
||||
bool hauntedWarning;
|
||||
bool survivalist;
|
||||
@ -7581,8 +7581,9 @@ void monstersTurn() {
|
||||
}
|
||||
}
|
||||
DEBB(DF_TURN, ("rop"));
|
||||
reduceOrbPowers();
|
||||
if(!dual::state) reduceOrbPowers();
|
||||
int phase1 = (1 & items[itOrbSpeed]);
|
||||
if(dual::state && items[itOrbSpeed]) phase1 = !phase1;
|
||||
DEBB(DF_TURN, ("lc"));
|
||||
if(!phase1) livecaves();
|
||||
if(!phase1) ca::simulate();
|
||||
@ -7787,6 +7788,7 @@ void handle_switchplaces(cell *c1, cell *c2, bool& switchplaces) {
|
||||
}
|
||||
|
||||
bool movepcto(int d, int subdir, bool checkonly) {
|
||||
if(dual::state == 1) return dual::movepc(d, subdir, checkonly);
|
||||
if(d >= 0 && !checkonly && subdir != 1 && subdir != -1) printf("subdir = %d\n", subdir);
|
||||
global_pushto = NULL;
|
||||
bool switchplaces = false;
|
||||
@ -7824,6 +7826,10 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
|
||||
gravity_state = gsNormal;
|
||||
|
||||
bool fmsMove = forcedmovetype == fmSkip || forcedmovetype == fmMove;
|
||||
bool fmsAttack = forcedmovetype == fmSkip || forcedmovetype == fmAttack;
|
||||
bool fmsActivate = forcedmovetype == fmSkip || forcedmovetype == fmActivate;
|
||||
|
||||
if(d >= 0) {
|
||||
cell *c2 = cwt.at->move(d);
|
||||
bool goodTortoise = c2->monst == moTortoise && tortoise::seek() && !tortoise::diff(tortoise::getb(c2)) && !c2->item;
|
||||
@ -7842,8 +7848,10 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(items[itOrbDomination] > ORBBASE && isMountable(c2->monst) && !monstersnear2()) {
|
||||
if(checkonly) return true;
|
||||
bool try_instant = (forcedmovetype == fmInstant) || (forcedmovetype == fmSkip && !passable(c2, cwt.at, P_ISPLAYER | P_MIRROR | P_USEBOAT | P_FRIENDSWAP));
|
||||
|
||||
if(items[itOrbDomination] > ORBBASE && isMountable(c2->monst) && !monstersnear2() && fmsMove) {
|
||||
if(checkonly) { nextmovetype = lmMove; return true; }
|
||||
if(!isMountable(cwt.at->monst)) dragon::target = NULL;
|
||||
movecost(cwt.at, c2, 3);
|
||||
|
||||
@ -7853,8 +7861,8 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
goto mountjump;
|
||||
}
|
||||
|
||||
if(!passable(c2, cwt.at, P_ISPLAYER | P_MIRROR | P_USEBOAT | P_FRIENDSWAP) && items[itOrbFlash]) {
|
||||
if(checkonly) return true;
|
||||
if(items[itOrbFlash] && try_instant) {
|
||||
if(checkonly) { nextmovetype = lmInstant; return true; }
|
||||
if(orbProtection(itOrbFlash)) return true;
|
||||
activateFlash();
|
||||
bfs();
|
||||
@ -7863,8 +7871,8 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!passable(c2, cwt.at, P_ISPLAYER | P_MIRROR | P_USEBOAT | P_FRIENDSWAP) && items[itOrbLightning]) {
|
||||
if(checkonly) return true;
|
||||
if(items[itOrbLightning] && try_instant) {
|
||||
if(checkonly) { nextmovetype = lmInstant; return true; }
|
||||
if(orbProtection(itOrbLightning)) return true;
|
||||
activateLightning();
|
||||
keepLightning = true;
|
||||
@ -7875,8 +7883,8 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isActivable(c2)) {
|
||||
if(checkonly) return true;
|
||||
if(isActivable(c2) && fmsActivate) {
|
||||
if(checkonly) { nextmovetype = lmInstant; return true; }
|
||||
activateActiv(c2, true);
|
||||
bfs();
|
||||
if(multi::players > 1) { multi::whereto[multi::cpid].d = MD_UNDECIDED; return false; }
|
||||
@ -7884,7 +7892,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isPushable(c2->wall) && !c2->monst && !nonAdjacentPlayer(c2, cwt.at)) {
|
||||
if(isPushable(c2->wall) && !c2->monst && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) {
|
||||
int pushdir;
|
||||
cell *c3 = determinePush(cwt, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.at); }, pushdir);
|
||||
if(c3 == c2) {
|
||||
@ -7897,7 +7905,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return false;
|
||||
}
|
||||
global_pushto = c3;
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmMove; return true; }
|
||||
addMessage(XLAT("You push %the1.", c2->wall));
|
||||
lastmovetype = lmPush; lastmove = cwt.at;
|
||||
pushThumper(c2, c3);
|
||||
@ -7913,7 +7921,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isWatery(c2) && !nonAdjacentPlayer(cwt.at,c2) && !c2->monst && cwt.at->wall == waBoat) {
|
||||
if(isWatery(c2) && !nonAdjacentPlayer(cwt.at,c2) && !c2->monst && cwt.at->wall == waBoat && fmsMove) {
|
||||
|
||||
if(havePushConflict(cwt.at, checkonly)) return false;
|
||||
|
||||
@ -7939,14 +7947,14 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
if(!checkonly && errormsgs) wouldkill("%The1 would kill you there!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(checkonly) return true;
|
||||
|
||||
if(checkonly) { nextmovetype = lmMove; return true; }
|
||||
moveBoat(c2, cwt.at, d);
|
||||
boatmove = true;
|
||||
goto boatjump;
|
||||
}
|
||||
|
||||
if(!c2->monst && cwt.at->wall == waBoat && boatGoesThrough(c2) && markOrb(itOrbWater) && !nonAdjacentPlayer(c2, cwt.at)) {
|
||||
if(!c2->monst && cwt.at->wall == waBoat && boatGoesThrough(c2) && markOrb(itOrbWater) && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) {
|
||||
|
||||
if(havePushConflict(cwt.at, checkonly)) return false;
|
||||
if(monstersnear(c2,NULL,moPlayer,NULL,cwt.at)) {
|
||||
@ -7954,7 +7962,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmMove; return true; }
|
||||
if(c2->item && !cwt.at->item) moveItem(c2, cwt.at, false), boatmove = true;
|
||||
placeWater(c2, cwt.at);
|
||||
moveBoat(c2, cwt.at, d);
|
||||
@ -7964,7 +7972,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
}
|
||||
|
||||
escape:
|
||||
if(c2->wall == waBigStatue && !c2->monst && !nonAdjacentPlayer(c2, cwt.at)) {
|
||||
if(c2->wall == waBigStatue && !c2->monst && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) {
|
||||
if(!canPushStatueOn(cwt.at)) {
|
||||
if(checkonly) return false;
|
||||
if(isFire(cwt.at))
|
||||
@ -7988,7 +7996,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(checkonly) { c2->wall = save_c2; cwt.at->wall = save_cw; return true; }
|
||||
if(checkonly) { c2->wall = save_c2; cwt.at->wall = save_cw; nextmovetype = lmMove; return true; }
|
||||
addMessage(XLAT("You push %the1 behind you!", waBigStatue));
|
||||
animateMovement(c2, cwt.at, LAYER_BOAT, cwt.at->c.spin(d));
|
||||
goto statuejump;
|
||||
@ -7999,18 +8007,19 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
c2->wall == waBigTree ||
|
||||
c2->wall == waSmallTree ||
|
||||
c2->wall == waMirrorWall;
|
||||
attackable = attackable && (!c2->monst || isFriendly(c2));
|
||||
if(attackable && markOrb(itOrbAether) && c2->wall != waMirrorWall)
|
||||
attackable = false;
|
||||
if(forcedmovetype == fmAttack) attackable = true;
|
||||
attackable = attackable && (!c2->monst || isFriendly(c2));
|
||||
attackable = attackable && !nonAdjacentPlayer(cwt.at,c2);
|
||||
|
||||
if(attackable) {
|
||||
if(attackable && fmsAttack) {
|
||||
if(checkNeedMove(checkonly, true)) return false;
|
||||
if(monstersnear(cwt.at,c2,moPlayer,NULL,cwt.at)) {
|
||||
if(!checkonly && errormsgs) wouldkill("%The1 would get you!");
|
||||
return false;
|
||||
}
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmAttack; return true; }
|
||||
if(c2->wall == waSmallTree) {
|
||||
drawParticles(c2, winf[c2->wall].color, 4);
|
||||
addMessage(XLAT("You chop down the tree."));
|
||||
@ -8029,7 +8038,12 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
}
|
||||
else {
|
||||
if(!peace::on) {
|
||||
addMessage(XLAT("You swing your sword at the mirror."));
|
||||
if(c2->wall == waMirrorWall)
|
||||
addMessage(XLAT("You swing your sword at the mirror."));
|
||||
else if(c2->wall)
|
||||
addMessage(XLAT("You swing your sword at %the1.", c2->wall));
|
||||
else
|
||||
addMessage(XLAT("You swing your sword."));
|
||||
sideAttack(cwt.at, d, moPlayer, 0);
|
||||
animateAttack(cwt.at, c2, LAYER_SMALL, d);
|
||||
}
|
||||
@ -8047,6 +8061,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
}
|
||||
else if(c2->monst && (!isFriendly(c2) || c2->monst == moTameBomberbird || isMountable(c2->monst))
|
||||
&& !(peace::on && !isMultitile(c2->monst) && !goodTortoise)) {
|
||||
if(!fmsAttack) return false;
|
||||
|
||||
flagtype attackflags = AF_NORMAL;
|
||||
if(items[itOrbSpeed]&1) attackflags |= AF_FAST;
|
||||
@ -8122,7 +8137,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
|
||||
if(c2->monst == moTameBomberbird && warningprotection_hit(moTameBomberbird)) return false;
|
||||
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmAttack; return true; }
|
||||
|
||||
/* if(c2->monst == moTortoise) {
|
||||
printf("seek=%d get=%d <%x/%x> item=%d\n",
|
||||
@ -8197,14 +8212,14 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
else if(fmsMove) {
|
||||
if(mineMarked(c2) && !minesafe() && !checkonly && warningprotection(XLAT("Are you sure you want to step there?")))
|
||||
return false;
|
||||
if(monstersnear(c2, NULL, moPlayer, NULL, cwt.at)) {
|
||||
if(checkonly) return false;
|
||||
|
||||
if(items[itOrbFlash]) {
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmInstant; return true; }
|
||||
if(orbProtection(itOrbFlash)) return true;
|
||||
activateFlash();
|
||||
checkmove();
|
||||
@ -8212,7 +8227,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
}
|
||||
|
||||
if(items[itOrbLightning]) {
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmInstant; return true; }
|
||||
if(orbProtection(itOrbLightning)) return true;
|
||||
activateLightning();
|
||||
checkmove();
|
||||
@ -8244,7 +8259,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
if(!checkonly && warningprotection_hit(do_we_stab_a_friend(cwt.at, c2, moPlayer)))
|
||||
return false;
|
||||
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmMove; return true; }
|
||||
boatjump:
|
||||
statuejump:
|
||||
flipplayer = true; if(multi::players > 1) multi::flipped[multi::cpid] = true;
|
||||
@ -8336,6 +8351,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
landvisited[cwt.at->land] = true;
|
||||
afterplayermoved();
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
else {
|
||||
if(items[itOrbGravity]) {
|
||||
@ -8350,7 +8366,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
|
||||
wouldkill("%The1 would get you!");
|
||||
return false;
|
||||
}
|
||||
if(checkonly) return true;
|
||||
if(checkonly) { nextmovetype = lmSkip; return true; }
|
||||
swordAttackStatic();
|
||||
if(d == -2)
|
||||
dropGreenStone(cwt.at);
|
||||
|
@ -361,6 +361,7 @@ namespace geom3 {
|
||||
namespace geom3 {
|
||||
#if MAXMDIM >= 4
|
||||
void switch_always3() {
|
||||
if(dual::split(switch_always3)) return;
|
||||
if(rug::rugged) rug::close();
|
||||
geom3::always3 = !geom3::always3;
|
||||
swapmatrix(View);
|
||||
@ -369,6 +370,7 @@ void switch_always3() {
|
||||
#endif
|
||||
|
||||
void switch_tpp() {
|
||||
if(dual::split(switch_fpp)) return;
|
||||
if(pmodel == mdDisk && vid.camera_angle) {
|
||||
vid.yshift = 0;
|
||||
vid.camera_angle = 0;
|
||||
@ -391,6 +393,7 @@ void switch_always3() {
|
||||
void switch_fpp() {
|
||||
#if MAXMDIM >= 4
|
||||
if(rug::rugged) rug::close();
|
||||
if(dual::split(switch_fpp)) return;
|
||||
if(!geom3::always3) {
|
||||
geom3::always3 = true;
|
||||
geom3::wall_height = 1.5;
|
||||
|
19
graph.cpp
19
graph.cpp
@ -7240,7 +7240,14 @@ void gamescreen(int _darken) {
|
||||
just_gmatrix = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(dual::split([=] () {
|
||||
dual::in_subscreen([=] () { gamescreen(_darken); });
|
||||
})) {
|
||||
calcparam();
|
||||
return;
|
||||
}
|
||||
|
||||
if((cmode & sm::MAYDARK) && !current_display->sidescreen) {
|
||||
_darken += 2;
|
||||
}
|
||||
@ -7463,11 +7470,19 @@ auto graphcm = addHook(clearmemory, 0, [] () {
|
||||
mouseover = centerover.at = lmouseover = NULL;
|
||||
gmatrix.clear(); gmatrix0.clear();
|
||||
clearAnimations();
|
||||
})
|
||||
+ addHook(hooks_gamedata, 0, [] (gamedata* gd) {
|
||||
gd->store(mouseover);
|
||||
gd->store(lmouseover);
|
||||
gd->store(animations);
|
||||
gd->store(flashes);
|
||||
gd->store(fallanims);
|
||||
});
|
||||
;
|
||||
|
||||
//=== animation
|
||||
|
||||
map<cell*, animation> animations[ANIMLAYERS];
|
||||
array<map<cell*, animation>, ANIMLAYERS> animations;
|
||||
|
||||
int revhint(cell *c, int hint) {
|
||||
if(hint >= 0 && hint < c->type) return c->c.spin(hint);
|
||||
|
61
hyper.h
61
hyper.h
@ -2090,7 +2090,7 @@ struct animation {
|
||||
#define LAYER_SMALL 1 // for others
|
||||
#define LAYER_BOAT 2 // mark that a boat has moved
|
||||
|
||||
extern map<cell*, animation> animations[ANIMLAYERS];
|
||||
extern array<map<cell*, animation>, ANIMLAYERS> animations;
|
||||
|
||||
void animateAttack(cell *src, cell *tgt, int layer, int direction_hint);
|
||||
|
||||
@ -4196,8 +4196,6 @@ bool score_loaded(int id);
|
||||
int score_default(int id);
|
||||
void handle_event(SDL_Event& ev);
|
||||
|
||||
void pop_game();
|
||||
void push_game();
|
||||
void start_game();
|
||||
void stop_game();
|
||||
void switch_game_mode(char switchWhat);
|
||||
@ -4612,6 +4610,8 @@ extern vector<help_extension> help_extensions;
|
||||
|
||||
namespace gamestack {
|
||||
bool pushed();
|
||||
void push();
|
||||
void pop();
|
||||
}
|
||||
|
||||
namespace geom3 {
|
||||
@ -5407,5 +5407,58 @@ static const int POLY_INTENSE = (1<<23); // extra intense colors
|
||||
|
||||
void pregen();
|
||||
extern vector<eLand> currentlands;
|
||||
}
|
||||
|
||||
struct gamedata {
|
||||
// important parameters should be visible
|
||||
eGeometry geo;
|
||||
eVariation var;
|
||||
eLand specland;
|
||||
bool active;
|
||||
// other properties are recorded
|
||||
vector<char> record;
|
||||
int index, mode;
|
||||
void storegame();
|
||||
void restoregame();
|
||||
template<class T> void store(T& x) {
|
||||
int ssize = sizeof(x);
|
||||
if(ssize & 7) ssize = (ssize | 7) + 1;
|
||||
if(mode == 0) {
|
||||
record.resize(index+ssize);
|
||||
T& at = *(new (&record[index]) T());
|
||||
at = move(x);
|
||||
}
|
||||
else {
|
||||
T& at = (T&) record[index];
|
||||
x = move(at);
|
||||
at.~T();
|
||||
}
|
||||
index += ssize;
|
||||
}
|
||||
};
|
||||
|
||||
/* lastmovetype uses lmSkip, lmMove, lmAttack, lmPush, lmTree */
|
||||
enum eLastmovetype { lmSkip, lmMove, lmAttack, lmPush, lmTree, lmInstant };
|
||||
extern eLastmovetype lastmovetype, nextmovetype;
|
||||
|
||||
enum eForcemovetype { fmSkip, fmMove, fmAttack, fmInstant, fmActivate };
|
||||
extern eForcemovetype forcedmovetype;
|
||||
|
||||
extern hookset<void(gamedata*)> *hooks_gamedata;
|
||||
|
||||
namespace dual {
|
||||
// 0 = dualmode off, 1 = in dualmode (no game chosen), 2 = in dualmode (working on one of subgames)
|
||||
extern int state;
|
||||
extern int currently_loaded, main_side;
|
||||
|
||||
bool movepc(int d, int subdir, bool checkonly);
|
||||
extern transmatrix player_orientation[2];
|
||||
|
||||
bool split(reaction_t what);
|
||||
void switch_to(int i);
|
||||
void in_subscreen(reaction_t what);
|
||||
|
||||
bool check_side(eLand l);
|
||||
void assign_landsides();
|
||||
}
|
||||
|
||||
}
|
16
hypgraph.cpp
16
hypgraph.cpp
@ -1063,7 +1063,19 @@ transmatrix eumovedir(int d) {
|
||||
|
||||
void spinEdge(ld aspd) {
|
||||
ld downspin = 0;
|
||||
if(playerfound && vid.fixed_facing) {
|
||||
if(dual::state == 2 && dual::currently_loaded != dual::main_side) {
|
||||
transmatrix our = gpushxto0(tC0(cwtV)) * cwtV;
|
||||
transmatrix their = dual::player_orientation[dual::main_side];
|
||||
fixmatrix(our);
|
||||
fixmatrix(their);
|
||||
if(DIM == 2) {
|
||||
transmatrix T = their * inverse(our);
|
||||
hyperpoint H = T * xpush0(1);
|
||||
downspin = -atan2(H[1], H[0]);
|
||||
}
|
||||
else View = their * inverse(our) * View;
|
||||
}
|
||||
else if(playerfound && vid.fixed_facing) {
|
||||
hyperpoint H = gpushxto0(playerV * C0) * playerV * xpush0(5);
|
||||
downspin = atan2(H[1], H[0]);
|
||||
downspin += vid.fixed_facing_dir * degree;
|
||||
@ -1099,6 +1111,7 @@ void spinEdge(ld aspd) {
|
||||
void centerpc(ld aspd) {
|
||||
|
||||
if(subscreens::split([=] () {centerpc(aspd);})) return;
|
||||
if(dual::split([=] () { centerpc(aspd); })) return;
|
||||
|
||||
#if CAP_CRYSTAL
|
||||
if(geometry == gCrystal)
|
||||
@ -1179,6 +1192,7 @@ void centerpc(ld aspd) {
|
||||
void optimizeview() {
|
||||
|
||||
if(subscreens::split(optimizeview)) return;
|
||||
if(dual::split(optimizeview)) return;
|
||||
|
||||
#if CAP_ANIMATIONS
|
||||
if(centerover.at && inmirror(centerover.at)) {
|
||||
|
@ -156,6 +156,9 @@ void place_elemental_wall(cell *c) {
|
||||
}
|
||||
|
||||
int hrand_monster(int x) {
|
||||
// dual geometry mode is much harder, so generate less monsters to balance it
|
||||
if(dual::state) x *= 3;
|
||||
// in 3D monster generation depends on the sight range
|
||||
if(WDIM == 3 && !sphere) {
|
||||
int t = isize(gmatrix);
|
||||
if(t > 300) x = ((long long)(x)) * t / 300;
|
||||
|
@ -572,6 +572,7 @@ eLand getLandForList(cell *c) {
|
||||
|
||||
bool isLandIngame(eLand l) {
|
||||
if(isElemental(l)) l = laElementalWall;
|
||||
if(dual::state == 2 && !dual::check_side(l)) return false;
|
||||
return land_validity(l).flags & lv::appears_in_full;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,10 @@ void showOverview() {
|
||||
|
||||
bool pages;
|
||||
|
||||
{
|
||||
dynamicval<int> ds(dual::state, dual::state ? 2 : 0);
|
||||
generateLandList(isLandIngame);
|
||||
}
|
||||
|
||||
bool not_in_game = false;
|
||||
|
||||
|
281
multigame.cpp
Normal file
281
multigame.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
// Hyperbolic Rogue
|
||||
|
||||
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
|
||||
|
||||
// gamedata structure, for recording the game data in memory temporarily
|
||||
// namespace dual (dual mode)
|
||||
|
||||
#include "allhyper.h"
|
||||
|
||||
namespace hr {
|
||||
|
||||
bool isEquidLand(eLand);
|
||||
extern bool orbused[ittypes];
|
||||
|
||||
void gamedata_all(gamedata& gd) {
|
||||
gd.index = 0;
|
||||
gd.store(currentmap);
|
||||
gd.store(cwt);
|
||||
gd.store(allmaps);
|
||||
gd.store(shmup::on);
|
||||
gd.store(*current_display);
|
||||
gd.store(cgip);
|
||||
if(GOLDBERG) gd.store(gp::param);
|
||||
callhooks(hooks_gamedata, &gd);
|
||||
}
|
||||
|
||||
void gamedata::storegame() {
|
||||
geo = geometry;
|
||||
var = variation;
|
||||
specland = specialland;
|
||||
active = game_active;
|
||||
record.clear();
|
||||
mode = 0;
|
||||
gamedata_all(*this);
|
||||
game_active = false;
|
||||
}
|
||||
|
||||
void gamedata::restoregame() {
|
||||
geometry = geo;
|
||||
variation = var;
|
||||
specialland = specland;
|
||||
game_active = active;
|
||||
mode = 1;
|
||||
gamedata_all(*this);
|
||||
}
|
||||
|
||||
hookset<void(gamedata*)> *hooks_gamedata;
|
||||
|
||||
namespace gamestack {
|
||||
|
||||
vector<gamedata> gd;
|
||||
|
||||
bool pushed() { return isize(gd); }
|
||||
|
||||
void push() {
|
||||
gd.emplace_back();
|
||||
gd.back().storegame();
|
||||
}
|
||||
|
||||
void pop() {
|
||||
if(!pushed()) return;
|
||||
if(game_active) stop_game();
|
||||
gd.back().restoregame();
|
||||
gd.pop_back();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace dual {
|
||||
int state;
|
||||
|
||||
int currently_loaded;
|
||||
int main_side;
|
||||
|
||||
gamedata dgd[2];
|
||||
ld scales[2];
|
||||
transmatrix player_orientation[2];
|
||||
|
||||
void switch_to(int k) {
|
||||
if(k != currently_loaded) {
|
||||
scales[currently_loaded] = vid.scale;
|
||||
player_orientation[currently_loaded] = gpushxto0(tC0(cwtV)) * cwtV;
|
||||
dgd[currently_loaded].storegame();
|
||||
currently_loaded = k;
|
||||
dgd[currently_loaded].restoregame();
|
||||
vid.scale = scales[currently_loaded];
|
||||
}
|
||||
}
|
||||
|
||||
bool movepc(int d, int subdir, bool checkonly) {
|
||||
dynamicval<int> dm(dual::state, 2);
|
||||
int cg = currently_loaded;
|
||||
|
||||
bool orbusedbak[ittypes];
|
||||
for(int i=0; i<ittypes; i++) orbusedbak[i] = orbused[i];
|
||||
|
||||
if(d < 0) {
|
||||
if(d == -2 && items[itGreenStone] == 1) {
|
||||
switch_to(cg);
|
||||
return false;
|
||||
}
|
||||
bool ok = true;
|
||||
|
||||
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];
|
||||
}
|
||||
if(ok && checkonly) {
|
||||
switch_to(cg);
|
||||
return true;
|
||||
}
|
||||
if(ok) for(int k=0; k<2; k++) {
|
||||
switch_to(k);
|
||||
movepcto(d, subdir, false);
|
||||
}
|
||||
switch_to(cg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lms[2][5];
|
||||
for(int k=0; k<2; k++) {
|
||||
switch_to(k);
|
||||
for(eForcemovetype fm: { fmMove, fmAttack, fmInstant, fmActivate }) {
|
||||
forcedmovetype = fm;
|
||||
lms[k][fm] = movepcto(fm == fmMove ? d : 0, subdir, true);
|
||||
println(hlog, k, int(fm), " -> ", lms[k][fm]);
|
||||
forcedmovetype = fmSkip;
|
||||
for(int i=0; i<ittypes; i++) orbused[i] = orbusedbak[i];
|
||||
}
|
||||
}
|
||||
|
||||
if(lms[0][fmActivate]) {
|
||||
if(checkonly) { switch_to(cg); return true; }
|
||||
switch_to(0); forcedmovetype = fmActivate; movepcto(0, subdir, false); forcedmovetype = fmSkip;
|
||||
if(!lms[1][fmActivate]) return true;
|
||||
}
|
||||
if(lms[1][fmActivate]) {
|
||||
if(checkonly) { switch_to(cg); return true; }
|
||||
switch_to(1); forcedmovetype = fmActivate; movepcto(0, subdir, false); forcedmovetype = fmSkip;
|
||||
switch_to(cg);
|
||||
return true;
|
||||
}
|
||||
for(auto fm: {fmMove, fmInstant, fmAttack}) if(lms[0][fm] && lms[1][fm]) {
|
||||
println(hlog, "apply ", int(fm));
|
||||
if(checkonly) { switch_to(cg); return true; }
|
||||
int flash = items[itOrbFlash], lgt = items[itOrbLightning];
|
||||
switch_to(0); forcedmovetype = fm; movepcto(0, subdir, false); forcedmovetype = fmSkip;
|
||||
if(fm == fmInstant) { items[itOrbFlash] = flash, items[itOrbLightning] = lgt; }
|
||||
switch_to(1); forcedmovetype = fm; movepcto(0, subdir, false); forcedmovetype = fmSkip;
|
||||
switch_to(cg);
|
||||
reduceOrbPowers();
|
||||
return true;
|
||||
}
|
||||
println(hlog, "no match");
|
||||
switch_to(cg);
|
||||
return false;
|
||||
}
|
||||
|
||||
void in_subscreen(reaction_t what) {
|
||||
dynamicval<ld> xmax(current_display->xmax, 0.5 * (currently_loaded+1));
|
||||
dynamicval<ld> xmin(current_display->xmin, 0.5 * (currently_loaded));
|
||||
what();
|
||||
}
|
||||
|
||||
bool split(reaction_t what) {
|
||||
if(state != 1) return false;
|
||||
state = 2;
|
||||
|
||||
for(int a=0; a<2; a++) {
|
||||
switch_to(currently_loaded ^ 1);
|
||||
what();
|
||||
}
|
||||
|
||||
state = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
int args() {
|
||||
using namespace arg;
|
||||
|
||||
if(0) ;
|
||||
else if(argis("-dual")) {
|
||||
PHASEFROM(3);
|
||||
stop_game();
|
||||
subscreens::prepare();
|
||||
|
||||
for(int s=0; s<2; s++) {
|
||||
// dynamicval<display_data*> pds(current_display, &subscreens::player_displays[s]);
|
||||
variation = eVariation::pure;
|
||||
geometry = s == 0 ? gEuclidSquare : gArchimedean;
|
||||
arcm::current.parse("4,4,4,4,4");
|
||||
scales[s] = vid.scale;
|
||||
dgd[s].storegame();
|
||||
}
|
||||
|
||||
currently_loaded = 0;
|
||||
dgd[0].restoregame();
|
||||
state = 1;
|
||||
}
|
||||
else if(argis("-dual0:")) {
|
||||
switch_to(0);
|
||||
}
|
||||
else if(argis("-dual1:")) {
|
||||
switch_to(1);
|
||||
}
|
||||
else return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto hook = addHook(hooks_args, 100, args);
|
||||
|
||||
vector<int> landsides;
|
||||
|
||||
bool check_side(eLand l) {
|
||||
return landsides[l] == currently_loaded || landsides[l] == 2;
|
||||
}
|
||||
|
||||
void assign_landsides() {
|
||||
landsides.resize(landtypes);
|
||||
int which_hyperbolic = -1;
|
||||
if(ginf[dgd[0].geo].cclass == gcHyperbolic && ginf[dgd[1].geo].cclass != gcHyperbolic)
|
||||
which_hyperbolic = 0;
|
||||
else if(ginf[dgd[1].geo].cclass == gcHyperbolic && ginf[dgd[0].geo].cclass != gcHyperbolic)
|
||||
which_hyperbolic = 1;
|
||||
int nxt = 0;
|
||||
for(int i=0; i<landtypes; i++) {
|
||||
eLand l = eLand(i);
|
||||
auto& v = landsides[i];
|
||||
land_validity_t lv[2];
|
||||
for(int s=0; s<2; s++) {
|
||||
switch_to(s);
|
||||
lv[s] = land_validity(l);
|
||||
}
|
||||
if(!(lv[0].flags & lv::appears_in_full) && !(lv[1].flags & lv::appears_in_full)) {
|
||||
v = -1;
|
||||
continue;
|
||||
}
|
||||
else if(isCrossroads(l))
|
||||
v = -1; /* simply boring */
|
||||
else if(isGravityLand(l))
|
||||
v = -1; /* too confusing */
|
||||
else if(among(l, laTortoise))
|
||||
v = -1; /* does not work in hyperbolic geos available, and better not do it in Euclidean ones either */
|
||||
else if(among(l, laHaunted))
|
||||
v = -1; /* graveyard prefers Euclidean, while Haunted prefers hyperbolic */
|
||||
else if(l == dgd[0].specland && l == dgd[1].specland)
|
||||
v = 2;
|
||||
else if(l == dgd[0].specland)
|
||||
v = 0;
|
||||
else if(l == dgd[1].specland)
|
||||
v = 1;
|
||||
else if(isElemental(l))
|
||||
v = 1;
|
||||
else if(!(lv[0].flags & lv::appears_in_full))
|
||||
v = 1;
|
||||
else if(!(lv[1].flags & lv::appears_in_full))
|
||||
v = 0;
|
||||
else if(lv[0].quality_level > lv[1].quality_level)
|
||||
v = 0;
|
||||
else if(lv[1].quality_level > lv[0].quality_level)
|
||||
v = 1;
|
||||
else if(isEquidLand(l) && which_hyperbolic >= 0)
|
||||
v = which_hyperbolic;
|
||||
else if(among(l, laHunting, laMotion, laCaves, laAlchemist) && which_hyperbolic >= 0)
|
||||
v = which_hyperbolic;
|
||||
else if(among(l, laMirrorOld, laIce, laJungle, laDesert, laDryForest, laStorms) && which_hyperbolic >= 0)
|
||||
v = 1 - which_hyperbolic;
|
||||
else if(which_hyperbolic >= 0)
|
||||
v = which_hyperbolic;
|
||||
else {
|
||||
println(hlog, "equivalent");
|
||||
v = nxt, nxt = 1 - nxt;
|
||||
}
|
||||
println(hlog, dnameof(l), ": ", lv[0].msg, " vs ", lv[1].msg, " verdict = ", v);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
2
rug.cpp
2
rug.cpp
@ -1491,6 +1491,7 @@ void init_model() {
|
||||
}
|
||||
|
||||
void init() {
|
||||
if(dual::state) return;
|
||||
reopen();
|
||||
if(rugged) init_model();
|
||||
}
|
||||
@ -2016,6 +2017,7 @@ void show() {
|
||||
}
|
||||
|
||||
void select() {
|
||||
if(dual::state) return;
|
||||
pushScreen(rug::show);
|
||||
}
|
||||
|
||||
|
@ -3272,6 +3272,7 @@ hookset<bool(int)> *hooks_turn;
|
||||
void turn(int delta) {
|
||||
|
||||
if(racing::on && subscreens::split( [delta] () { turn(delta); })) return;
|
||||
if(dual::split( [delta] () { turn(delta); })) return;
|
||||
|
||||
if(callhandlers(false, hooks_turn, delta)) return;
|
||||
if(!shmup::on) return;
|
||||
|
98
system.cpp
98
system.cpp
@ -1082,76 +1082,9 @@ void loadsave() {
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace gamestack {
|
||||
|
||||
struct gamedata {
|
||||
hrmap *hmap;
|
||||
cellwalker cwt;
|
||||
display_data d;
|
||||
eGeometry geometry;
|
||||
eVariation variation;
|
||||
bool shmup;
|
||||
void store();
|
||||
void restore();
|
||||
};
|
||||
|
||||
vector<gamedata> gd;
|
||||
|
||||
bool pushed() { return isize(gd); }
|
||||
|
||||
void gamedata::store() {
|
||||
hmap = currentmap;
|
||||
cwt = hr::cwt;
|
||||
geometry = hr::geometry;
|
||||
shmup = hr::shmup::on;
|
||||
variation = hr::variation;
|
||||
d = *current_display;
|
||||
}
|
||||
|
||||
void gamedata::restore() {
|
||||
currentmap = hmap;
|
||||
hr::cwt = cwt;
|
||||
hr::geometry = geometry;
|
||||
hr::variation = variation;
|
||||
if(shmup::on) shmup::clearMonsters();
|
||||
shmup::on = shmup;
|
||||
check_cgi();
|
||||
cgi.require_basics();
|
||||
*current_display = d;
|
||||
bfs();
|
||||
}
|
||||
|
||||
void push() {
|
||||
if(geometry) {
|
||||
printf("ERROR: push implemented only in non-hyperbolic geometry\n");
|
||||
exit(1);
|
||||
}
|
||||
gd.emplace_back();
|
||||
gd.back().store();
|
||||
}
|
||||
|
||||
void pop() {
|
||||
gd.back().restore();
|
||||
gd.pop_back();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void pop_game() {
|
||||
if(gamestack::pushed()) {
|
||||
gamestack::pop();
|
||||
game_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
void popAllGames() {
|
||||
while(gamestack::pushed()) {
|
||||
gamestack::pop();
|
||||
}
|
||||
}
|
||||
|
||||
void stop_game() {
|
||||
if(!game_active) return;
|
||||
if(dual::split(stop_game)) return;
|
||||
DEBBI(DF_INIT, ("stop_game"));
|
||||
achievement_final(true);
|
||||
#if CAP_SAVE
|
||||
@ -1186,13 +1119,6 @@ void stop_game() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void push_game() {
|
||||
gamestack::push();
|
||||
pd_from = NULL;
|
||||
centerover.at = NULL;
|
||||
game_active = false;
|
||||
}
|
||||
|
||||
void set_geometry(eGeometry target) {
|
||||
if(geometry != target) {
|
||||
int old_DIM = DIM;
|
||||
@ -1269,6 +1195,10 @@ void switch_game_mode(char switchWhat) {
|
||||
|
||||
#if CAP_TOUR
|
||||
case rg::tour:
|
||||
while(gamestack::pushed()) {
|
||||
gamestack::pop();
|
||||
stop_game();
|
||||
}
|
||||
geometry = gNormal;
|
||||
yendor::on = tactic::on = princess::challenge = peace::on = inv::on = false;
|
||||
chaosmode = randomPatternsMode = false;
|
||||
@ -1360,6 +1290,8 @@ void switch_game_mode(char switchWhat) {
|
||||
void start_game() {
|
||||
if(game_active) return;
|
||||
DEBBI(DF_INIT, ("start_game"));
|
||||
if(dual::state == 1) dual::assign_landsides();
|
||||
if(dual::split(start_game)) return;
|
||||
restart:
|
||||
game_active = true;
|
||||
gamegen_failure = false;
|
||||
@ -1397,7 +1329,6 @@ void start_game() {
|
||||
|
||||
void restart_game(char switchWhat) {
|
||||
popScreenAll();
|
||||
popAllGames();
|
||||
stop_game();
|
||||
switch_game_mode(switchWhat);
|
||||
start_game();
|
||||
@ -1428,6 +1359,21 @@ auto cgm = addHook(clearmemory, 40, [] () {
|
||||
rosemap.clear();
|
||||
adj_memo.clear();
|
||||
}) +
|
||||
addHook(hooks_gamedata, 0, [] (gamedata* gd) {
|
||||
gd->store(pathq);
|
||||
gd->store(dcal);
|
||||
gd->store(recallCell);
|
||||
gd->store(butterflies);
|
||||
gd->store(buggycells);
|
||||
gd->store(crush_now);
|
||||
gd->store(crush_next);
|
||||
gd->store(rosemap);
|
||||
gd->store(adj_memo);
|
||||
gd->store(pd_from);
|
||||
gd->store(pd_range);
|
||||
gd->store(pathqm);
|
||||
gd->store(reachedfrom);
|
||||
}) +
|
||||
addHook(hooks_removecells, 0, [] () {
|
||||
eliminate_if(crush_next, is_cell_removed);
|
||||
eliminate_if(crush_now, is_cell_removed);
|
||||
|
18
tour.cpp
18
tour.cpp
@ -22,7 +22,7 @@ void setCanvas(presmode mode, char canv) {
|
||||
static char wc;
|
||||
static eLand ld;
|
||||
if(mode == pmStart) {
|
||||
push_game();
|
||||
gamestack::push();
|
||||
wc = patterns::whichCanvas;
|
||||
patterns::whichCanvas = canv;
|
||||
ld = firstland;
|
||||
@ -30,9 +30,9 @@ void setCanvas(presmode mode, char canv) {
|
||||
start_game();
|
||||
}
|
||||
if(mode == pmStop) {
|
||||
gamestack::pop();
|
||||
patterns::whichCanvas = wc;
|
||||
firstland = ld;
|
||||
pop_game();
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ void slidehelp() {
|
||||
}
|
||||
|
||||
void return_geometry() {
|
||||
pop_game();
|
||||
gamestack::pop();
|
||||
vid.scale = 1; vid.alpha = 1;
|
||||
presentation(pmGeometryReset);
|
||||
addMessage(XLAT("Returned to your game."));
|
||||
@ -103,7 +103,7 @@ bool handleKeyTour(int sym, int uni) {
|
||||
}
|
||||
if(sym == SDLK_BACKSPACE) {
|
||||
if(gamestack::pushed()) {
|
||||
pop_game();
|
||||
gamestack::pop();
|
||||
if(!(flags & QUICKGEO)) return true;
|
||||
}
|
||||
if(currentslide == 0) { slidehelp(); return true; }
|
||||
@ -158,7 +158,7 @@ bool handleKeyTour(int sym, int uni) {
|
||||
presentation(pmGeometry);
|
||||
|
||||
firstland = specialland = cwt.at->land;
|
||||
push_game();
|
||||
gamestack::push();
|
||||
switch(NUMBERKEY) {
|
||||
case '3':
|
||||
set_variation(eVariation::pure);
|
||||
@ -290,8 +290,8 @@ namespace ss {
|
||||
for(int i=0;; i++) {
|
||||
dialog::addBoolItem(XLAT(wts[i].name), wts == slides && i == currentslide, slidechars[i]);
|
||||
dialog::add_action([i] {
|
||||
if(geometry || CHANGED_VARIATION) {
|
||||
pop_game();
|
||||
if(gamestack::pushed()) {
|
||||
gamestack::pop();
|
||||
presentation(pmGeometryReset);
|
||||
}
|
||||
if(slides != wts) {
|
||||
@ -770,13 +770,13 @@ slide default_slides[] = {
|
||||
[] (presmode mode) {
|
||||
if(mode == 1) {
|
||||
firstland = cwt.at->land;
|
||||
push_game();
|
||||
gamestack::push();
|
||||
switch_game_mode(rg::shmup);
|
||||
start_game();
|
||||
}
|
||||
if(mode == 3) {
|
||||
shmup::clearMonsters();
|
||||
pop_game();
|
||||
gamestack::pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user