1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-06-28 07:52:53 +00:00

other changes in 9.4n

This commit is contained in:
Zeno Rogue 2017-06-18 18:51:46 +02:00
parent 131dfb2b4b
commit b172d3930d
7 changed files with 759 additions and 227 deletions

202
game.cpp
View File

@ -848,7 +848,7 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
if((flags & AF_ONLY_FBUG) && m2 != moPlayer && !isFriendlyOrBug(c2)) return false; if((flags & AF_ONLY_FBUG) && m2 != moPlayer && !isFriendlyOrBug(c2)) return false;
if((flags & AF_ONLY_ENEMY) && (m2 == moPlayer || isFriendlyOrBug(c2))) return false; if((flags & AF_ONLY_ENEMY) && (m2 == moPlayer || isFriendlyOrBug(c2))) return false;
if(m2 == moHedge && !(flags & (AF_STAB | AF_TOUGH | AF_EAT | AF_MAGIC | AF_LANCE | AF_SWORD_INTO | AF_HORNS))) if(m2 == moHedge && !(flags & (AF_STAB | AF_TOUGH | AF_EAT | AF_MAGIC | AF_LANCE | AF_SWORD_INTO | AF_HORNS | AF_BULL)))
if(!checkOrb(m1, itOrbThorns)) return false; if(!checkOrb(m1, itOrbThorns)) return false;
if(m2 == moDraugr && !(flags & (AF_SWORD | AF_MAGIC | AF_SWORD_INTO | AF_HORNS))) return false; if(m2 == moDraugr && !(flags & (AF_SWORD | AF_MAGIC | AF_SWORD_INTO | AF_HORNS))) return false;
@ -906,17 +906,17 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
// if(m2 == moTortoise && !(flags & AF_MAGIC)) return false; // if(m2 == moTortoise && !(flags & AF_MAGIC)) return false;
if(m2 == moRoseBeauty) if(m2 == moRoseBeauty)
if(!(flags & (AF_MAGIC | AF_LANCE | AF_GUN | AF_SWORD_INTO))) if(!(flags & (AF_MAGIC | AF_LANCE | AF_GUN | AF_SWORD_INTO | AF_BULL)))
if(!isMimic(m1)) if(!isMimic(m1))
if(!checkOrb(m1, itOrbBeauty) && !checkOrb(m1, itOrbAether) && !checkOrb(m1, itOrbShield)) if(!checkOrb(m1, itOrbBeauty) && !checkOrb(m1, itOrbAether) && !checkOrb(m1, itOrbShield))
if(!c1 || !c2 || !withRose(c1,c2)) if(!c1 || !c2 || !withRose(c1,c2))
return false; return false;
if(m2 == moFlailer && !c2->stuntime) if(m2 == moFlailer && !c2->stuntime)
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_SWORD_INTO))) return false; if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_SWORD_INTO | AF_BULL))) return false;
if(m2 == moVizier && c2->hitpoints > 1) if(m2 == moVizier && c2->hitpoints > 1)
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_FAST))) return false; if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_FAST | AF_BULL))) return false;
return true; return true;
} }
@ -1583,7 +1583,7 @@ void stunMonster(cell *c2) {
if(c2->monst == moPrincess) if(c2->monst == moPrincess)
playSound(c2, princessgender() ? "hit-princess" : "hit-prince"); playSound(c2, princessgender() ? "hit-princess" : "hit-prince");
} }
c2->stuntime = ( int newtime = (
c2->monst == moFatGuard ? 2 : c2->monst == moFatGuard ? 2 :
c2->monst == moSkeleton && c2->land != laPalace && c2->land != laHalloween ? 7 : c2->monst == moSkeleton && c2->land != laPalace && c2->land != laHalloween ? 7 :
isMetalBeast(c2->monst) ? 7 : isMetalBeast(c2->monst) ? 7 :
@ -1599,6 +1599,7 @@ void stunMonster(cell *c2) {
c2->monst == moHedge ? 1 : c2->monst == moHedge ? 1 :
c2->monst == moFlailer ? 1 : c2->monst == moFlailer ? 1 :
3); 3);
if(c2->stuntime < newtime) c2->stuntime = newtime;
if(isBull(c2->monst)) c2->mondir = NODIR; if(isBull(c2->monst)) c2->mondir = NODIR;
checkStunKill(c2); checkStunKill(c2);
} }
@ -2116,6 +2117,10 @@ bool attackMonster(cell *c, flagtype flags, eMonster killer) {
bool dostun = (flags & AF_ORSTUN) && attackJustStuns(c); bool dostun = (flags & AF_ORSTUN) && attackJustStuns(c);
if((flags & AF_HORNS) && hornStuns(c)) dostun = true; if((flags & AF_HORNS) && hornStuns(c)) dostun = true;
if((flags & AF_BULL) && c->monst == moVizier && c->hitpoints > 1) {
dostun = true;
c->stuntime = 2;
}
if(c->monst == moSkeleton && (flags & AF_SWORD)) dostun = false; if(c->monst == moSkeleton && (flags & AF_SWORD)) dostun = false;
@ -3265,6 +3270,7 @@ int moveval(cell *c1, cell *c2, int d, int mf) {
if(isWorm(m) && !passable_for(c1->monst, c2, c1, P_MONSTER)) return -1700; if(isWorm(m) && !passable_for(c1->monst, c2, c1, P_MONSTER)) return -1700;
if(canAttack(c1, m, c2, c2->monst, AF_GETPLAYER | mf) && !(mf & MF_NOATTACKS)) { if(canAttack(c1, m, c2, c2->monst, AF_GETPLAYER | mf) && !(mf & MF_NOATTACKS)) {
if(m == moRagingBull && c1->mondir != NODIR) return -1700;
if(mf & MF_MOUNT) { if(mf & MF_MOUNT) {
if(c2 == dragon::target) return 3000; if(c2 == dragon::target) return 3000;
else if(isFriendlyOrBug(c2)) return 500; else if(isFriendlyOrBug(c2)) return 500;
@ -3369,6 +3375,43 @@ int stayval(cell *c, flagtype mf) {
return 1000; return 1000;
} }
int totalbulldistance(cell *c, int k) {
int tbd = 0;
for(int p=0; p<numplayers(); p++) {
cell *c2 = shpos[p][(cshpos+SHSIZE-k-1)%SHSIZE];
if(c2) tbd += bulldistance(c, c2);
}
return tbd;
}
void determinizeBull(cell *c, int *posdir, int& nc) {
// determinize the Angry Beast movement:
// use the previous PC's positions as the tiebreaker
for(int k=0; k<SHSIZE && nc>1; k++) {
int pts[10];
for(int d=0; d<nc; d++) pts[d] = totalbulldistance(c->mov[posdir[d]], k);
int bestpts = 1000;
for(int d=0; d<nc; d++) if(pts[d] < bestpts) bestpts = pts[d];
int nc0 = 0;
for(int d=0; d<nc; d++) if(pts[d] == bestpts) posdir[nc0++] = posdir[d];
nc = nc0;
}
}
int determinizeBullPush(cellwalker bull) {
int nc = 2;
int dirs[2], positive;
cwstep(bull);
cell *c2 = bull.c;
if(c2->type == 6) return 1; // irrelevant
cwspin(bull, 3); dirs[0] = positive = bull.spin;
cwspin(bull, -6); dirs[1] = bull.spin;
determinizeBull(c2, dirs, nc);
if(dirs[0] == positive) return -1;
return 1;
}
int pickMoveDirection(cell *c, flagtype mf) { int pickMoveDirection(cell *c, flagtype mf) {
int posdir[10], nc = 0, bestval = stayval(c, mf); int posdir[10], nc = 0, bestval = stayval(c, mf);
@ -3381,25 +3424,8 @@ int pickMoveDirection(cell *c, flagtype mf) {
if(val == bestval) posdir[nc++] = d; if(val == bestval) posdir[nc++] = d;
} }
if(c->monst == moRagingBull) { if(c->monst == moRagingBull)
// determinize the Angry Beast movement: determinizeBull(c, posdir, nc);
// use the previous PC's positions as the tiebreaker
for(int k=0; k<SHSIZE && nc>1; k++) {
int pts[10];
for(int d=0; d<nc; d++) pts[d] = 0;
for(int d=0; d<nc; d++)
for(int p=0; p<numplayers(); p++) {
cell *c2 = shpos[p][(cshpos+SHSIZE-k-1)%SHSIZE];
if(c2) pts[d] += bulldistance(c->mov[posdir[d]], c2);
}
int bestpts = 1000;
for(int d=0; d<nc; d++)
if(pts[d] < bestpts) bestpts = pts[d];
int nc0 = 0;
for(int d=0; d<nc; d++) if(pts[d] == bestpts) posdir[nc0++] = posdir[d];
nc = nc0;
}
}
if(!nc) return -1; if(!nc) return -1;
nc = hrand(nc); nc = hrand(nc);
@ -3427,6 +3453,32 @@ int pickDownDirection(cell *c, flagtype mf) {
return downs[hrand(qdowns)]; return downs[hrand(qdowns)];
} }
template<class T>
cell *determinePush(cellwalker who, cell *c2, int subdir, T valid) {
cellwalker push = who;
cwstep(push);
cwspin(push, 3 * -subdir);
cwstep(push);
if(valid(push.c)) return push.c;
if(c2->type == 7) {
cwstep(push);
cwspin(push, 1 * -subdir);
cwstep(push);
if(valid(push.c)) return push.c;
}
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, -2); cwstep(push);
}
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
}
if(valid(push.c)) return push.c;
}
return c2;
}
// Angry Beast attack // Angry Beast attack
// note: this is done both before and after movement // note: this is done both before and after movement
void beastAttack(cell *c, bool player) { void beastAttack(cell *c, bool player) {
@ -3436,8 +3488,27 @@ void beastAttack(cell *c, bool player) {
int flags = AF_BULL | AF_ORSTUN; int flags = AF_BULL | AF_ORSTUN;
if(player) flags |= AF_GETPLAYER; if(player) flags |= AF_GETPLAYER;
if(!opposite) flags |= AF_ONLY_FBUG; if(!opposite) flags |= AF_ONLY_FBUG;
if(canAttack(c, moRagingBull, c2, c2->monst, flags)) if(canAttack(c, moRagingBull, c2, c2->monst, flags)) {
attackMonster(c2, flags | AF_MSG, moRagingBull); attackMonster(c2, flags | AF_MSG, moRagingBull);
if(c2->monst && c2->stuntime) {
cellwalker bull (c, d);
int subdir = determinizeBullPush(bull);
cell *c3 = determinePush(bull, c2, subdir, [c2] (cell *c) { return passable(c, c2, P_BLOW); });
if(c3 && c3 != c2)
pushMonster(c3, c2);
}
}
if(c2->wall == waThumperOff) {
playSound(c2, "click");
c2->wall = waThumperOn;
}
if(c2->wall == waThumperOn) {
cellwalker bull (c, d);
int subdir = determinizeBullPush(bull);
cell *c3 = determinePush(bull, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, c); });
if(c3 && c3 != c2)
pushThumper(c2, c3);
}
} }
} }
@ -6358,6 +6429,10 @@ void killFriendlyIvy() {
killMonster(c2, moPlayer, 0); killMonster(c2, moPlayer, 0);
} }
bool monsterPushable(cell *c2) {
return (c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise);
}
bool movepcto(int d, int subdir, bool checkonly) { bool movepcto(int d, int subdir, bool checkonly) {
global_pushto = NULL; global_pushto = NULL;
bool switchplaces = false; bool switchplaces = false;
@ -6440,21 +6515,8 @@ bool movepcto(int d, int subdir, bool checkonly) {
} }
if(c2->wall == waThumperOn && !c2->monst && !nonAdjacentPlayer(c2, cwt.c)) { if(c2->wall == waThumperOn && !c2->monst && !nonAdjacentPlayer(c2, cwt.c)) {
cellwalker push = cwt; cell *c3 = determinePush(cwt, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.c); });
cwstep(push); if(c3 == c2) {
cwspin(push, 3 * -subdir);
cwstep(push);
/* if(w == waBigStatue && push.c->type == 7) {
if(checkonly) return false;
addMessage(XLAT("%The1 is too heavy to put it back on the pedestal.", c2->wall));
return false;
} */
if((!canPushThumperOn(push.c, c2, cwt.c) && c2->type == 7)) {
cwstep(push);
cwspin(push, 1 * -subdir);
cwstep(push);
}
if(!canPushThumperOn(push.c, c2, cwt.c)) {
if(checkonly) return false; if(checkonly) return false;
addMessage(XLAT("No room to push %the1.", c2->wall)); addMessage(XLAT("No room to push %the1.", c2->wall));
return false; return false;
@ -6463,40 +6525,13 @@ bool movepcto(int d, int subdir, bool checkonly) {
if(!checkonly && errormsgs) wouldkill("%The1 would kill you there!"); if(!checkonly && errormsgs) wouldkill("%The1 would kill you there!");
return false; return false;
} }
global_pushto = push.c; global_pushto = c3;
if(checkonly) return true; if(checkonly) return true;
addMessage(XLAT("You push %the1.", c2->wall)); addMessage(XLAT("You push %the1.", c2->wall));
lastmovetype = lmPush; lastmove = cwt.c; lastmovetype = lmPush; lastmove = cwt.c;
pushThumper(c2, push.c); pushThumper(c2, c3);
} }
/* if((c2->wall == waBigStatue) && c2->type == 7 && !monstersnear(c2)) {
int q = 0;
for(int i=3; i<=4; i++) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, i);
cwstep(push);
if(passable(push.c, c2, false, true)) q++;
}
if(!q) {
if(checkonly) return false;
addMessage(XLAT("No room to push %the1.", c2->wall));
return false;
}
if(checkonly) return true;
addMessage(XLAT("You push %the1.", c2->wall));
c2->wall = waNone;
for(int i=3; i<=4; i++) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, i);
cwstep(push);
if(passable(push.c, c2, false, true))
push.c->wall = waBigStatue;
}
} */
if(c2->item == itHolyGrail && roundTableRadius(c2) < newRoundTableRadius()) { if(c2->item == itHolyGrail && roundTableRadius(c2) < newRoundTableRadius()) {
if(checkonly) return false; if(checkonly) return false;
addMessage(XLAT("That was not a challenge. Find a larger castle!")); addMessage(XLAT("That was not a challenge. Find a larger castle!"));
@ -6658,33 +6693,14 @@ bool movepcto(int d, int subdir, bool checkonly) {
return false; return false;
} }
cell *pushto = NULL;
if(isStunnable(c2->monst) && c2->hitpoints > 1) {
// pushto=c2 means that the monster is not killed and thus // pushto=c2 means that the monster is not killed and thus
// still counts for lightning in monstersnear // still counts for lightning in monstersnear
cell *pushto = NULL;
if(isStunnable(c2->monst) && c2->hitpoints > 1) {
if(monsterPushable(c2))
pushto = determinePush(cwt, c2, subdir, [c2] (cell *c) { return passable(c, c2, P_BLOW); });
else
pushto = c2; pushto = c2;
if(c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, 3 * -subdir);
cwstep(push);
if(c2->type == 7 && !passable(push.c, c2, P_BLOW)) {
cwstep(push);
cwspin(push, 1 * -subdir);
cwstep(push);
}
if(!passable(push.c, c2, P_BLOW) && gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, -2); cwstep(push);
}
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
}
}
if(passable(push.c, c2, P_BLOW))
pushto = push.c;
}
} }
if(c2->monst == moTroll || c2->monst == moFjordTroll || if(c2->monst == moTroll || c2->monst == moFjordTroll ||
c2->monst == moForestTroll || c2->monst == moStormTroll || c2->monst == moVineSpirit) c2->monst == moForestTroll || c2->monst == moStormTroll || c2->monst == moVineSpirit)

242
graph.cpp
View File

@ -114,14 +114,14 @@ movedir mousedest, joydir;
int mousex, mousey, joyx, joyy, panjoyx, panjoyy; int mousex, mousey, joyx, joyy, panjoyx, panjoyy;
bool autojoy = true; bool autojoy = true;
hyperpoint mouseh; hyperpoint mouseh, mouseoh;
bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick, bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick,
forcetarget, lshiftclick, lctrlclick; forcetarget, lshiftclick, lctrlclick;
bool gtouched; bool gtouched;
bool revcontrol; bool revcontrol;
int getcstat; ld getcshift; bool inslider; int getcstat, lgetcstat; ld getcshift; bool inslider;
int ZZ; int ZZ;
@ -1252,13 +1252,23 @@ double q3 = sqrt(double(3));
bool outofmap(hyperpoint h) { bool outofmap(hyperpoint h) {
if(euclid) if(euclid)
return h[0] * h[0] + h[1] * h[1] > 15 * eurad; return false; // h[0] * h[0] + h[1] * h[1] > 15 * eurad;
else if(sphere) else if(sphere)
return h[2] < .1 && h[2] > -.1 && h[1] > -.1 && h[1] < .1 && h[0] > -.1 && h[0] < .1; return h[2] < .1 && h[2] > -.1 && h[1] > -.1 && h[1] < .1 && h[0] > -.1 && h[0] < .1;
else else
return h[2] < .5; return h[2] < .5;
} }
bool mouseout() {
if((getcstat != '-' && getcstat) || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh);
}
bool mouseout2() {
if((getcstat && getcstat != '-') || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh) || outofmap(mouseoh);
}
void drawShield(const transmatrix& V, eItem it) { void drawShield(const transmatrix& V, eItem it) {
float ds = ticks / 300.; float ds = ticks / 300.;
int col = iinf[it].color; int col = iinf[it].color;
@ -1432,53 +1442,9 @@ void drawStunStars(const transmatrix& V, int t) {
} }
} }
bool drawUserShape(transmatrix V, int group, int id, int color) { hyperpoint mirrorif(const hyperpoint& V, bool b) {
#ifdef MOBILE if(b) return Mirror*V;
return false; else return V;
#else
usershape *us = usershapes[group][id];
if(!us) return false;
for(int i=0; i<USERLAYERS; i++) {
usershapelayer& ds(us->d[i]);
hpcshape& sh(ds.sh);
if(sh.s != sh.e)
queuepoly(V, sh, ds.color ? ds.color : color);
}
#ifndef NOEDIT
if(cmode == emDraw && mapeditor::editingShape(group, id)) {
usershapelayer &ds(usershapes[group][id]->d[mapeditor::dslayer]);
/* for(int a=0; a<size(ds.list); a++) {
hyperpoint P2 = V * ds.list[a];
int xc, yc, sc;
getcoord(P2, xc, yc, sc);
queuechr(xc, yc, sc, 10, 'x',
a == 0 ? 0x00FF00 :
a == size(ds.list)-1 ? 0xFF0000 :
0xFFFF00);
} */
hyperpoint mh = inverse(mapeditor::drawtrans) * mouseh;
for(int a=0; a<ds.rots; a++)
for(int b=0; b<(ds.sym?2:1); b++) {
if(outofmap(mouseh)) break;
hyperpoint P2 = V * spin(2*M_PI*a/ds.rots) * (b?Mirror*mh:mh);
queuechr(P2, 10, 'x', 0xFF00FF);
}
}
#endif
return true;
#endif
} }
string csnameid(int id) { string csnameid(int id) {
@ -1773,7 +1739,7 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks,
xsh = NULL; xsh = NULL;
} }
else if(drawUserShape(V, 2, it, darkena(icol, 0, 0xFF))) ; else if(mapeditor::drawUserShape(V, 2, it, darkena(icol, 0, 0xFF), c)) ;
else if(it == itRose) { else if(it == itRose) {
for(int u=0; u<4; u++) for(int u=0; u<4; u++)
@ -1838,11 +1804,6 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
char xch = minf[m].glyph; char xch = minf[m].glyph;
#ifndef NOEDIT
if(where == mapeditor::drawcell)
mapeditor::drawtrans = V;
#endif
if(m == moTortoise && where && where->stuntime >= 3) if(m == moTortoise && where && where->stuntime >= 3)
drawStunStars(V, where->stuntime-2); drawStunStars(V, where->stuntime-2);
else if (m == moTortoise || m == moPlayer || (where && !where->stuntime)) ; else if (m == moTortoise || m == moPlayer || (where && !where->stuntime)) ;
@ -1860,7 +1821,7 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
charstyle& cs = getcs(); charstyle& cs = getcs();
bool havus = drawUserShape(V, 0, cs.charid, cs.skincolor); bool havus = mapeditor::drawUserShape(V, 0, cs.charid, cs.skincolor, where);
if(mapeditor::drawplayer && !havus) { if(mapeditor::drawplayer && !havus) {
@ -1974,12 +1935,12 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
} }
} }
else if(drawUserShape(V, 1, m, darkena(col, 0, 0xFF))) return false; else if(mapeditor::drawUserShape(V, 1, m, darkena(col, 0, 0xFF), where)) return false;
else if(isMimic(m) || m == moShadow || m == moIllusion) { else if(isMimic(m) || m == moShadow || m == moIllusion) {
charstyle& cs = getcs(); charstyle& cs = getcs();
if(drawUserShape(V, 0, (cs.charid&1)?1:0, darkena(col, 0, 0x80))) return false; if(mapeditor::drawUserShape(V, 0, (cs.charid&1)?1:0, darkena(col, 0, 0x80), where)) return false;
if(cs.charid >= 8) { if(cs.charid >= 8) {
queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xC0)); queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xC0));
@ -2729,11 +2690,7 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) {
else else
Vb = Vb * ddspin(c, c->mondir); Vb = Vb * ddspin(c, c->mondir);
#ifndef NOEDIT if(mapeditor::drawUserShape(Vb, 1, c->monst, (col << 8) + 0xFF, c)) return false;
if(c == mapeditor::drawcell) mapeditor::drawtrans = Vb;
#endif
if(drawUserShape(Vb, 1, c->monst, (col << 8) + 0xFF)) return false;
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy)
queuepoly(Vb, shIBranch, (col << 8) + 0xFF); queuepoly(Vb, shIBranch, (col << 8) + 0xFF);
@ -2863,7 +2820,7 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) {
if(flipplayer) Vs = Vs * pispin; if(flipplayer) Vs = Vs * pispin;
if(!outofmap(mouseh) && !nospins) { if(!mouseout() && !nospins) {
// transmatrix invxy = Id; invxy[0][0] = invxy[1][1] = -1; // transmatrix invxy = Id; invxy[0][0] = invxy[1][1] = -1;
hyperpoint P2 = Vs * inverse(cwtV) * mouseh; hyperpoint P2 = Vs * inverse(cwtV) * mouseh;
@ -4091,7 +4048,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
ivoryz = isGravityLand(c->land); ivoryz = isGravityLand(c->land);
transmatrix& gm = gmatrix[c]; transmatrix& gm = gmatrix[c];
bool orig = (gm[2][2] == 0 || fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8); bool orig =
gm[2][2] == 0 ? true :
torus ? hypot(gm[0][2], gm[1][2]) >= hypot(V[0][2], V[1][2]) :
(sphere && vid.alphax >= 1.001) ? fabs(gm[2][2]-1) <= fabs(V[2][2]-1) :
fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8;
if(orig) gm = V; if(orig) gm = V;
@ -4164,12 +4125,14 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
mouseover2 = c; mouseover2 = c;
} }
if(!torus) {
double dfc = euclid ? intval(VC0, C0) : VC0[2]; double dfc = euclid ? intval(VC0, C0) : VC0[2];
if(dfc < centdist) { if(dfc < centdist) {
centdist = dfc; centdist = dfc;
centerover = c; centerover = c;
} }
}
int orbrange = (items[itRevolver] ? 3 : 2); int orbrange = (items[itRevolver] ? 3 : 2);
@ -4356,12 +4319,6 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
bool eoh = euclid || purehepta; bool eoh = euclid || purehepta;
#ifndef NOEDIT
if(c == mapeditor::drawcell && c != cwt.c && !c->monst && !c->item) {
mapeditor::drawtrans = Vpdir;
}
#endif
if(c->wall == waChasm) { if(c->wall == waChasm) {
if(c->land == laZebra) fd++; if(c->land == laZebra) fd++;
if(c->land == laHalloween && !wmblack) { if(c->land == laHalloween && !wmblack) {
@ -4372,8 +4329,8 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
} }
#ifndef NOEDIT #ifndef NOEDIT
if(drawUserShape(Vpdir, mapeditor::cellShapeGroup(), mapeditor::realpatternsh(c), if(mapeditor::drawUserShape(Vpdir, mapeditor::cellShapeGroup(), mapeditor::realpatternsh(c),
darkena(fcol, fd, cmode == emDraw ? 0xC0 : 0xFF))); darkena(fcol, fd, cmode == emDraw ? 0xC0 : 0xFF), c));
else if(mapeditor::whichShape == '7') { else if(mapeditor::whichShape == '7') {
if(ishept(c)) if(ishept(c))
@ -5309,6 +5266,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
#ifndef NOEDIT #ifndef NOEDIT
if(cmode == emMapEditor && !mapeditor::subscreen && lmouseover && darken == 0 && if(cmode == emMapEditor && !mapeditor::subscreen && lmouseover && darken == 0 &&
!mouseout() &&
(mapeditor::whichPattern ? mapeditor::subpattern(c) == mapeditor::subpattern(lmouseover) : c == lmouseover)) { (mapeditor::whichPattern ? mapeditor::subpattern(c) == mapeditor::subpattern(lmouseover) : c == lmouseover)) {
queuecircle(V, .78, 0x00FFFFFF); queuecircle(V, .78, 0x00FFFFFF);
} }
@ -5338,7 +5296,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
} }
bool confusingGeometry() { bool confusingGeometry() {
return elliptic || quotient == 1; return elliptic || quotient == 1 || torus;
} }
struct flashdata { struct flashdata {
@ -5717,6 +5675,25 @@ string princessReviveHelp() {
return h; return h;
} }
void describeOrb(string& help, const orbinfo& oi) {
eOrbLandRelation olr = getOLR(oi.orb, cwt.c->land);
eItem tr = treasureType(oi.l);
help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land));
int t = items[tr] * landMultiplier(oi.l);
if(t >= 25)
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
help += XLAT("\nSpawn rate (as prize Orb): %1%/%2\n",
its(int(.5 + 100 * orbprizefun(t))),
its(oi.gchance));
}
if(t >= 10)
if(olr == olrHub) {
help += XLAT("\nSpawn rate (in Hubs): %1%/%2\n",
its(int(.5 + 100 * orbcrossfun(t))),
its(oi.gchance));
}
}
string generateHelpForItem(eItem it) { string generateHelpForItem(eItem it) {
string help = helptitle(XLATN(iinf[it].name), iinf[it].color); string help = helptitle(XLATN(iinf[it].name), iinf[it].color);
@ -5786,25 +5763,23 @@ string generateHelpForItem(eItem it) {
} }
if(itemclass(it) == IC_ORB || it == itGreenStone || it == itOrbYendor) { if(itemclass(it) == IC_ORB || it == itGreenStone || it == itOrbYendor) {
eOrbLandRelation olr = getOLR(it, cwt.c->land);
for(int i=0; i<ORBLINES; i++) { for(int i=0; i<ORBLINES; i++) {
orbinfo& oi(orbinfos[i]); const orbinfo& oi(orbinfos[i]);
if(oi.orb == it) { if(oi.orb == it) describeOrb(help, oi);
eItem tr = treasureType(oi.l);
help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land));
int t = items[tr] * landMultiplier(oi.l);
if(t >= 25)
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
help += XLAT("\nSpawn rate (as prize Orb): %1%/%2\n",
its(int(.5 + 100 * orbprizefun(t))),
its(oi.gchance));
} }
if(t >= 10) }
if(olr == olrHub) {
help += XLAT("\nSpawn rate (in Hubs): %1%/%2\n", if(itemclass(it) == IC_TREASURE) {
its(int(.5 + 100 * orbcrossfun(t))), for(int i=0; i<ORBLINES; i++) {
its(oi.gchance)); const orbinfo& oi(orbinfos[i]);
if(treasureType(oi.l) == it) {
if(oi.gchance > 0) {
help += XLAT("\n\nOrb unlocked: %1", oi.orb);
describeOrb(help, oi);
}
else if(oi.l == cwt.c->land) {
help += XLAT("\n\nSecondary orb: %1", oi.orb);
describeOrb(help, oi);
} }
} }
} }
@ -6192,10 +6167,15 @@ void describeMouseover() {
out += " " + describeRPM(c->land); out += " " + describeRPM(c->land);
if(euclid && cheater) { if(euclid && cheater) {
if(torus) {
out += " ("+its(decodeId(c->master))+")";
}
else {
eucoord x, y; eucoord x, y;
decodeMaster(c->master, x, y); decodeMaster(c->master, x, y);
out += " ("+its(short(x))+","+its(short(y))+")"; out += " ("+its(short(x))+","+its(short(y))+")";
} }
}
// char zz[64]; sprintf(zz, " P%d", princess::dist(c)); out += zz; // char zz[64]; sprintf(zz, " P%d", princess::dist(c)); out += zz;
// out += " MD"+its(c->mpdist); // out += " MD"+its(c->mpdist);
@ -6233,10 +6213,10 @@ void describeMouseover() {
if(isActivable(c)) out += XLAT(" (touch to activate)"); if(isActivable(c)) out += XLAT(" (touch to activate)");
if(hasTimeout(c)) out += XLAT(" [" + turnstring(c->wparam) + "]"); if(hasTimeout(c)) out += " [" + turnstring(c->wparam) + "]";
if(isReptile(c->wall)) if(isReptile(c->wall))
out += XLAT(" [" + turnstring((unsigned char) c->wparam) + "]"); out += " [" + turnstring((unsigned char) c->wparam) + "]";
if(c->monst) { if(c->monst) {
out += ", "; out += XLAT1(minf[c->monst].name); out += ", "; out += XLAT1(minf[c->monst].name);
@ -6299,7 +6279,6 @@ void describeMouseover() {
if(isWarped(c)) if(isWarped(c))
help += s0 + "\n\n" + warpdesc; help += s0 + "\n\n" + warpdesc;
} }
else if(cmode == emVisual1) { else if(cmode == emVisual1) {
if(getcstat == 'p') { if(getcstat == 'p') {
@ -6352,6 +6331,10 @@ void describeMouseover() {
mouseovers = out; mouseovers = out;
#ifdef ROGUEVIZ
rogueviz::describe(c);
#endif
int col = linf[cwt.c->land].color; int col = linf[cwt.c->land].color;
if(cwt.c->land == laRedRock) col = 0xC00000; if(cwt.c->land == laRedRock) col = 0xC00000;
@ -6422,12 +6405,30 @@ transmatrix eumove(int x, int y) {
return Mat; return Mat;
} }
transmatrix eumovedir(int d) {
d = fix6(d);
switch(d) {
case 0: return eumove(1,0);
case 1: return eumove(0,1);
case 2: return eumove(-1,1);
case 3: return eumove(-1,0);
case 4: return eumove(0,-1);
case 5: return eumove(1,-1);
}
return eumove(0,0);
}
void drawEuclidean() { void drawEuclidean() {
DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n")); DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n"));
eucoord px, py; eucoord px=0, py=0;
if(!centerover) centerover = cwt.c; if(!centerover) centerover = cwt.c;
// printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c, // printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c,
// mindx, mindy, maxdx, maxdy); // mindx, mindy, maxdx, maxdy);
int pid;
const bool b = torus;
if(b)
pid = decodeId(centerover->master);
else
decodeMaster(centerover->master, px, py); decodeMaster(centerover->master, px, py);
int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1; int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1;
@ -6435,12 +6436,21 @@ void drawEuclidean() {
for(int dx=minsx; dx<=maxsx; dx++) for(int dx=minsx; dx<=maxsx; dx++)
for(int dy=minsy; dy<=maxsy; dy++) { for(int dy=minsy; dy<=maxsy; dy++) {
reclevel = eudist(dx, dy);
cell *c;
transmatrix Mat;
if(b) {
reclevel = eudist(dx, dy);
c = getTorusId(pid+torusconfig::dx*dx+torusconfig::dy*dy);
Mat = eumove(dx,dy);
}
else {
eucoord x = dx+px; eucoord x = dx+px;
eucoord y = dy+py; eucoord y = dy+py;
reclevel = eudist(dx, dy); c = euclideanAt(x,y);
cell *c = euclideanAt(x,y); Mat = eumove(x, y);
}
if(!c) continue; if(!c) continue;
transmatrix Mat = eumove(x, y);
Mat = View * Mat; Mat = View * Mat;
// Mat[0][0] = -1; // Mat[0][0] = -1;
@ -6526,6 +6536,8 @@ void drawthemap() {
} }
} }
if(mapeditor::autochoose) mapeditor::ew = mapeditor::ewsearch;
mapeditor::ewsearch.dist = 1e30;
modist = 1e20; mouseover = NULL; modist = 1e20; mouseover = NULL;
modist2 = 1e20; mouseover2 = NULL; modist2 = 1e20; mouseover2 = NULL;
mouseovers = XLAT("Press F1 or right click for help"); mouseovers = XLAT("Press F1 or right click for help");
@ -6535,7 +6547,8 @@ void drawthemap() {
#ifdef TOUR #ifdef TOUR
if(tour::on) mouseovers = tour::tourhelp; if(tour::on) mouseovers = tour::tourhelp;
#endif #endif
centdist = 1e20; centerover = NULL; centdist = 1e20;
if(!torus) centerover = NULL;
for(int i=0; i<multi::players; i++) { for(int i=0; i<multi::players; i++) {
multi::ccdist[i] = 1e20; multi::ccat[i] = NULL; multi::ccdist[i] = 1e20; multi::ccat[i] = NULL;
@ -6544,7 +6557,7 @@ void drawthemap() {
#ifdef MOBILE #ifdef MOBILE
mouseovers = XLAT("No info about this..."); mouseovers = XLAT("No info about this...");
#endif #endif
if(outofmap(mouseh)) if(mouseout())
modist = -5; modist = -5;
playerfound = false; playerfound = false;
// playerfoundL = false; // playerfoundL = false;
@ -6617,7 +6630,7 @@ void drawthemap() {
Uint8 *keystate = SDL_GetKeyState(NULL); Uint8 *keystate = SDL_GetKeyState(NULL);
lmouseover = mouseover; lmouseover = mouseover;
bool useRangedOrb = (!(vid.shifttarget & 1) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1) || (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]); bool useRangedOrb = (!(vid.shifttarget & 1) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1) || (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
if(!useRangedOrb && cmode != emMapEditor && DEFAULTCONTROL && !outofmap(mouseh)) { if(!useRangedOrb && cmode != emMapEditor && DEFAULTCONTROL && !mouseout()) {
void calcMousedest(); void calcMousedest();
calcMousedest(); calcMousedest();
cellwalker cw = cwt; bool f = flipplayer; cellwalker cw = cwt; bool f = flipplayer;
@ -6796,7 +6809,7 @@ void movepckeydir(int d) {
} }
void calcMousedest() { void calcMousedest() {
if(outofmap(mouseh)) return; if(mouseout()) return;
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; } if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
ld mousedist = intval(mouseh, tC0(shmup::ggmatrix(cwt.c))); ld mousedist = intval(mouseh, tC0(shmup::ggmatrix(cwt.c)));
mousedest.d = -1; mousedest.d = -1;
@ -6918,6 +6931,7 @@ void calcparam() {
vid.alphax = vid.alpha + eye; vid.alphax = vid.alpha + eye;
vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001; vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001;
vid.goteyes2 = vid.goteyes; vid.goteyes2 = vid.goteyes;
vid.scrdist = vid.radius;
} }
void displayButton(int x, int y, const string& name, int key, int align, int rad) { void displayButton(int x, int y, const string& name, int key, int align, int rad) {
@ -6927,6 +6941,16 @@ void displayButton(int x, int y, const string& name, int key, int align, int rad
} }
} }
char mousekey = 'n';
char newmousekey;
void displaymm(char c, int x, int y, int rad, int size, const string& title, int align) {
if(displayfr(x, y, rad, size, title, c == mousekey ? 0xFF8000 : 0xC0C0C0, align)) {
displayfr(x, y, rad, size, title, 0xFFFF00, align);
getcstat = SETMOUSEKEY, newmousekey = c;
}
}
bool displayButtonS(int x, int y, const string& name, int col, int align, int size) { bool displayButtonS(int x, int y, const string& name, int col, int align, int size) {
if(displaystr(x, y, 0, size, name, col, align)) { if(displaystr(x, y, 0, size, name, col, align)) {
displaystr(x, y, 0, size, name, 0xFFFF00, align); displaystr(x, y, 0, size, name, 0xFFFF00, align);
@ -7413,8 +7437,8 @@ transmatrix atscreenpos(ld x, ld y, ld size) {
V[0][2] += (x - vid.xcenter); V[0][2] += (x - vid.xcenter);
V[1][2] += (y - vid.ycenter); V[1][2] += (y - vid.ycenter);
V[0][0] = size * 2 * crossf / hcrossf; V[0][0] = size * 2 * hcrossf / crossf;
V[1][1] = size * 2 * crossf / hcrossf; V[1][1] = size * 2 * hcrossf / crossf;
V[2][2] = vid.scrdist; V[2][2] = vid.scrdist;
if(euclid) V[2][2] /= EUCSCALE; if(euclid) V[2][2] /= EUCSCALE;
@ -7964,6 +7988,7 @@ void drawscreen() {
if(conformal::includeHistory && cmode != emProgress) conformal::restoreBack(); if(conformal::includeHistory && cmode != emProgress) conformal::restoreBack();
lgetcstat = getcstat;
getcstat = 0; inslider = false; getcstat = 0; inslider = false;
if(cmode == emNormal || cmode == emQuit) drawStats(); if(cmode == emNormal || cmode == emQuit) drawStats();
@ -8132,7 +8157,7 @@ void restartGraph() {
linepatterns::clearAll(); linepatterns::clearAll();
if(currentmap) { if(currentmap) {
if(euclid) { if(euclid) {
centerover = euclideanAtCreate(0,0); centerover = torus ? getTorusId(0) : euclideanAtCreate(0,0);
} }
else { else {
viewctr.h = currentmap->getOrigin(); viewctr.h = currentmap->getOrigin();
@ -9210,7 +9235,7 @@ void mainloopiter() {
} }
if(ev.type == SDL_MOUSEMOTION) { if(ev.type == SDL_MOUSEMOTION) {
hyperpoint mouseoh = mouseh; mouseoh = mouseh;
mousing = true; mousing = true;
mousemoved = true; mousemoved = true;
@ -9224,8 +9249,7 @@ void mainloopiter() {
#endif #endif
mouseh = gethyper(mousex, mousey); mouseh = gethyper(mousex, mousey);
if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2() &&
!outofmap(mouseh) && !outofmap(mouseoh) &&
mouseh[2] < 50 && mouseoh[2] < 50) { mouseh[2] < 50 && mouseoh[2] < 50) {
panning(mouseoh, mouseh); panning(mouseoh, mouseh);
} }

View File

@ -309,6 +309,11 @@ else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { if(curphase ==
else if(argis("-svgsize")) { else if(argis("-svgsize")) {
shift(); sscanf(args(), "%d/%d", &svg::svgsize, &svg::divby); shift(); sscanf(args(), "%d/%d", &svg::svgsize, &svg::divby);
} }
else if(argis("-svgfont")) {
shift(); svg::font = args();
// note: use '-svgfont latex' to produce text output as: \myfont{size}{text}
// (this is helpful with Inkscape's PDF+TeX output feature; define \myfont yourself)
}
#ifndef NOPNG #ifndef NOPNG
else if(argis("-pngsize")) { else if(argis("-pngsize")) {
shift(); sscanf(args(), "%d", &pngres); shift(); sscanf(args(), "%d", &pngres);

43
hyper.h
View File

@ -216,6 +216,7 @@ namespace shmup {
void virtualRebase(cell*& base, transmatrix& at, bool tohex); void virtualRebase(cell*& base, transmatrix& at, bool tohex);
void virtualRebase(shmup::monster *m, bool tohex); void virtualRebase(shmup::monster *m, bool tohex);
void fixStorage();
} }
// graph // graph
@ -384,6 +385,7 @@ namespace mapeditor {
int patterndir(cell *c, char w = whichPattern); int patterndir(cell *c, char w = whichPattern);
int subpattern(cell *c); int subpattern(cell *c);
extern cell *drawcell; extern cell *drawcell;
void initdraw(cell *c);
} }
#ifndef NORUG #ifndef NORUG
@ -992,6 +994,7 @@ namespace rogueviz {
void fixparam(); void fixparam();
int readArgs(); int readArgs();
void close(); void close();
void mark(cell *c);
} }
#endif #endif
@ -1004,6 +1007,7 @@ void movecost(cell* from, cell *to);
void checkmove(); void checkmove();
transmatrix eumove(int x, int y); transmatrix eumove(int x, int y);
transmatrix eumovedir(int d);
#ifndef NOSAVE #ifndef NOSAVE
void loadScores(); void loadScores();
@ -1136,6 +1140,8 @@ namespace tour {
pmGeometry = 11, pmGeometryReset = 13, pmGeometryStart = 15 pmGeometry = 11, pmGeometryReset = 13, pmGeometryStart = 15
}; };
void setCanvas(presmode mode, char canv);
void presentation(presmode mode); void presentation(presmode mode);
void checkGoodLand(eLand l); void checkGoodLand(eLand l);
int getid(); int getid();
@ -1145,12 +1151,34 @@ namespace tour {
extern function<bool(eLand)> showland; extern function<bool(eLand)> showland;
void start(); void start();
struct slide {
const char *name; int unused_id; int flags; const char *help;
function<void(presmode mode)> action;
} ;
extern slide *slides;
extern slide default_slides[];
static const int LEGAL_NONE=0;
static const int LEGAL_UNLIMITED=1;
static const int LEGAL_HYPERBOLIC=2;
static const int LEGAL_ANY=3;
static const int LEGAL_NONEUC=4;
static const int QUICKSKIP=8;
static const int FINALSLIDE=16;
extern slide slideHypersian;
extern slide slideExpansion;
}; };
#endif #endif
namespace rogueviz { namespace rogueviz {
extern bool rog3; extern bool rog3;
extern bool rvwarp; extern bool rvwarp;
namespace rvtour {
extern tour::slide rvslides[];
}
}; };
extern bool doCross; extern bool doCross;
@ -1199,3 +1227,18 @@ int celldistance(cell *c1, cell *c2);
bool behindsphere(const transmatrix& V); bool behindsphere(const transmatrix& V);
extern hyperpoint pirateCoords; extern hyperpoint pirateCoords;
bool mouseout();
bool againstWind(cell *c2, cell *c1); // to, from
transmatrix atscreenpos(ld x, ld y, ld size);
hyperpoint mirrorif(const hyperpoint& V, bool b);
#define SETMOUSEKEY 5000
extern char mousekey;
extern char newmousekey;
void displaymm(char c, int x, int y, int rad, int size, const string& title, int align);
bool canPushThumperOn(cell *tgt, cell *thumper, cell *player);
void pushThumper(cell *th, cell *cto);

View File

@ -1,8 +1,8 @@
id ICON "hr-icon.ico" id ICON "hr-icon.ico"
1 VERSIONINFO 1 VERSIONINFO
FILEVERSION 9,4,0,12 FILEVERSION 9,4,0,14
PRODUCTVERSION 9,4,0,12 PRODUCTVERSION 9,4,0,14
BEGIN BEGIN
BLOCK "StringFileInfo" BLOCK "StringFileInfo"
BEGIN BEGIN
@ -10,12 +10,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Zeno Rogue" VALUE "CompanyName", "Zeno Rogue"
VALUE "FileDescription", "A roguelike in non-euclidean space" VALUE "FileDescription", "A roguelike in non-euclidean space"
VALUE "FileVersion", "94k" VALUE "FileVersion", "94n"
VALUE "InternalName", "hyper" VALUE "InternalName", "hyper"
VALUE "LegalCopyright", "Zeno Rogue" VALUE "LegalCopyright", "Zeno Rogue"
VALUE "OriginalFilename", "hyper.exe" VALUE "OriginalFilename", "hyper.exe"
VALUE "ProductName", "HyperRogue" VALUE "ProductName", "HyperRogue"
VALUE "ProductVersion", "9.4l" VALUE "ProductVersion", "9.4n"
END END
END END

View File

@ -1,6 +1,6 @@
#define VER "9.4l" #define VER "9.4n"
#define VERNUM 9412 #define VERNUM 9414
#define VERNUM_HEX 0x9412 #define VERNUM_HEX 0x9414
#define GEN_M 0 #define GEN_M 0
#define GEN_F 1 #define GEN_F 1

444
kohonen.cpp Normal file
View File

@ -0,0 +1,444 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2017 Zeno and Tehora Rogue, see 'hyper.cpp' for details
// Kohonen's self-organizing networks.
// This is a part of RogueViz, not a part of HyperRogue.
namespace kohonen {
int cols;
typedef vector<double> kohvec;
struct sample {
kohvec val;
string name;
};
vector<sample> data;
int whattodraw[3];
struct neuron {
kohvec net;
cell *where;
double udist;
int lpbak;
int col;
int samples, csample;
};
kohvec weights;
vector<neuron> net;
void alloc(kohvec& k) { k.resize(cols); }
bool neurons_indexed = false;
int samples;
template<class T> T sqr(T x) { return x*x; }
vector<neuron*> whowon;
void normalize() {
alloc(weights);
for(int k=0; k<cols; k++) {
double sum = 0, sqsum = 0;
for(sample& s: data)
sum += s.val[k],
sqsum += s.val[k] * s.val[k];
double variance = sqsum/samples - sqr(sum/samples);
weights[k] = 1 / sqrt(variance);
}
}
double vnorm(kohvec& a, kohvec& b) {
double diff = 0;
for(int k=0; k<cols; k++) diff += sqr((a[k]-b[k]) * weights[k]);
return diff;
}
void loadsamples(const char *fname) {
normalize();
FILE *f = fopen(fname, "rt");
if(!f) return;
if(fscanf(f, "%d", &cols) != 1) { fclose(f); return; }
while(true) {
sample s;
alloc(s.val);
for(int i=0; i<cols; i++)
if(fscanf(f, "%lf", &s.val[i]) != 1) { fclose(f); return; }
if(feof(f)) break;
fgetc(f);
while(true) {
int c = fgetc(f);
if(c == -1 || c == 10 || c == 13) break;
if(c != 32 && c != 9) s.name += c;
}
data.push_back(move(s));
}
fclose(f);
samples = size(data);
normalize();
vdata.resize(samples);
for(int i=0; i<samples; i++) {
vdata[i].name = data[i].name;
vdata[i].cp = dftcolor;
createViz(i, cwt.c, Id);
}
storeall();
}
int t;
int lpct, mul, maxdist, cells, perdist;
double maxfac;
neuron& winner(int id) {
double bdiff = 1e20;
neuron *bcell = NULL;
for(neuron& n: net) {
double diff = vnorm(n.net, data[id].val);
if(diff < bdiff) bdiff = diff, bcell = &n;
}
return *bcell;
}
void setindex(bool b) {
if(b == neurons_indexed) return;
neurons_indexed = b;
if(b) {
for(neuron& n: net) n.lpbak = n.where->landparam, n.where->landparam = (&n - &net[0]);
}
else {
for(neuron& n: net) n.where->landparam = n.lpbak;
}
}
neuron *getNeuron(cell *c) {
if(!c) return NULL;
setindex(true);
if(c->landparam < 0 || c->landparam >= cells) return NULL;
neuron& ret = net[c->landparam];
if(ret.where != c) return NULL;
return &ret;
}
neuron *getNeuronSlow(cell *c) {
if(neurons_indexed) return getNeuron(c);
for(neuron& n: net) if(n.where == c) return &n;
return NULL;
}
double maxudist;
neuron *distfrom;
void coloring() {
setindex(false);
for(int pid=0; pid<3; pid++) {
int c = whattodraw[pid];
vector<double> listing;
for(neuron& n: net) switch(c) {
case -3:
if(distfrom)
listing.push_back(vnorm(n.net, distfrom->net));
else
listing.push_back(0);
break;
case -2:
listing.push_back(n.udist);
break;
case -1:
listing.push_back(-n.udist);
break;
default:
listing.push_back(n.net[c]);
break;
}
double minl = listing[0], maxl = listing[0];
for(double& d: listing) minl = min(minl, d), maxl = max(maxl, d);
if(maxl-minl < 1e-3) maxl = minl+1e-3;
for(int i=0; i<cells; i++)
part(net[i].where->landparam, pid) = (255 * (listing[i] - minl)) / (maxl - minl);
}
}
void analyze() {
setindex(true);
maxudist = 0;
for(neuron& n: net) {
int qty = 0;
double total = 0;
forCellEx(c2, n.where) {
neuron *n2 = getNeuron(c2);
if(!n2) continue;
qty++;
total += sqrt(vnorm(n.net, n2->net));
}
n.udist = total / qty;
maxudist = max(maxudist, n.udist);
}
whowon.resize(samples);
for(neuron& n: net) n.samples = 0;
for(int id=0; id<samples; id++) {
auto& w = winner(id);
whowon[id] = &w;
w.samples++;
}
for(int id=0; id<samples; id++) {
auto& w = *whowon[id];
vdata[id].m->base = w.where;
vdata[id].m->at =
spin(2*M_PI*w.csample / w.samples) * xpush(.25 * (w.samples-1) / w.samples);
w.csample++;
}
shmup::fixStorage();
setindex(false);
coloring();
}
struct cellcrawler {
struct cellcrawlerdata {
cellwalker orig;
int from, spin, dist;
cellwalker target;
cellcrawlerdata(const cellwalker& o, int fr, int sp) : orig(o), from(fr), spin(sp) {}
};
vector<cellcrawlerdata> data;
void store(const cellwalker& o, int from, int spin) {
if(eq(o.c->aitmp, sval)) return;
o.c->aitmp = sval;
data.emplace_back(o, from, spin);
}
void build(const cellwalker& start) {
sval++;
data.clear();
store(start, 0, 0);
for(int i=0; i<size(data); i++) {
cellwalker cw0 = data[i].orig;
for(int j=0; j<cw0.c->type; j++) {
cellwalker cw = cw0;
cwspin(cw, j); cwstep(cw);
if(!getNeuron(cw.c)) continue;
store(cw, i, j);
}
}
for(cellcrawlerdata& s: data)
s.dist = celldistance(s.orig.c, start.c);
}
void sprawl(const cellwalker& start) {
data[0].target = start;
for(int i=1; i<size(data); i++) {
cellcrawlerdata& s = data[i];
s.target = data[s.from].target;
if(!s.target.c) continue;
cwspin(s.target, s.spin);
if(cwstepcreates(s.target)) s.target.c = NULL;
else cwstep(s.target);
}
}
};
cellcrawler s0, s1; // hex and non-hex
void buildcellcrawler(cell *c) {
(c->type == 6 ? s0 : s1).build(cellwalker(c,0));
}
bool finished() { return t == 0; }
void step() {
double sigma = maxdist * t / (perdist*(double) mul);
if(t == 0) return;
// double sigma = maxdist * exp(-t / t1);
int pct = (int) (100 * ((t*(double) mul) / perdist));
if(pct != lpct) {
lpct = pct;
analyze();
printf("t = %6d/%2dx%6d pct = %3d sigma=%10.7lf maxudist=%10.7lf\n", t, mul, perdist, pct, sigma, maxudist);
}
int id = hrand(samples);
neuron& n = winner(id);
/*
for(neuron& n2: net) {
int d = celldistance(n.where, n2.where);
double nu = maxfac;
// nu *= exp(-t*(double)maxdist/perdist);
// nu *= exp(-t/t2);
nu *= exp(-sqr(d/sigma));
for(int k=0; k<cols; k++)
n2.net[k] += nu * (irisdata[id][k] - n2.net[k]);
} */
cellcrawler& s = n.where->type == 6 ? s0 : s1;
s.sprawl(cellwalker(n.where, 0));
for(auto& sd: s.data) {
neuron *n2 = getNeuron(sd.target.c);
if(!n2) continue;
double nu = maxfac;
nu *= exp(-sqr(sd.dist/sigma));
for(int k=0; k<cols; k++)
n2->net[k] += nu * (data[id].val[k] - n2->net[k]);
}
t--;
if(t == 0) analyze();
}
void run(const char *fname, int _perdist, double _maxfac) {
perdist = _perdist;
maxfac = _maxfac;
init(); kind = kKohonen;
loadsamples(fname);
/* if(geometry != gQuotient1) {
targetGeometry = gQuotient1;
restartGame('g');
}
if(!purehepta) restartGame('7'); */
#define Z 1
vector<cell*>& allcells = currentmap->allcells();
cells = size(allcells);
net.resize(cells);
for(int i=0; i<cells; i++) net[i].where = allcells[i], allcells[i]->landparam = i;
for(int i=0; i<cells; i++) {
net[i].where->land = laCanvas;
alloc(net[i].net);
for(int k=0; k<cols; k++)
for(int z=0; z<Z; z++)
net[i].net[k] += data[hrand(samples)].val[k] / Z;
}
for(neuron& n: net) for(int d=BARLEV; d>=7; d--) setdist(n.where, d, NULL);
cell *c1 = net[cells/2].where;
vector<int> mapdist;
for(neuron &n2: net) mapdist.push_back(celldistance(c1,n2.where));
sort(mapdist.begin(), mapdist.end());
maxdist = mapdist[size(mapdist)*5/6];
printf("samples = %d cells = %d maxdist = %d\n", samples, cells, maxdist);
c1 = currentmap->gamestart();
cell *c2 = createMov(c1, 0);
buildcellcrawler(c1);
if(c1->type != c2->type) buildcellcrawler(c2);
lpct = -46130;
mul = 1;
t = perdist*mul;
step();
for(int i=0; i<3; i++) whattodraw[i] = -2;
analyze();
}
void describe(cell *c) {
if(cmode == emHelp) return;
neuron *n = getNeuronSlow(c);
if(!n) return;
help += "cell number: " + its(n - &net[0]) + "\n";
help += "parameters:"; for(int k=0; k<cols; k++) help += " " + fts(n->net[k]);
help += ", u-matrix = " + fts(n->udist);
help += "\n";
for(int s=0; s<samples; s++) if(whowon[s] == n) {
help += "sample "+its(s)+":";
for(int k=0; k<cols; k++) help += " " + fts(data[s].val[k]);
help += " "; help += data[s].name; help += "\n";
}
}
void ksave(const char *fname) {
FILE *f = fopen(fname, "wt");
fprintf(f, "%d %d\n", cells, t);
for(neuron& n: net) {
for(int k=0; k<cols; k++) fprintf(f, "%.4lf ", n.net[k]); fprintf(f, "\n");
}
fclose(f);
}
void kload(const char *fname) {
int xcells;
FILE *f = fopen(fname, "rt");
if(!f) return;
if(fscanf(f, "%d%d\n", &xcells, &t) != 2) return;
if(xcells != cells) {
printf("Error: bad number of cells\n");
exit(1);
}
for(neuron& n: net) {
for(int k=0; k<cols; k++) if(fscanf(f, "%lf", &n.net[k]) != 1) return;
}
fclose(f);
analyze();
}
void steps() {
if(!kohonen::finished()) {
unsigned int t = SDL_GetTicks();
while(SDL_GetTicks() < t+20) kohonen::step();
setindex(false);
}
}
void showMenu() {
string parts[3] = {"red", "green", "blue"};
for(int i=0; i<3; i++) {
string c;
if(whattodraw[i] == -1) c = "u-matrix";
else if(whattodraw[i] == -2) c = "u-matrix reversed";
else if(whattodraw[i] == -3) c = "distance from marked ('m')";
else c = "column " + its(whattodraw[i]);
dialog::addSelItem(XLAT("coloring (%1)", parts[i]), c, '1'+i);
}
}
bool handleMenu(int sym, int uni) {
if(uni >= '1' && uni <= '3') {
int i = uni - '1';
whattodraw[i]++;
if(whattodraw[i] == cols) whattodraw[i] = -3;
coloring();
return true;
}
if(uni == '0') {
for(char x: {'1','2','3'}) handleMenu(x, x);
return true;
}
return false;
}
}
void mark(cell *c) {
using namespace kohonen;
distfrom = getNeuronSlow(c);
coloring();
}