dartterm/lib/game/sitemode/camera.dart

128 lines
3.9 KiB
Dart

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("██");
cursorBelow.small().fg(colorWallInner).puts("░░");
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);
}
}
}