2023-09-23 04:08:03 +00:00
|
|
|
part of 'sitemode.dart';
|
|
|
|
|
|
|
|
// We render each thing as a 2x2 block.
|
|
|
|
// We want the player's cell to be
|
|
|
|
// _actually centered_, and the terminal is an even number of cells across
|
|
|
|
// So, shifting the camera an extra cell to the north and then
|
|
|
|
// drawing one extra tile offscreen? That should accomplish it!
|
|
|
|
const cameraViewWidth = Terminal.width ~/ 2 + 1;
|
|
|
|
const cameraViewHeight = Terminal.height ~/ 2 + 1;
|
|
|
|
const cameraMargin =
|
|
|
|
4; // how far the camera is from the ideal position before it pans
|
|
|
|
const cameraTileOffset = geo.Offset(-1, -1);
|
|
|
|
|
|
|
|
extension CameraParts on SiteMode {
|
|
|
|
void cameraInit() {
|
|
|
|
camera = _cameraIdeal();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cameraMaintain() {
|
|
|
|
var ideal = _cameraIdeal();
|
|
|
|
while (camera.x < ideal.x - cameraMargin) {
|
|
|
|
camera = geo.Offset(camera.x + 1, camera.y);
|
|
|
|
}
|
|
|
|
while (camera.x > ideal.x + cameraMargin) {
|
|
|
|
camera = geo.Offset(camera.x - 1, camera.y);
|
|
|
|
}
|
|
|
|
while (camera.y < ideal.y - cameraMargin) {
|
|
|
|
camera = geo.Offset(camera.x, camera.y + 1);
|
|
|
|
}
|
|
|
|
while (camera.y > ideal.y + cameraMargin) {
|
|
|
|
camera = geo.Offset(camera.x, camera.y - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
geo.Offset _cameraIdeal() {
|
|
|
|
return geo.Offset(playerPosition.x - cameraViewWidth ~/ 2 - 1,
|
|
|
|
playerPosition.y - cameraViewHeight ~/ 2 - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cameraDraw() {
|
|
|
|
// Draw the world!
|
|
|
|
// Work in columns, top to bottom, which should facilitate our fake 3D effects
|
|
|
|
for (var cx = 0; cx < cameraViewWidth; cx++) {
|
|
|
|
for (var cy = 0; cy < cameraViewHeight; cy++) {
|
|
|
|
var tx = cameraTileOffset.x + cx * 2;
|
|
|
|
var ty = cameraTileOffset.y + cy * 2;
|
|
|
|
cameraDrawFloor(tx, ty, camera.x + cx, camera.y + cy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var cx = 0; cx < cameraViewWidth; cx++) {
|
|
|
|
for (var cy = 0; cy < cameraViewHeight; cy++) {
|
|
|
|
var tx = cameraTileOffset.x + cx * 2;
|
|
|
|
var ty = cameraTileOffset.y + cy * 2;
|
|
|
|
cameraDrawCell(tx, ty, camera.x + cx, camera.y + cy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cameraDrawFloor(int tx, int ty, int cx, int cy) {
|
|
|
|
var cxy = geo.Offset(cx, cy);
|
|
|
|
if (!fovVisible.contains(cxy)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var tile = level.tiles.get(cx, cy);
|
|
|
|
at(tx, ty).bg(Palette.sitemodeSeenFloor).puts(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
void cameraDrawCell(int tx, int ty, int cx, int cy) {
|
|
|
|
var cursorAbove = at(tx, ty);
|
|
|
|
var cursorBelow = at(tx, ty + 2);
|
|
|
|
|
|
|
|
LevelTile? tile;
|
|
|
|
// TODO: Fade tiles when loaded from memory
|
|
|
|
// TODO: Darken live floors
|
|
|
|
bool seenLive;
|
|
|
|
|
|
|
|
var cxy = geo.Offset(cx, cy);
|
|
|
|
if (fovVisible.contains(cxy)) {
|
|
|
|
tile = level.tiles.get(cx, cy);
|
|
|
|
seenLive = true;
|
|
|
|
} else if (fovMemory.contains(cxy)) {
|
|
|
|
tile = level.tiles.get(cx, cy);
|
|
|
|
seenLive = false;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fovMovable.contains(geo.Offset(cx, cy))) {
|
|
|
|
cursorAbove
|
|
|
|
.highlight()
|
|
|
|
.act(Act(label: "Move", callback: () => playerMoveTo(cx, cy)));
|
|
|
|
}
|
|
|
|
|
|
|
|
var colorWall = Palette.sitemodeSeenWall;
|
|
|
|
var colorWallInner = Palette.sitemodeSeenWall;
|
|
|
|
var colorExit = Palette.sitemodeSeenExit;
|
|
|
|
var colorDoor = Palette.sitemodeSeenDoor;
|
|
|
|
if (cy >= playerPosition.y) {
|
|
|
|
colorWallInner = Palette.sitemodeUnseenWall; // we wouldn't see it anyway
|
|
|
|
}
|
|
|
|
if (!seenLive) {
|
|
|
|
colorWallInner = colorWall = Palette.sitemodeUnseenWall;
|
|
|
|
colorExit = Palette.sitemodeUnseenExit;
|
|
|
|
colorDoor = Palette.sitemodeUnseenDoor;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (tile) {
|
|
|
|
case null:
|
|
|
|
case LevelTile.floor:
|
|
|
|
case LevelTile.openDoor:
|
|
|
|
cursorAbove.touch(2);
|
|
|
|
case LevelTile.exit:
|
|
|
|
cursorAbove.big().fg(colorExit).puts("X");
|
|
|
|
case LevelTile.wall:
|
|
|
|
cursorAbove.fg(colorWall).puts("██");
|
2023-09-23 04:49:39 +00:00
|
|
|
cursorBelow.small().fg(colorWallInner).puts("░░");
|
2023-09-23 04:08:03 +00:00
|
|
|
case LevelTile.closedDoor:
|
|
|
|
cursorAbove.big().fg(colorDoor).puts("+");
|
|
|
|
}
|
|
|
|
|
|
|
|
var cursorContent = at(tx, ty);
|
|
|
|
if (geo.Offset(cx, cy) == playerPosition) {
|
|
|
|
cursorContent.fg(Palette.sitemodePlayer).big().putc(0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|