lineofsight:: first commit

This commit is contained in:
Zeno Rogue
2026-07-31 11:26:30 +02:00
parent 1880494dd2
commit 9dbbd113a7
12 changed files with 153 additions and 21 deletions
+18 -15
View File
@@ -2904,8 +2904,10 @@ void celldrawer::draw() {
checkTide(c);
if(1) {
int seen = in_line_of_sight_for_player(c);
if(seen) {
if(inmirror(c)) {
if(inmirrorcount >= 10) return;
cellwalker cw(c);
@@ -3045,23 +3047,24 @@ void celldrawer::draw() {
queuestrn(V, mapfontscale / 100, s, darkenedby(asciicol, darken), 2);
}
draw_grid();
if(onradar && WDIM == 2 && GDIM == 3) addradar(V, asciichar, darkenedby(asciicol, darken), 0);
if(WDIM == 2 && GDIM == 3) radar_grid();
#endif
check_rotations();
#if CAP_EDIT
if(!inHighQual) mapeditor::drawGhosts(c, V, c->type);
#endif
#if CAP_MODEL
netgen::buildVertexInfo(c, unshift(V));
#endif
}
draw_grid();
if(WDIM == 2 && GDIM == 3) radar_grid();
check_rotations();
#if CAP_EDIT
if(!inHighQual) mapeditor::drawGhosts(c, V, c->type);
#endif
#if CAP_MODEL
netgen::buildVertexInfo(c, unshift(V));
#endif
}
void celldrawer::set_towerfloor(const cellfunction& cf) {
+8
View File
@@ -1672,6 +1672,14 @@ EX void initConfig() {
}, "stereo/high-FOV mode", 'm')
->set_reaction(reset_all_shaders);
param_enum(lineofsight, "mode-lineofsight", los::none)
->editable({
{"OFF", "You see everything in range, but also everything in range sees you!"},
{"geodesic", "Graph geodesic: any sequence of tiles is OK for line-of-sight as long as there are no shortcuts"},
{"geometric", "Approximations of geometric straight lines."}},
"line-of-sight mode", 'v')
->set_reaction(switchLOS_quiet);
param_f(vid.plevel_factor, "plevel_factor", 0.7);
param_b(nohud, "no-hud", false);
+84
View File
@@ -238,6 +238,7 @@ EX void computePathdist(eMonster param, bool include_allies IS(true)) {
cellwalker cw1 = cw + j;
// printf("i=%d cd=%d\n", i, c->move(i)->cpdist);
cell *c2 = cw1.peek();
if(!in_line_of_sight(c2)) continue;
flagtype f = P_MONSTER;
if(param == moIvyRoot) f |= P_IVY;
@@ -546,6 +547,8 @@ EX void bfs() {
for(auto& t: tempmonsters) t.first->monst = t.second;
buildAirmap();
create_los();
}
EX void moverefresh(bool turn IS(true)) {
@@ -932,4 +935,85 @@ EX bool looped(vector<cell*>& whirlline) {
return false;
}
/*-- lineofsight --*/
#if HDR
enum class los { none, geodesic, geometric };
#define PURELOS_LEVEL 10
#endif
EX los lineofsight;
/** when did we switch the lineofsight mode */
EX int lineofsightAt;
EX map<cell*, int> current_fov; // 1 == seen, 2 = see through
EX bool blocks_sight(cell *c, cell *last) {
if(inmirror(c)) {
cellwalker cw(c, neighborId(c, last));
auto cw2 = mirror::reflect(cw);
return blocks_sight(cw2.at, cw2.cpeek());
}
if(c->monst == passive_switch) return true;
return (isWall(c) && !among(c->wall, waFreshGrave, waAncientGrave, waClosedGate, waMirrorWall)) || thruVine(c, last);
}
EX bool in_line_of_sight(cell *c) {
if(lineofsight == los::none) return true;
return current_fov[c];
}
EX bool in_line_of_sight_for_player(cell *c) {
if(in_line_of_sight(c)) return true;
int range = 0;
if(items[itOrbAether]) range = 2;
if(items[itOrbDash] || items[itOrbFrog] || items[itOrbPhasing]) range = 3;
if(items[itOrbInvis]) range = 999999;
return c->cpdist <= range;
}
EX void create_los() {
current_fov.clear();
if(lineofsight == los::none) lineofsight = los::geometric;
if(lineofsight == los::geodesic) {
for(auto c: dcal) {
if(c->cpdist == 0) current_fov[c] |= 3;
forCellEx(c1, c) if(c1->cpdist < c->cpdist && (current_fov[c1] & 2))
current_fov[c] |= blocks_sight(c, c1) ? 1 : 3;
}
}
if(lineofsight == los::geometric) {
for(cell *cp: player_positions()) current_fov[cp] = 3;
for(auto c: dcal) for(auto cp: player_positions()) {
hyperpoint h = tC0(currentmap->relative_matrix(c, cp, C0));
transmatrix T = spintox(h);
cellwalker at = cwt;
while(true) {
int best_i = -1;
ld best_y = HUGE_VAL;
for(int i=0; i<at.at->type; i++) {
cellwalker at1 = at + i;
if(at1.cpeek()->cpdist != at.at->cpdist + 1) continue;
transmatrix U = T * currentmap->adj(at.at, at1.spin);
hyperpoint U0 = tC0(U);
hyperpoint T0 = tC0(T);
if(U0[0] < T0[0] + 1e-6) continue;
ld y;
if(GDIM == 3) y = hypot(U0[1], U0[2]); else y = abs(U0[1]) + (U0[1] > 0 ? 1e-6 : 0);
if(y < best_y) { best_y = y; best_i = i; }
}
if(best_i < 0) break;
at = at + best_i;
T = T * currentmap->adj(at.at, at.spin);
at = at + wstep;
if(blocks_sight(at.at, at.cpeek())) { current_fov[at.at] |= 1; break; }
current_fov[at.at] |= 3;
}
}
}
}
}
+5 -1
View File
@@ -500,7 +500,11 @@ EX color_t stdgridcolor = 0x202020FF;
EX int gridcolor(cell *c1, cell *c2) {
if(cmode & sm::DRAW && !mapeditor::drawing_tool) return Dark(forecolor);
if(!c2)
return 0x202020 >> darken;
return Dark(0x202020);
if(!in_line_of_sight(c1) || !in_line_of_sight(c2))
return Dark(0x808080);
int rd1 = rosedist(c1), rd2 = rosedist(c2);
if(rd1 != rd2) {
int r = rd1+rd2;
+8 -1
View File
@@ -939,6 +939,10 @@ EX void describeMouseover() {
cell *c = mousing ? mouseover : playermoved ? NULL : centerover;
string& out = mouseovers;
if(!c || instat || getcstat != '-') { }
else if(!in_line_of_sight_for_player(c)) {
out = XLAT("you cannot see this place");
help = XLAT("You cannot currently see this tile.\n\nThis also means that your enemies (except ghosts) will not try to reach you through this tile.");
}
else if(c->wall != waInvisibleFloor) {
out = XLAT1(linf[c->land].name);
set_help_to(c->land);
@@ -1136,13 +1140,16 @@ EX void describeMouseover() {
if(isWarped(c) && !isWarpedType(c->land))
out += ", warped";
if(!in_line_of_sight(c))
out += " (magical vision)";
if(isWarped(c)) {
appendHelp(string("\n\n") + XLAT(warpdesc));
if(S7 != 7 || !BITRUNCATED) if(c->item != itOrb37)
appendHelp("\n\n" + other_geometry() + forbidden_unmarked());
}
if(isElectricLand(c) || isElectricLand(cwt.at->land)) {
using namespace elec;
eCharge ch = getCharge(c);
+2
View File
@@ -515,6 +515,8 @@ EX string mode_description() {
if(multi::players > 1) md += " P" + its(multi::players);
if(pureHardcore()) md += " hardcore";
else if(hardcore) md += " partial hardcore";
if(lineofsightAt >= PURELOS_LEVEL) md += "partial lineofsight";
else if(lineofsight != los::none) md += " lineofsight";
if(daily::on) md += " strange";
if(bow::crossbow_mode()) md += " b/" + bow::bowName[bow::style];
if(land_structure != default_land_structure())
+1 -1
View File
@@ -1477,7 +1477,7 @@ EX namespace mapstream {
if(shmup::on) shmup::init();
timerstart = time(NULL); turncount = 0; lastexplore = 0;
sagephase = 0; hardcoreAt = 0;
sagephase = 0; hardcoreAt = 0; lineofsightAt = 0;
timerstopped = false;
savecount = 0; savetime = 0;
cheater = 1;
+7 -1
View File
@@ -302,6 +302,10 @@ EX void switchHardcore_quiet() {
else { hardcore = true; canmove = true; hardcoreAt = turncount; }
}
EX void switchLOS_quiet() {
lineofsightAt = turncount;
}
EX void switchHardcore() {
switchHardcore_quiet();
if(hardcore)
@@ -841,7 +845,9 @@ EX void showChangeMode() {
#if CAP_RACING
dialog::addBoolItem(XLAT("racing mode"), racing::on, 'R');
dialog::add_action(racing::configure_race);
#endif
#endif
add_edit(lineofsight);
show_achievement_eligibility();
+2
View File
@@ -1215,6 +1215,8 @@ EX void groupmove(eMonster movtype, flagtype mf) {
for(int i=0; i<isize(gendfs); i++) {
cell *c = gendfs[i];
if(!in_line_of_sight(c)) continue;
vector<int> dirtable;
forCellIdAll(c2,t,c) dirtable.push_back(t);
+5
View File
@@ -1402,6 +1402,11 @@ EX eItem targetRangedOrb(cell *c, orbAction a) {
addMessage(XLAT("You cannot target that far away!"));
return itNone;
}
if(!in_line_of_sight_for_player(c)) {
if(!isWeakCheck(a))
addMessage(XLAT("You cannot target what you cannot see!"));
return itNone;
}
}
vector<string> orb_error_messages;
+6
View File
@@ -61,6 +61,9 @@ int modediff(score *S) {
if(S->box[196] != (int) land_structure) diff += 32;
if(S->box[119] != shmup::on) diff += 64;
if(pureHardcore() && !isHardcore(S)) diff += 128;
if((S->box[422] < PURELOS_LEVEL) != (lineofsightAt < PURELOS_LEVEL)) diff += 8;
else if(S->box[421] != int(lineofsight)) diff += 8;
if(g != gNormal && S->box[120] != specialland)
diff += 256;
if(g != geometry) {
@@ -84,6 +87,9 @@ string modedesc(score *S) {
if(S->box[197] > 1) s += "/P" + its(S->box[197]);
if(S->box[306]) s += "/i";
if(isHardcore(S)) s += "/h";
if(S->box[421] == 1 && S->box[422] < PURELOS_LEVEL) s += "/l1";
if(S->box[421] == 2 && S->box[422] < PURELOS_LEVEL) s += "/l2";
if(S->box[422] >= PURELOS_LEVEL) s += "/l?";
return s;
}
+7 -2
View File
@@ -419,7 +419,7 @@ EX void initgame() {
timerstart = time(NULL); turncount = 0; lastexplore = 0; rosewave = 0; rosephase = 0;
tickstart = ticks;
noiseuntil = 0;
sagephase = 0; hardcoreAt = 0;
sagephase = 0; hardcoreAt = 0; lineofsightAt = 0;
timerstopped = false;
savecount = 0; savetime = 0;
loadcount = 0; current_loadcount = 0; load_branching = 0;
@@ -485,7 +485,7 @@ EX namespace scores {
/** \brief the amount of boxes reserved for each hr::score item */
#define MAXBOX 500
/** \brief currently used boxes in hr::score */
#define POSSCORE 421
#define POSSCORE 423
/** \brief a struct to keep local score from an earlier game */
struct score {
/** \brief version used */
@@ -1062,6 +1062,11 @@ EX void applyBoxes() {
setprio(bpWeirdStat);
applyBoxNum(current_loadcount, "current load count");
setprio(bpMode);
applyBoxEnum(lineofsight, "lineofsight");
setprio(bpMode);
applyBoxNum(lineofsightAt, "@lineofsightAt");
if(POSSCORE != boxid) printf("ERROR: %d boxes\n", boxid);
if(isize(invorb)) { println(hlog, "ERROR: Orbs not taken into account"); exit(1); }
}