This commit is contained in:
Zeno Rogue 2019-12-26 22:40:41 +01:00
commit 6c61162392
23 changed files with 465 additions and 210 deletions

View File

@ -26,7 +26,7 @@ language-data.cpp: langen
./langen > language-data.cpp
autohdr.h: makeh language-data.cpp *.cpp
./makeh classes.cpp locations.cpp hyperpoint.cpp geometry.cpp goldberg.cpp init.cpp floorshapes.cpp cell.cpp multi.cpp shmup.cpp pattern2.cpp mapeditor.cpp graph.cpp textures.cpp hprint.cpp language.cpp *.cpp > autohdr.h
./makeh classes.cpp locations.cpp hyperpoint.cpp geometry.cpp goldberg.cpp init.cpp floorshapes.cpp cell.cpp multi.cpp shmup.cpp pattern2.cpp mapeditor.cpp graph.cpp textures.cpp hprint.cpp language.cpp util.cpp complex.cpp *.cpp > autohdr.h
#############################
# Platform specific setup

View File

@ -159,7 +159,7 @@ makeh$(EXE_EXTENSION): makeh.cpp
$(CXX) makeh.cpp -o $@
autohdr.h: makeh$(EXE_EXTENSION) *.cpp
./makeh classes.cpp locations.cpp hyperpoint.cpp geometry.cpp goldberg.cpp init.cpp floorshapes.cpp cell.cpp multi.cpp shmup.cpp pattern2.cpp mapeditor.cpp graph.cpp textures.cpp hprint.cpp language.cpp complex.cpp *.cpp > autohdr.h
./makeh classes.cpp locations.cpp hyperpoint.cpp geometry.cpp goldberg.cpp init.cpp floorshapes.cpp cell.cpp multi.cpp shmup.cpp pattern2.cpp mapeditor.cpp graph.cpp textures.cpp hprint.cpp language.cpp util.cpp complex.cpp *.cpp > autohdr.h
language-data.cpp: langen$(EXE_EXTENSION)
./langen > language-data.cpp

View File

@ -216,7 +216,7 @@ EX void killIvy(cell *c, eMonster who) {
c->monst = moIvyDead; // NEWYEARFIX
for(int i=0; i<c->type; i++) if(c->move(i))
if(isIvy(c->move(i)) && c->move(i)->mondir == c->c.spin(i))
killIvy(c->move(i), who);
killIvy(c->move(i), who), kills[moIvyDead]++;
}
EX void prespill(cell* c, eWall t, int rad, cell *from) {
@ -293,14 +293,14 @@ EX void prespill(cell* c, eWall t, int rad, cell *from) {
// block spill
if(t == waTemporary) return;
// cwt.at->item = itNone;
if(rad) for(int i=0; i<c->type; i++) if(c->move(i))
prespill(c->move(i), t, rad-1, c);
if(rad) for(cell *c2: adj_minefield_cells(c))
prespill(c2, t, rad-1, c);
}
EX void spillfix(cell* c, eWall t, int rad) {
if(c->wall == waTemporary) c->wall = t;
if(rad) for(int i=0; i<c->type; i++) if(c->move(i))
spillfix(c->move(i), t, rad-1);
if(rad) for(cell *c2: adj_minefield_cells(c))
spillfix(c2, t, rad-1);
}
EX void spill(cell* c, eWall t, int rad) {
@ -391,7 +391,14 @@ EX void killMutantIvy(cell *c, eMonster who) {
removeIvy(c);
for(int i=0; i<c->type; i++)
if(c->move(i)->mondir == c->c.spin(i) && (isMutantIvy(c->move(i)) || c->move(i)->monst == moFriendlyIvy))
killMutantIvy(c->move(i), who);
kills[c->move(i)->monst]++, killMutantIvy(c->move(i), who);
if(c->land == laClearing) clearing::imput(c);
}
EX bignum ivy_total() {
return kills[moMutant] + kills[moFriendlyIvy] +
kills[moIvyRoot] + kills[moIvyHead] + kills[moIvyBranch] + kills[moIvyWait] + kills[moIvyDead]
+ clearing::imputed;
}
EX void killMonster(cell *c, eMonster who, flagtype deathflags IS(0)) {
@ -441,7 +448,7 @@ EX void killMonster(cell *c, eMonster who, flagtype deathflags IS(0)) {
if(m == moHunterGuard) m = moHunterDog;
if(m == moHunterChanging) m = moHunterDog;
if(m == moWolfMoved) m = moWolf;
if(!isBulletType(m)) kills[m]++;
if(!isBulletType(m) && m != moIvyDead) kills[m]++;
if(saved_tortoise_on(c)) c->item = itNone;
@ -462,7 +469,12 @@ EX void killMonster(cell *c, eMonster who, flagtype deathflags IS(0)) {
if(isMutantIvy(m) || m == moFriendlyIvy) {
pcount = 0;
if(isMutantIvy(m)) clearing::direct++;
bignum s = ivy_total() - 1;
killMutantIvy(c, who);
s = ivy_total() - s;
if(vid.bubbles_special && s > bignum(1))
drawBubble(c, 0xFFFF00, s.get_str(100), .5);
}
if(m == moPrincess) {
@ -680,12 +692,27 @@ EX void killMonster(cell *c, eMonster who, flagtype deathflags IS(0)) {
}
if(m == moLesser && !(kills[m] % 10))
degradeDemons();
if(m == moLesser) {
if(kills[m] % 10) {
if(vid.bubbles_special)
drawBubble(c, 0xFF0000, its(kills[m]%10), 1);
}
else {
if(vid.bubbles_special)
drawBubble(c, 0xFF8000, "+1 XP", .8);
degradeDemons();
}
}
if(isIvy(c)) {
pcount = 0;
eMonster m = c->monst;
bignum s = ivy_total() - 1;
/*if((m == moIvyBranch || m == moIvyHead) && c->move(c->mondir)->monst == moIvyRoot)
ivynext(c, moIvyNext); */
killIvy(c, who);
s = ivy_total() - s;
if(s > bignum(1) && vid.bubbles_special)
drawBubble(c, 0xFFFF00, s.get_str(100), .5);
if(m == moIvyBranch || m == moIvyHead || m == moIvyNext) {
int qty = 0;
cell *c2 = c->move(c->mondir);

View File

@ -1782,7 +1782,7 @@ EX void moreBigStuff(cell *c) {
c->land = laClearing, c->wall = waNone; // , c->monst = moNone, c->item = itNone;
}
else if(d == 1 && !tactic::on && !eubinary)
c->wall = waSmallTree, c->monst = moNone, c->item = itNone;
c->wall = waSmallTree, c->monst = moNone, c->item = itNone, c->landparam = 1;
}
}

View File

@ -2242,9 +2242,20 @@ void celldrawer::add_map_effects() {
int tim = ticks - lightat;
if(tim > 1000) tim = 800;
if(elec::havecharge && tim > 400) tim = 400;
for(int t=0; t<c->type; t++) if(c->move(t) && c->move(t)->ligon) {
int lcol = darkena(gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100), 0, 0xFF);
queueline(V*chei(xspinpush(ticks * M_PI / cgi.S42, cgi.hexf/2), rand() % 1000, 1000) * C0, V*chei(currentmap->adj(c, t), rand() % 1000, 1000) * C0, lcol, 2 + vid.linequality);
for(int t=0; t<c->type; t++) if(c->move(t)) {
if(c->move(t)->ligon) {
int lcol = darkena(gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100), 0, 0xFF);
queueline(V*chei(xspinpush(ticks * M_PI / cgi.S42, cgi.hexf/2), rand() % 1000, 1000) * C0, V*chei(currentmap->adj(c, t), rand() % 1000, 1000) * C0, lcol, 2 + vid.linequality);
}
for(int u: {-1, 1}) {
cellwalker cw = cellwalker(c, t) + wstep + u;
if(u == -1 && VALENCE == 4) continue;
cell *c2 = cw.peek();
if(c2 && c2->ligon) {
int lcol = darkena(gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100), 0, 0xFF);
queueline(V*chei(xspinpush(ticks * M_PI / cgi.S42, cgi.hexf/2), rand() % 1000, 1000) * C0, V*chei(currentmap->adj(c, t)*currentmap->adj(cw.at, cw.spin), rand() % 1000, 1000) * C0, lcol, 2 + vid.linequality);
}
}
}
}

View File

@ -722,7 +722,7 @@ enum eGeometry {
gBinary4, gSol,
gKiteDart2, gKiteDart3, gNil, gProduct, gRotSpace,
gTernary, gNIH, gSolN, gInfOrder, gSpace336, gSpace344, gCrystal344,
gArnoldCat, gArbitrary,
gArnoldCat, gArbitrary, gInfOrder4,
gGUARD};
enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSolNIH, gcNil, gcProduct, gcSL2 };
@ -887,6 +887,7 @@ EX vector<geometryinfo> ginf = {
{"{3,4,4}","Crystal", "4D crystal in H3", "Cryst3" , 8, 4, qIDEAL | qANYQ | qCRYSTAL, giHyperb3, 0x52000, {{7, 3}}, eVariation::pure},
{"cat", "cat", "Arnold's cat mapping torus", "cat", 12, 3, qBINARY | qSOL | qsBQ | qOPTQ, giSolNIH, 0x52200, {{6, 4}}, eVariation::pure},
{"arb", "arb", "arbitrary", "arb", 7, 3, qEXPERIMENTAL, giEuclid2, 0, {{7, 5}}, eVariation::pure},
{"{4,oo}", "none", "{4,∞} (infinite squares)", "oox4", 4, 100, qIDEAL, giHyperb2, 0x49400, {{5, 5}}, eVariation::pure},
};
// bits: 9, 10, 15, 16, (reserved for later) 17, 18

View File

@ -306,9 +306,7 @@ EX namespace elec {
if(from != 1) charges[id].lowlink = 1;
}
for(int i=0; i<c->type; i++) {
cell *c2 = c->move(i);
if(!c2) continue;
for(cell *c2: adj_minefield_cells(c)) {
if(c2->listindex == from) continue;
eCharge ct = getCharge(c2);
if(conduct(chh, ct))
@ -956,6 +954,46 @@ EX namespace clearing {
steps--; ds++;
}
}
typedef tuple<int, int, int, int> celltype;
map<celltype, pair<bignum, int> > stats;
EX bignum imputed;
EX int direct;
map<cell*, pair<bignum, int> > score;
celltype get_celltype(cell *c) {
cell *c1 = c;
if(c->mondir < c->type)
c1 = c->move(c->mondir);
return make_tuple(
celldistAlt(c), type_in(expansion, c, celldistAlt),
celldistAlt(c1), type_in(expansion, c1, celldistAlt)
);
}
EX void imput(cell *c) {
if(bounded) return;
if(score.count(c)) return;
auto& is = score[c];
celltype t = get_celltype(c);
auto& stat = stats[t];
is.second = c->mondir;
if(c->mpdist <= 6) {
is.first = 1;
forCellEx(c2, c) if(score.count(c2) && c2->move(score[c2].second) == c)
is.first += score[c2].first;
stat.first += is.first;
stat.second++;
}
else {
is.first = stat.second ? stat.first.randomized_div(stat.second) : bignum(1);
imputed += is.first;
}
}
EX }
EX namespace whirlpool {
@ -2047,14 +2085,16 @@ EX namespace heat {
if(isFire(c)) hmod += 4 * xrate;
if(isPrincess(c->monst)) hmod += (markEmpathy(itOrbWinter) ? -1.2 : 1.2) * xrate;
forCellEx(ct, c) {
auto ls = adj_minefield_cells(c);
for(cell* ct: ls) {
if(!isIcyLand(ct) && isFire(ct))
hmod += xrate*.1;
if(ct->land == laVolcano)
hmod += xrate * (ct->wall == waMagma ? .4 : .2);
}
forCellEx(ct, c) {
for(cell* ct: ls) {
if(!isIcyLand(ct)) {
// make sure that we can still enter Cocytus,
// it won't heat up right away even without Orb of Winter or Orb of Speed
@ -2139,7 +2179,8 @@ EX namespace heat {
cell *last = c->move(c->type-1);
forCellEx(c2, c) {
auto ls = adj_minefield_cells(c);
for(cell* c2: ls) {
if(c->wall == waPartialFire) {
// two partial fires adjacent are necessary to spread
@ -2260,7 +2301,7 @@ EX void livecaves() {
hv = 0;
if(c->monst == moDarkTroll) c->monst = moTroll;
if(c->item || c->monst || c->cpdist == 0) continue;
forCellEx(c2, c) {
for(cell *c2: adj_minefield_cells(c)) {
eWall w = c2->wall;
if(w == waDeadfloor) hv++, bringlife.push_back(c2);
else if(w == waDeadwall || (w == waDeadfloor2 && !c2->monst))

View File

@ -618,6 +618,10 @@ EX void initConfig() {
addsaver(s2xe::qrings, "s2xe-rings");
addsaver(rots::underlying_scale, "rots-underlying-scale");
addsaver(vid.bubbles_special, "bubbles-special", 1);
addsaver(vid.bubbles_threshold, "bubbles-special", 1);
addsaver(vid.bubbles_all, "bubbles-special", 0);
#if CAP_SHMUP
multi::initConfig();
@ -996,6 +1000,22 @@ EX void menuitem_sightrange(char c IS('c')) {
dialog::add_action(edit_sightrange);
}
EX void showSpecialEffects() {
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
gamescreen(0);
dialog::init(XLAT("extra graphical effects"));
dialog::addBoolItem_action(XLAT("particles on attack"), (vid.particles), 'p');
dialog::addBoolItem_action(XLAT("floating bubbles: special"), vid.bubbles_special, 's');
dialog::addBoolItem_action(XLAT("floating bubbles: treasure thresholds"), vid.bubbles_threshold, 't');
dialog::addBoolItem_action(XLAT("floating bubbles: all treasures"), vid.bubbles_all, 'a');
dialog::addBoolItem_action(XLAT("background particle effects"), (vid.backeffects), 'b');
dialog::addBreak(50);
dialog::addBack();
dialog::display();
}
EX void showGraphConfig() {
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
gamescreen(0);
@ -1039,8 +1059,7 @@ EX void showGraphConfig() {
dialog::addSelItem(XLAT("movement animation speed"), fts(vid.mspeed), 'm');
dialog::addBoolItem(XLAT("extra graphical effects"), (vid.particles), 'u');
dialog::addBoolItem(XLAT("background particle effects"), (vid.backeffects), 'p');
dialog::addItem(XLAT("extra graphical effects"), 'u');
dialog::addBreak(50);
dialog::addBack();
@ -1055,7 +1074,7 @@ EX void showGraphConfig() {
if((uni >= 32 && uni < 64) || uni == 'L' || uni == 'C') xuni = uni;
if(xuni == 'u') vid.particles = !vid.particles;
if(xuni == 'u') pushScreen(showSpecialEffects);
else if(xuni == 'a') dialog::editNumber(vid.sspeed, -5, 5, 1, 0,
XLAT("scrolling speed"),

View File

@ -1027,7 +1027,23 @@ EX bool gmodekeys(int sym, int uni) {
if(NUMBERKEY == '7') { vid.darkhepta = !vid.darkhepta; return true; }
if(GDIM == 2) {
if(NUMBERKEY == '1' && !rug::rugged) { vid.alpha = 999; vid.scale = 998; vid.xposition = vid.yposition = 0; }
if(among(NUMBERKEY, '1', '2', '3') && !rug::rugged && euclid && WDIM == 2) {
vid.xposition = vid.yposition = 0;
ld maxs = 0;
auto& cd = current_display;
for(auto& p: gmatrix) for(int i=0; i<p.first->type; i++) {
hyperpoint h = tC0(p.second * currentmap->adj(p.first, i));
hyperpoint onscreen;
applymodel(h, onscreen);
maxs = max(maxs, onscreen[0] / cd->xsize);
maxs = max(maxs, onscreen[1] / cd->ysize);
}
vid.alpha = 1;
vid.scale = vid.scale / 2 / maxs / cd->radius;
if(NUMBERKEY == '3') vid.scale *= 2;
if(NUMBERKEY == '1') vid.scale /= 2;
}
else if(NUMBERKEY == '1' && !rug::rugged) { vid.alpha = 999; vid.scale = 998; vid.xposition = vid.yposition = 0; }
else if(NUMBERKEY == '2' && !rug::rugged) { vid.alpha = 1; vid.scale = 0.4; vid.xposition = vid.yposition = 0; }
else if(NUMBERKEY == '3' && !rug::rugged) { vid.alpha = 1; vid.scale = 1; vid.xposition = vid.yposition = 0; }
else if(NUMBERKEY == '4' && !rug::rugged) { vid.alpha = 0; vid.scale = 1; vid.xposition = vid.yposition = 0; }

View File

@ -348,6 +348,7 @@ EX void bfs() {
if(c2->land == laWhirlwind) havewhat |= HF_WHIRLWIND;
if(c2->land == laWestWall) havewhat |= HF_WESTWALL;
if(c2->land == laPrairie) havewhat |= HF_RIVER;
if(c2->land == laClearing) havewhat |= HF_MUTANT;
if(c2->wall == waRose) havewhat |= HF_ROSE;

View File

@ -15,164 +15,6 @@ int subtype(cell *c) {
return patterns::getpatterninfo(c, patterns::PAT_NONE, 0).id;
}
#if HDR
struct bignum {
static const int BASE = 1000000000;
static const long long BASE2 = BASE * (long long)BASE;
vector<int> digits;
bignum() {}
bignum(int i) : digits() { digits.push_back(i); }
void be(int i) { digits.resize(1); digits[0] = i; }
bignum& operator +=(const bignum& b);
void addmul(const bignum& b, int factor);
string get_str(int max_length);
bool operator < (const bignum&) const;
ld leading() const {
switch(isize(digits)) {
case 0:
return 0;
case 1:
return digits.back();
default:
return digits.back() + ld(digits[isize(digits)-2]) / BASE;
}
}
ld approx() const {
return leading() * pow(BASE, isize(digits) - 1);
}
ld log_approx() const {
return log(leading()) * log(BASE) * (isize(digits) - 1);
}
ld operator / (const bignum& b) const {
return leading() / b.leading() * pow(BASE, isize(digits) - isize(b.digits));
}
int approx_int() const {
if(isize(digits) > 1) return BASE;
if(digits.empty()) return 0;
return digits[0];
}
long long approx_ll() const {
if(isize(digits) > 2) return BASE2;
if(digits.empty()) return 0;
if(isize(digits) == 1) return digits[0];
return digits[0] + digits[1] * (long long) BASE;
}
friend inline bignum operator +(bignum a, const bignum& b) { a.addmul(b, 1); return a; }
friend inline bignum operator -(bignum a, const bignum& b) { a.addmul(b, -1); return a; }
};
#endif
bignum& bignum::operator +=(const bignum& b) {
int K = isize(b.digits);
if(K > isize(digits)) digits.resize(K);
int carry = 0;
for(int i=0; i<K || carry; i++) {
if(i >= isize(digits)) digits.push_back(0);
digits[i] += carry;
if(i < K) digits[i] += b.digits[i];
if(digits[i] >= BASE) {
digits[i] -= BASE;
carry = 1;
}
else carry = 0;
}
return *this;
}
bool bignum::operator < (const bignum& b) const {
if(isize(digits) != isize(b.digits))
return isize(digits) < isize(b.digits);
for(int i = isize(digits)-1; i>=0; i--)
if(digits[i] != b.digits[i])
return digits[i] < b.digits[i];
return false;
}
void bignum::addmul(const bignum& b, int factor) {
int K = isize(b.digits);
if(K > isize(digits)) digits.resize(K);
int carry = 0;
for(int i=0; i<K || (carry > 0 || carry < -1) || (carry == -1 && i < isize(digits)); i++) {
if(i >= isize(digits)) digits.push_back(0);
long long l = digits[i];
l += carry;
if(i < K) l += b.digits[i] * factor;
carry = 0;
if(l >= BASE) carry = l / BASE;
if(l < 0) carry = -(BASE-1-l) / BASE;
l -= carry * BASE;
digits[i] = l;
}
if(carry < 0) digits.back() -= BASE;
while(isize(digits) && digits.back() == 0) digits.pop_back();
}
EX bignum hrand(bignum b) {
bignum res;
int d = isize(b.digits);
while(true) {
res.digits.resize(d);
for(int i=0; i<d-1; i++) res.digits[i] = hrand(bignum::BASE);
res.digits.back() = hrand(b.digits.back() + 1);
if(res < b) return res;
}
}
EX void operator ++(bignum &b, int) {
int i = 0;
while(true) {
if(isize(b.digits) == i) { b.digits.push_back(1); break; }
else if(b.digits[i] == bignum::BASE-1) {
b.digits[i] = 0;
i++;
}
else {
b.digits[i]++;
break;
}
}
}
EX void operator --(bignum &b, int) {
int i = 0;
while(true) {
if(isize(b.digits) == i) { b.digits.push_back(bignum::BASE-1); break; }
else if(b.digits[i] == 0) {
b.digits[i] = bignum::BASE-1;
i++;
}
else {
b.digits[i]--;
break;
}
}
}
string bignum::get_str(int max_length) {
if(digits.empty()) return "0";
string ret = its(digits.back());
for(int i=isize(digits)-2; i>=0; i--) {
if(isize(ret) > max_length && i) {
ret += XLAT(" (%1 more digits)", its(9 * (i+1)));
return ret;
}
ret += " ";
string val = its(digits[i]);
while(isize(val) < 9) val = "0" + val;
ret += val;
}
return ret;
}
void canonicize(vector<int>& t) {
for(int i=2; i<isize(t); i++)
if((t[i] & 3) == 1 && (t[i-1] & 3) != 1)

View File

@ -751,12 +751,13 @@ EX void showEuclideanMenu() {
});
}
if(among(specialland, laMinefield, laCA) && geometry_has_alt_mine_rule()) {
dialog::addSelItem(XLAT("mine adjacency rule"), XLAT(mine_adjacency_rule ? "vertex" : WDIM == 3 ? "face" : "edge"), 'M');
if(geometry_has_alt_mine_rule()) {
dialog::addSelItem(XLAT("adjacency rule"), XLAT(mine_adjacency_rule ? "vertex" : WDIM == 3 ? "face" : "edge"), 'M');
dialog::add_action([] {
stop_game();
mine_adjacency_rule = !mine_adjacency_rule;
start_game();
addMessage(XLAT("Note: adjacency rule affects environmental effects, but not movement."));
});
}

View File

@ -3762,6 +3762,7 @@ struct flashdata {
double angle2;
int spd; // 0 for flashes, >0 for particles
color_t color;
string text;
flashdata(int _t, int _s, cell *_w, color_t col, int sped) {
t=_t; size=_s; where=_w; color = col;
angle = rand() % 1000; spd = sped;
@ -3771,6 +3772,13 @@ struct flashdata {
vector<flashdata> flashes;
EX void drawBubble(cell *c, color_t col, string s, ld size) {
auto fd = flashdata(ticks, 1000, c, col, 0);
fd.text = s;
fd.angle = size;
flashes.push_back(fd);
}
EX void drawFlash(cell *c) {
flashes.push_back(flashdata(ticks, 1000, c, iinf[itOrbFlash].color, 0));
}
@ -3990,8 +3998,8 @@ EX void drawMarkers() {
queuecircleat(lmouseover, .8, darkena(lmouseover->cpdist > 1 ? 0x00FFFF : 0xFF0000, 0, 0xFF));
}
if(pcm.mip.t && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && WDIM == 2) {
queuecircleat(pcm.mip.t, .6, darkena(0xFFD500, 0, 0xFF));
if(global_pushto && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && WDIM == 2) {
queuecircleat(global_pushto, .6, darkena(0xFFD500, 0, 0xFF));
}
#endif
@ -4137,8 +4145,20 @@ EX void draw_flash(struct flashdata& f, const transmatrix& V, bool& kill) {
int tim = ticks - f.t;
if(tim <= f.size && !f.spd) kill = false;
if(f.spd) {
if(f.text != "") {
if(GDIM == 3 || sphere)
queuestr(V, (1 - tim * 1. / f.size) * f.angle, f.text, f.color);
else if(!kill) {
hyperpoint h = tC0(V);
if(hdist0(h) > .1) {
transmatrix V2 = rspintox(h) * xpush(hdist0(h) * (1 / (1 - tim * 1. / f.size)));
queuestr(V2, f.angle, f.text, f.color);
}
}
}
else if(f.spd) {
#if CAP_SHAPES
if(tim <= 300) kill = false;
int partcol = darkena(f.color, 0, GDIM == 3 ? 255 : max(255 - tim*255/300, 0));
@ -4786,7 +4806,7 @@ EX bool nohelp;
EX void normalscreen() {
help = "@";
mouseovers = XLAT("Press F1 or right click for help");
mouseovers = standard_help();
#if CAP_TOUR
if(tour::on) mouseovers = tour::tourhelp;

View File

@ -178,6 +178,10 @@ void buildHelpText() {
#endif
}
EX string standard_help() {
return XLAT("Press F1 or right click for help");
}
EX void buildCredits() {
help = "";
help += XLAT("game design, programming, texts and graphics by Zeno Rogue <zeno@attnam.com>\n\n");
@ -576,6 +580,16 @@ EX string generateHelpForMonster(eMonster m) {
if(isGhost(m))
s += XLAT("\n\nA Ghost never moves to a cell which is adjacent to another Ghost of the same kind.", m);
if(m == moMutant) {
using namespace clearing;
if(direct)
s += XLAT("\n\nLeaves cut directly: %1", its(direct));
if(kills[moMutant])
s += XLAT("\n\nLeaves cut onscreen: %1", its(kills[moMutant]));
if(imputed.nonzero())
s += XLAT("\n\nLeaves cut offscreen (approximately): %1", imputed.get_str(10000));
}
if(m == moBat || m == moEagle)
s += XLAT("\n\nFast flying creatures may attack or go against gravity only in their first move.", m);

59
hud.cpp
View File

@ -253,8 +253,25 @@ bool displayglyph(int cx, int cy, int buttonsize, char glyph, color_t color, int
(qty < 10 && (flags & (GLYPH_MARKTODO | GLYPH_RUNOUT))) ? buttonsize*3/4 :
qty < 100 ? buttonsize / 2 :
buttonsize / 3;
if(str != "")
displayfr(cx + buttonsize, cy + buttonsize/2 - bsize/2, 1, bsize, str, color, 16);
if(id == moMutant + ittypes && clearing::imputed.nonzero()) {
ld d = qty + clearing::imputed.approx_ld();
if(d < 100000) str = its(int(d));
else {
int digits = 0;
while(d >= 10) digits++, d /= 10;
str = its(int(d*100)) + "E" + its(digits);
str.insert(1, ".");
}
bsize = buttonsize / 4;
}
if(str != "") {
if(textwidth(bsize, str) < buttonsize)
displayfr(cx + buttonsize, cy + buttonsize/2 - bsize/2, 1, bsize, str, color, 16);
else
displayfr(cx, cy + buttonsize/2 - bsize/2, 1, bsize, str, color, 0);
}
return b;
}
@ -365,6 +382,8 @@ EX bool nofps = false;
EX color_t crosshair_color = 0xFFFFFFC0;
EX ld crosshair_size = 0;
EX bool long_kills;
EX void drawStats() {
if(nohud || vid.stereo_mode == sLR) return;
if(callhandlers(false, hooks_prestats)) return;
@ -577,7 +596,8 @@ EX void drawStats() {
#endif
}
else if(!peace::on) {
if(displayButtonS(vid.xres - 8, vid.fsize, XLAT("score: %1", its(gold())), forecolor, 16, vid.fsize)) {
string scoreline = XLAT("score: %1", its(gold()));
if(displayButtonS(vid.xres - 8, vid.fsize, scoreline, forecolor, 16, vid.fsize)) {
mouseovers = XLAT("Your total wealth"),
instat = true,
getcstat = SDLK_F1,
@ -590,16 +610,29 @@ EX void drawStats() {
"Orbs of Yendor are worth 50 $$$ each.\n\n"
);
}
if(displayButtonS(8, vid.fsize, XLAT("kills: %1", its(tkills())), forecolor, 0, vid.fsize)) {
instat = true,
getcstat = SDLK_F1,
mouseovers = XLAT("Your total kills")+": " + its(tkills()),
help = helptitle(XLAT("Your total kills") + ": " + its(tkills()), 0x404040) +
XLAT(
"In most lands, more treasures are generated with each enemy native to this land you kill. "
"Moreover, 100 kills is a requirement to enter the Graveyard and the Hive.\n\n"
"Friendly creatures and parts of monsters (such as the Ivy) do appear in the list, "
"but are not counted in the total kill count.");
string s = XLAT("kills: %1", its(tkills()));
long_kills = false;
int siz = vid.fsize;
if(cwt.at->land == laClearing && clearing::imputed.approx_ld() >= 100000) {
long_kills = true;
s = XLAT("leaves cut: " + (bignum(kills[moMutant]) + clearing::imputed).get_str(200));
if(mouseovers == standard_help()) mouseovers = " ";
while(siz > 4 && textwidth(siz, s) > vid.xres - textwidth(vid.fsize, scoreline)) siz--;
}
if(displayButtonS(8, vid.fsize, s, forecolor, 0, siz)) {
instat = true;
getcstat = SDLK_F1;
if(long_kills) { mouseovers = " "; help = generateHelpForMonster(moMutant); }
else {
mouseovers = XLAT("Your total kills")+": " + its(tkills()),
help = helptitle(XLAT("Your total kills") + ": " + its(tkills()), 0x404040) +
XLAT(
"In most lands, more treasures are generated with each enemy native to this land you kill. "
"Moreover, 100 kills is a requirement to enter the Graveyard and the Hive.\n\n"
"Friendly creatures and parts of monsters (such as the Ivy) do appear in the list, "
"but are not counted in the total kill count.");
}
}
}
string vers = VER;

View File

@ -246,7 +246,7 @@ struct videopar {
ld xposition, yposition;
bool grid;
int particles;
bool particles;
int fsize;
int flashtime;
@ -325,6 +325,7 @@ struct videopar {
ld collignon_parameter; bool collignon_reflected;
ld plevel_factor;
bool bubbles_special, bubbles_threshold, bubbles_all;
};
extern videopar vid;

View File

@ -1157,7 +1157,9 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
(hrand(50+items[itMutant]/2+yendor::hardness()) < 30) ? (hrand(100) < 50 ? waBigTree : waSmallTree) : waNone;
}
if(d == 8) {
if(hrand(doCross ?450:15000) < 20 + (2 * items[itMutant] + yendor::hardness()) && !safety) {
bool ok = c->landparam == 0;
forCellEx(c2, c) if(c2->landparam) ok = false;
if(ok && hrand(doCross ?450:15000) < 20 + (2 * items[itMutant] + yendor::hardness()) && !safety) {
if(!peace::on) c->item = itMutant;
c->landparam = items[itMutant] + 5 + hrand(11);
c->wall = waNone;
@ -1165,7 +1167,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
if(c->move(i) && (c->move(i)->wall == waBigTree || c->move(i)->wall == waSmallTree))
c->move(i)->wall = waNone;
}
else if(hrand_monster(15000) < 20 + (2 * items[itMutant] + yendor::hardness()) && !safety) {
else if(hrand_monster(15000) < 20 + (2 * items[itMutant] + yendor::hardness()) && ok && !safety) {
// for the Yendor Challenge, use only Mutants
if(!(yendor::on && yendor::clev().l == laMirror)) {
c->monst = moForestTroll;

View File

@ -38,6 +38,7 @@ EX int buildIvy(cell *c, int children, int minleaf) {
leaf += leafchild;
if(leaf < minleaf) {
if(child) killIvy(child, moNone);
dynamicval<int> k(kills[moIvyDead]);
killIvy(c, moNone);
return 0;
}

View File

@ -1333,8 +1333,18 @@ EX void movehex_rest(bool mounted) {
}
EX void movemutant() {
manual_celllister mcells;
for(cell *c: currentmap->allcells()) mcells.add(c);
if(!bounded)
for(int i=0; i<isize(mcells.lst); i++) {
cell *c = mcells.lst[i];
if(c->land == laClearing && c->monst != moMutant && !pseudohept(c))
forCellEx(c2, c) forCellEx(c3, c2) if(celldistAlt(c3) < celldistAlt(c))
mcells.add(c3);
}
vector<cell*> young;
for(cell *c: currentmap->allcells())
for(cell *c: mcells.lst)
if(c->monst == moMutant && c->stuntime == mutantphase)
young.push_back(c);

View File

@ -958,6 +958,11 @@ EX void handleInput(int delta) {
cdir = d;
if(multi::multiPlayerTarget(i) == c) break;
cdir = scdir;
cwt = multi::player[i];
calcMousedest();
auto& sd = multi::whereto[i].subdir;
sd = mousedest.subdir;
if(sd == 0) sd = 1;
}
}
}

View File

@ -224,12 +224,15 @@ struct pcmove {
};
#endif
EX pcmove pcm;
EX cell *global_pushto;
EX bool movepcto(int d, int subdir IS(1), bool checkonly IS(false)) {
pcmove pcm;
pcm.checkonly = checkonly;
pcm.d = d; pcm.subdir = subdir;
return pcm.movepcto();
auto b = pcm.movepcto();
global_pushto = pcm.mip.t;
return b;
}
bool pcmove::movepcto() {

View File

@ -394,7 +394,7 @@ bool havesave = true;
#if HDR
#define MAXBOX 500
#define POSSCORE 371 // update this when new boxes are added!
#define POSSCORE 373 // update this when new boxes are added!
struct score {
string ver;
int box[MAXBOX];
@ -414,6 +414,16 @@ void applyBox(int& t) {
else boxid++;
}
void applyBoxBignum(bignum& tb) {
float tf;
int ti;
if(saving) tf = tb.approx_ld();
if(saving) memcpy(&ti, &tf, 4);
applyBox(ti);
if(loading) memcpy(&tf, &ti, 4);
if(loading) tb = bignum(tf);
}
EX void applyBoxNum(int& i, string name IS("")) {
fakebox[boxid] = (name == "");
boxname[boxid] = name;
@ -814,6 +824,9 @@ EX void applyBoxes() {
applyBoxM(moNarciss);
applyBoxM(moMirrorSpirit);
applyBox(clearing::direct);
applyBoxBignum(clearing::imputed);
if(POSSCORE != boxid) printf("ERROR: %d boxes\n", boxid);
}

194
util.cpp
View File

@ -325,4 +325,198 @@ EX string parser_help() {
"(a)sin(h), (a)cos(h), (a)tan(h), exp, log, abs, re, im, conj, let(t=...,...t...), floor, frac, e, i, pi, s, ms, mousex, mousey, mousez, shot [1 if taking screenshot/animation], sqrt, to01, random, edge(7,3), regradius(7,3), ifp(a,v,w) [if positive]");
}
#if HDR
struct bignum {
static const int BASE = 1000000000;
static const long long BASE2 = BASE * (long long)BASE;
vector<int> digits;
bignum() {}
bignum(int i) : digits() { digits.push_back(i); }
void be(int i) { digits.resize(1); digits[0] = i; }
bignum& operator +=(const bignum& b);
void addmul(const bignum& b, int factor);
string get_str(int max_length) const;
bignum(ld d);
bool operator < (const bignum&) const;
bool operator > (const bignum& b) const { return b < self; }
ld leading() const {
switch(isize(digits)) {
case 0:
return 0;
case 1:
return digits.back();
default:
return digits.back() + ld(digits[isize(digits)-2]) / BASE;
}
}
ld approx() const {
return leading() * pow(BASE, isize(digits) - 1);
}
ld log_approx() const {
return log(leading()) * log(BASE) * (isize(digits) - 1);
}
ld approx_div(const bignum& b) const {
return leading() / b.leading() * pow(BASE, isize(digits) - isize(b.digits));
}
int approx_int() const {
if(isize(digits) > 1) return BASE;
if(digits.empty()) return 0;
return digits[0];
}
bool nonzero() { return approx_ld() != 0; }
bignum randomized_div(int x) const;
ld approx_ld() const {
ld res = 0;
for(int i=0; i<isize(digits); i++) res += digits[i] * pow(BASE, i);
return res;
}
long long approx_ll() const {
if(isize(digits) > 2) return BASE2;
if(digits.empty()) return 0;
if(isize(digits) == 1) return digits[0];
return digits[0] + digits[1] * (long long) BASE;
}
friend inline bignum operator +(bignum a, const bignum& b) { a.addmul(b, 1); return a; }
friend inline bignum operator -(bignum a, const bignum& b) { a.addmul(b, -1); return a; }
};
#endif
bignum& bignum::operator +=(const bignum& b) {
int K = isize(b.digits);
if(K > isize(digits)) digits.resize(K);
int carry = 0;
for(int i=0; i<K || carry; i++) {
if(i >= isize(digits)) digits.push_back(0);
digits[i] += carry;
if(i < K) digits[i] += b.digits[i];
if(digits[i] >= BASE) {
digits[i] -= BASE;
carry = 1;
}
else carry = 0;
}
return *this;
}
bool bignum::operator < (const bignum& b) const {
if(isize(digits) != isize(b.digits))
return isize(digits) < isize(b.digits);
for(int i = isize(digits)-1; i>=0; i--)
if(digits[i] != b.digits[i])
return digits[i] < b.digits[i];
return false;
}
bignum bignum::randomized_div(int x) const {
bignum res = self;
long long carry = 0;
int K = isize(res.digits);
for(int i=K-1; i>=0; i--) {
carry *= BASE;
carry += digits[i];
tie(carry, res.digits[i]) = make_pair(carry % x, carry / x);
}
while(isize(res.digits) && res.digits.back() == 0) res.digits.pop_back();
if(rand() % x < carry) res += 1;
println(hlog, get_str(100), " / ", x, " = ", res.get_str(100));
return res;
}
void bignum::addmul(const bignum& b, int factor) {
int K = isize(b.digits);
if(K > isize(digits)) digits.resize(K);
int carry = 0;
for(int i=0; i<K || (carry > 0 || carry < -1) || (carry == -1 && i < isize(digits)); i++) {
if(i >= isize(digits)) digits.push_back(0);
long long l = digits[i];
l += carry;
if(i < K) l += b.digits[i] * factor;
carry = 0;
if(l >= BASE) carry = l / BASE;
if(l < 0) carry = -(BASE-1-l) / BASE;
l -= carry * BASE;
digits[i] = l;
}
if(carry < 0) digits.back() -= BASE;
while(isize(digits) && digits.back() == 0) digits.pop_back();
}
EX bignum hrand(bignum b) {
bignum res;
int d = isize(b.digits);
while(true) {
res.digits.resize(d);
for(int i=0; i<d-1; i++) res.digits[i] = hrand(bignum::BASE);
res.digits.back() = hrand(b.digits.back() + 1);
if(res < b) return res;
}
}
EX void operator ++(bignum &b, int) {
int i = 0;
while(true) {
if(isize(b.digits) == i) { b.digits.push_back(1); break; }
else if(b.digits[i] == bignum::BASE-1) {
b.digits[i] = 0;
i++;
}
else {
b.digits[i]++;
break;
}
}
}
EX void operator --(bignum &b, int) {
int i = 0;
while(true) {
if(isize(b.digits) == i) { b.digits.push_back(bignum::BASE-1); break; }
else if(b.digits[i] == 0) {
b.digits[i] = bignum::BASE-1;
i++;
}
else {
b.digits[i]--;
break;
}
}
}
string bignum::get_str(int max_length) const {
if(digits.empty()) return "0";
string ret = its(digits.back());
for(int i=isize(digits)-2; i>=0; i--) {
if(isize(ret) > max_length && i) {
ret += XLAT(" (%1 more digits)", its(9 * (i+1)));
return ret;
}
ret += " ";
string val = its(digits[i]);
while(isize(val) < 9) val = "0" + val;
ret += val;
}
return ret;
}
bignum::bignum(ld d) {
if(d == 0) return;
int n = 1;
while(d > BASE) d /= BASE, n++;
digits.resize(n);
n--;
while(n >= 0) { digits[n] = int(d); d -= digits[n]; d *= BASE; n--; }
}
}