diff --git a/complex2.cpp b/complex2.cpp index 9ca6c34d..26153b30 100644 --- a/complex2.cpp +++ b/complex2.cpp @@ -1051,7 +1051,7 @@ EX namespace dice { c->wall = (pct < (items[itOrbLuck] ? 9 : 11)) ? waRichDie : waHappyDie; generate_specific(c, &d6, 1, 2); } - if(pct2 < 40 + hard) { + else if(pct2 < 40 + hard) { c->monst = moAnimatedDie; generate_specific(c, &d6, 0, 99); } @@ -1062,7 +1062,7 @@ EX namespace dice { c->wall = (pct < (items[itOrbLuck] ? 9 : 11)) ? waRichDie : waHappyDie; generate_specific(c, &d12, 2, 3); } - if(pct2 < 40 + hard) { + else if(pct2 < 40 + hard) { c->monst = moAnimatedDie; generate_specific(c, &d12, 0, 99); } @@ -1153,6 +1153,12 @@ EX namespace dice { return isDie(c->wall) || isDie(c->monst); } + EX string describe(cell *c) { + if (!data.count(c)) return "BUG: die data missing"; + else if (!data[c].which) return "BUG: die data default-initialized"; + else return XLAT("d%1 rolled %2", its(data[c].which->faces), its(data[c].val + 1)); + } + EX void roll(movei mi) { auto &cto = mi.t; auto &th = mi.s; diff --git a/graph.cpp b/graph.cpp index d0d601a2..a3cad9f8 100644 --- a/graph.cpp +++ b/graph.cpp @@ -45,7 +45,23 @@ EX bool hide_player() { ; } -#define ADC(V,c) IF_KEY_EXISTS(it, current_display->all_drawn_copies, c) for(const shiftmatrix& V: it->second) +template +class span { + T *begin_ = nullptr; + T *end_ = nullptr; + + public: + explicit span() = default; + explicit span(T *p, int n) : begin_(p), end_(p + n) {} + T *begin() const { return begin_; } + T *end() const { return end_; } + }; + +template +hr::span span_at(const Map& map, const Key& key) { + auto it = map.find(key); + return (it == map.end()) ? hr::span() : hr::span(it->second.data(), it->second.size()); + } EX hookset hooks_handleKey; EX hookset hooks_drawcell; @@ -4361,7 +4377,8 @@ EX void queuecircleat1(cell *c, const shiftmatrix& V, double rad, color_t col) { EX void queuecircleat(cell *c, double rad, color_t col) { if(!c) return; - ADC(V, c) queuecircleat1(c, V, rad, col); + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, c)) + queuecircleat1(c, V, rad, col); } #endif @@ -4418,10 +4435,11 @@ EX void drawMarkers() { ignore(ok); #if CAP_QUEUE - if(haveMount()) ADC(V, dragon::target) { - queuestr(V, 1, "X", - gradient(0, iinf[itOrbDomination].color, -1, sintick(dragon::whichturn == turncount ? 75 : 150), 1)); - } + if(haveMount()) + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, dragon::target)) { + queuestr(V, 1, "X", + gradient(0, iinf[itOrbDomination].color, -1, sintick(dragon::whichturn == turncount ? 75 : 150), 1)); + } #endif /* for(int i=0; i<12; i++) if(c->type == 5 && c->master == &dodecahedron[i]) @@ -4536,16 +4554,20 @@ EX void drawMarkers() { int adj = 1 - ((sword_angles/cwt.at->type)&1); - if(items[itOrbSword]) ADC(V, cwt.at) - queuestr(V * spin(M_PI+(-adj-2*ang)*M_PI/sword_angles) * xpush0(cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword].color); - if(items[itOrbSword2]) ADC(V, cwt.at) - queuestr(V * spin((-adj-2*ang)*M_PI/sword_angles) * xpush0(-cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword2].color); + if(items[itOrbSword]) + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at)) + queuestr(V * spin(M_PI+(-adj-2*ang)*M_PI/sword_angles) * xpush0(cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword].color); + if(items[itOrbSword2]) + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at)) + queuestr(V * spin((-adj-2*ang)*M_PI/sword_angles) * xpush0(-cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword2].color); } if(SWORDDIM == 3 && !shmup::on) { - if(items[itOrbSword]) ADC(V, cwt.at) - queuestr(V * sword::dir[multi::cpid].T * xpush0(cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword].color); - if(items[itOrbSword2]) ADC(V, cwt.at) - queuestr(V * sword::dir[multi::cpid].T * xpush0(-cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword2].color); + if(items[itOrbSword]) + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at)) + queuestr(V * sword::dir[multi::cpid].T * xpush0(cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword].color); + if(items[itOrbSword2]) + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at)) + queuestr(V * sword::dir[multi::cpid].T * xpush0(-cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword2].color); } } @@ -4603,10 +4625,17 @@ void drawFlashes() { bool kill = true; flashdata& f = flashes[k]; bool copies = false; - ADC(V, f.where) copies = true, draw_flash(f, V, kill); - forCellIdEx(c2, id, f.where) if(!copies) ADC(V, c2) { - draw_flash(f, V * currentmap->iadj(f.where, id), kill); + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, f.where)) { copies = true; + draw_flash(f, V, kill); + } + forCellIdEx(c2, id, f.where) { + if(!copies) { + for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, c2)) { + draw_flash(f, V * currentmap->iadj(f.where, id), kill); + copies = true; + } + } } if(f.t > ticks - 800 && !copies) { kill = false; diff --git a/help.cpp b/help.cpp index 323cc789..a3b9e2e5 100644 --- a/help.cpp +++ b/help.cpp @@ -945,6 +945,9 @@ EX void describeMouseover() { if(c->wall == waRose) out += " (" + its(7-rosephase) + ")"; if(c->wall == waTerraWarrior) out += " (" + its(c->landparam) + ")"; + #if CAP_COMPLEX2 + if(isDie(c->wall)) out += " (" + dice::describe(c) + ")"; + #endif if((c->wall == waBigTree || c->wall == waSmallTree) && c->land != laDryForest) help = @@ -972,6 +975,10 @@ EX void describeMouseover() { else if(c->monst) { out += ", "; out += XLAT1(minf[c->monst].name); + #if CAP_COMPLEX2 + if(isDie(c->monst)) + out += " (" + dice::describe(c) + ")"; + #endif if(hasHitpoints(c->monst)) out += " (" + its(c->hitpoints)+" HP)"; if(isMutantIvy(c)) diff --git a/hyper.h b/hyper.h index 3c43b595..63cad3d2 100644 --- a/hyper.h +++ b/hyper.h @@ -575,11 +575,6 @@ typedef function cellfunction; #define forCellCM(ct, cf) forCellIdCM(ct,forCellCM ## __LINE__,cf) #define forCellAll(ct, cf) forCellIdCM(ct,forCellAll ## __LINE__,cf) -/* conditions */ - -/** `IF_KEY_EXISTS(it, map, key) statement` checks whether the map 'map' contain key 'key', and if so, executes statement with it set to the relevant iterator */ -#define IF_KEY_EXISTS(it, map, key) for(auto it: {map.find(key)}) if(it != map.end()) - // canAttack/moveval flags #define AF_NORMAL 0 // nothing special about this attack @@ -851,6 +846,13 @@ template array make_array(T a, T b, T c, T d) { array x; x[0 template array make_array(T a, T b, T c) { array x; x[0] = a; x[1] = b; x[2] = c; return x; } template array make_array(T a, T b) { array x; x[0] = a; x[1] = b; return x; } +// Find in a std::map or std::unordered_map, or return null. +template +const typename Map::mapped_type *at_or_null(const Map& map, const Key& key) { + auto it = map.find(key); + return (it == map.end()) ? nullptr : &it->second; + } + namespace daily { extern bool on; extern int daily_id; diff --git a/landgen.cpp b/landgen.cpp index ae392a4c..a6c1f856 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -537,8 +537,8 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { c->item = hrand(100) < 80 ? itOrbFrog : itOrbDiscord; if(hrand(5000) < 20*PRIZEMUL && c->wall != waOpenGate) placePrizeOrb(c); - if(c->wall == waNone) buildPrizeMirror(c, 250); - if(c->land == laPalace && (eubinary || c->master->alt) && celldistAlt(c) <= 150 && !(havewhat&HF_MOUSE) && !princess::generating && + if(c->wall == waNone && buildPrizeMirror(c, 250)) {} + else if(c->land == laPalace && (eubinary || c->master->alt) && celldistAlt(c) <= 150 && !(havewhat&HF_MOUSE) && !princess::generating && princess::getPrisonInfo(c) && (eubinary || (princess::getPrisonInfo(c)->bestdist < 6 && princess::getPrisonInfo(c)->princess))) { c->monst = moMouse; @@ -2345,7 +2345,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { } if(d == 7 && c->landparam == 2) forCellEx(c2, c) if(c2->land == laRuins && out_ruin(c2)) c->landparam = 1; ONEMPTY { - if(hrand(1500) < PT(30 + kills[moHexDemon] + kills[moSkeleton] + kills[moMonk] + kills[moPair], 100) && notDippingFor(itRuins)) { + if(hrand(1500) < PT(30 + kills[moHexDemon] + kills[moAltDemon] + kills[moMonk] + kills[moPair] + kills[moCrusher], 100) && notDippingFor(itRuins)) { c->item = itRuins; forCellEx(c2, c) if(c2->monst == moMonk) c->item = itNone; @@ -2587,8 +2587,9 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { case laDice: { #if CAP_COMPLEX2 - if(fargen) + if(fargen && !c->monst && !c->wall) { dice::generate_full(c, items[itDice] + yendor::hardness()); + } #endif break; } @@ -2632,6 +2633,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { c->monst = moHexer; c->item = pick(itCurseWeakness, itCurseDraining, itCurseWater, itCurseFatigue, itCurseRepulsion, itCurseGluttony); } + break; } } diff --git a/language-ru.cpp b/language-ru.cpp index 89f707be..552192f3 100644 --- a/language-ru.cpp +++ b/language-ru.cpp @@ -7270,10 +7270,23 @@ S("A brown gem.", "Коричневый камень.") Orb("Choice", "Выбора") N("Free Fall", GEN_O, "Свободное падение", "Свободные падения", "Свободное падение", "в Свободном падении") + +S("What on one side looks to be a normal (well, infinite) horizontal wall, on the other side turns out to be the vertical wall" + " of an infinitely high tower. Jump from the window, and let the magical gravity carry you...", + + "То, что на одной стороне похоже на обычную (хотя и бесконечную) горизонтальную стену, " + "на другой стороне оказывается вертикальной стеной бесконечно высокой башни. " + "Выпрыгайте через окно, и пусть сила волшебной тяжести везёт Вас...") + N("Falling Dog", GEN_F, "Падающая собака", "Падающие собаки", "Падающую собаку", "Падающей собакой") S("Distant relatives of the Running Dogs.", "Дальние родственники Бегущих собак.") N("Western Hawk", GEN_M, "Западный ястреб", "Западные ястреба", "Западного ястреба", "Западным ястребом") N("Meteorite", GEN_O, "Метеорит", "Метеориты", "Метеорит", "Метеоритом") + +S("These rocks falling from the sky have been captured to fall forever in the artificial gravity. Meteorite iron is believed to be a valuable material for magical weapons.", + "Эти падающие с неба камни захвачены, чтобы вечно падать под силой исскуственной тяжести. " + "Верят, что метеоритное железо - ценный материал для ковки волшебных орудий.") + Orb("Gravity", "Гравитации") N("Irradiated Field", GEN_N, "Облучённое поле", "Облучённые поля", "Облучённое поле", "в Облучённом поле") diff --git a/language.cpp b/language.cpp index 93842d44..19293abf 100644 --- a/language.cpp +++ b/language.cpp @@ -232,7 +232,7 @@ void parrep(string& x, string w, stringpar p) { rep(x, "%a"+w, N->n[3].acc); rep(x, "%abl"+w, N->n[3].abl); rep(x, "%E"+w, choose3(N->n[3].genus, "", "а", "о")); - rep(x, "%A"+w, choose3(N->n[3].genus, "ый", "ая", "ое")); + rep(x, "%A"+w, choose3(N->n[3].genus, "ый", "ую", "ое")); rep(x, "%c"+w, choose3(N->n[3].genus, "ся", "ась", "")); rep(x, "%y"+w, choose3(N->n[3].genus, "ый", "ая", "ое")); } diff --git a/monstermove.cpp b/monstermove.cpp index ffdca97d..1c0f03f2 100644 --- a/monstermove.cpp +++ b/monstermove.cpp @@ -1796,8 +1796,13 @@ EX void specialMoves() { if(m == moHunterGuard && items[itHunting] >= 10) c->monst = moHunterChanging; - if(m == moHunterDog && (havewhat & HF_FAILED_AMBUSH) && hyperbolic && !quotient) - c->monst = moHunterChanging; + if ((havewhat & HF_FAILED_AMBUSH) && hyperbolic && !quotient) { + if(m == moHunterDog) + c->monst = moHunterChanging; + forCellEx(c2, c) + if(c2->monst == moHunterDog) + c2->monst = moHunterChanging; + } if(m == moSleepBull && !peace::on) { bool wakeup = false; diff --git a/orbgen.cpp b/orbgen.cpp index 8bc65909..3c3663b6 100644 --- a/orbgen.cpp +++ b/orbgen.cpp @@ -297,7 +297,7 @@ EX eOrbLandRelation getOLR(eItem it, eLand l) { if(it == itOrbSlaying && !among(l, laMirror, laHell, laEmerald, laDryForest, laCamelot, laPalace, laStorms, laRose, laTortoise, laBurial, laDungeon, laReptile, - laPrairie, laBull, laVolcano, laTerracotta, laRuins, laVariant, laEclectic, laBrownian)) + laPrairie, laBull, laVolcano, laTerracotta, laRuins, laVariant, laEclectic, laBrownian, laCursed)) return olrUseless; if(l == laCocytus) diff --git a/util.cpp b/util.cpp index f98e7d60..c48581d4 100644 --- a/util.cpp +++ b/util.cpp @@ -207,10 +207,10 @@ cld exp_parser::parse(int prio) { cld c = rparse(0); force_eat(")"); - IF_KEY_EXISTS(it, extra_params, "angleunit") { - a *= it->second; - b *= it->second; - c *= it->second; + if (auto *angleunit = hr::at_or_null(extra_params, "angleunit")) { + a *= *angleunit; + b *= *angleunit; + c *= *angleunit; } return edge_of_triangle_with_angles(real(a), real(b), real(c)); @@ -237,14 +237,14 @@ cld exp_parser::parse(int prio) { test.compute_sum(); test.compute_geometry(); res = test.edgelength; - IF_KEY_EXISTS(it, extra_params, "distunit") - res /= it->second; + if (auto *distunit = hr::at_or_null(extra_params, "distunit")) + res /= *distunit; } #endif else if(eat("regangle(")) { cld edgelen = parse(0); - IF_KEY_EXISTS(it, extra_params, "distunit") { - edgelen = edgelen * it->second; + if (auto *distunit = hr::at_or_null(extra_params, "distunit")) { + edgelen *= *distunit; } force_eat(","); @@ -260,14 +260,14 @@ cld exp_parser::parse(int prio) { if(arb::legacy) { res = M_PI - result; - IF_KEY_EXISTS(it, extra_params, "angleofs") - res -= it->second; + if (auto *angleofs = hr::at_or_null(extra_params, "angleofs")) + res -= *angleofs; } else res = result; - IF_KEY_EXISTS(it, extra_params, "angleunit") - res /= it->second; + if (auto *angleunit = hr::at_or_null(extra_params, "angleunit")) + res /= *angleunit; } else if(eat("test(")) { res = parsepar(); @@ -318,8 +318,8 @@ cld exp_parser::parse(int prio) { else if(next() == '(') at++, res = parsepar(); else { string number = next_token(); - IF_KEY_EXISTS(it, extra_params, number) res = it->second; - else IF_KEY_EXISTS(it, params, number) res = it->second->get_cld(); + if (auto *p = hr::at_or_null(extra_params, number)) res = *p; + else if (auto *p = hr::at_or_null(params, number)) res = (*p)->get_cld(); else if(number == "e") res = exp(1); else if(number == "i") res = cld(0, 1); else if(number == "p" || number == "pi") res = M_PI;