diff --git a/mods/README.txt b/mods/README.txt new file mode 100644 index 0000000..2598e87 --- /dev/null +++ b/mods/README.txt @@ -0,0 +1,10 @@ +ANARCH MODS + +All mods here are created solely by drummyfish and released under the same terms +as Anarch, i.e. completely public domain (CC0). + +Single file mods are right in this directory, multiple file mods are in their +own subdirectory. + +Most mods are diffs and can easily be installed with "git apply", the suckless +way. The diffs sometimes have comments in them with details about the mod. diff --git a/mods/crt.diff b/mods/crt.diff new file mode 100644 index 0000000..cc1f7c8 --- /dev/null +++ b/mods/crt.diff @@ -0,0 +1,91 @@ +This is a mod for Anarch that adds a simple CRT screen effect (scanlines plus +screen warp). By drummyfish. + +diff --git a/game.h b/game.h +index 24285cb..4625ba1 100755 +--- a/game.h ++++ b/game.h +@@ -816,24 +816,32 @@ uint16_t SFG_keyRegisters(uint8_t key) + return SFG_keyJustPressed(key) || SFG_keyRepeated(key); + } + +-#if SFG_RESOLUTION_SCALEDOWN == 1 +- #define SFG_setGamePixel SFG_setPixel +-#else ++uint8_t SFG_screenWarpMap[SFG_GAME_RESOLUTION_X][SFG_GAME_RESOLUTION_Y]; + + /** + Sets the game pixel (a pixel that can potentially be bigger than the screen + pixel). + */ +-static inline void SFG_setGamePixel(uint16_t x, uint16_t y, uint8_t colorIndex) ++static inline void SFG_setGamePixel(int16_t x, int16_t y, uint8_t colorIndex) + { +- uint16_t screenY = y * SFG_RESOLUTION_SCALEDOWN; +- uint16_t screenX = x * SFG_RESOLUTION_SCALEDOWN; ++ int16_t screenX = x - SFG_GAME_RESOLUTION_X / 2; ++ int16_t screenY = y - SFG_GAME_RESOLUTION_Y / 2; ++ ++ uint8_t mul = SFG_screenWarpMap[x][y]; ++ ++ screenX = (screenX * mul) / 128; ++ screenY = (screenY * mul) / 128; + +- for (uint16_t j = screenY; j < screenY + SFG_RESOLUTION_SCALEDOWN; ++j) +- for (uint16_t i = screenX; i < screenX + SFG_RESOLUTION_SCALEDOWN; ++i) ++ screenX = (screenX + SFG_GAME_RESOLUTION_X / 2) * SFG_RESOLUTION_SCALEDOWN; ++ screenY = (screenY + SFG_GAME_RESOLUTION_Y / 2) * SFG_RESOLUTION_SCALEDOWN; ++ ++ // scanline effect: ++ colorIndex -= ((screenY / 2) % 2) && (colorIndex % 8 != 0); ++ ++ for (int16_t j = screenY; j < screenY + SFG_RESOLUTION_SCALEDOWN; ++j) ++ for (int16_t i = screenX; i < screenX + SFG_RESOLUTION_SCALEDOWN; ++i) + SFG_setPixel(i,j,colorIndex); + } +-#endif + + void SFG_recomputePLayerDirection() + { +@@ -1691,6 +1699,23 @@ void SFG_init() + SFG_game.cheatState = 0; + SFG_game.continues = 1; + ++ for (int32_t y = 0; y < SFG_GAME_RESOLUTION_Y; ++y) ++ for (int32_t x = 0; x < SFG_GAME_RESOLUTION_X; ++x) ++ { ++ int32_t dx = x - SFG_GAME_RESOLUTION_X / 2; ++ dx = (dx * dx) / 512; ++ ++ int32_t dy = y - SFG_GAME_RESOLUTION_Y / 2; ++ dy = (dy * dy) / 512; ++ ++ SFG_screenWarpMap[x][y] = 128 - ++ ((dx + dy) * SFG_SCREEN_WARP) / ++ ( ++ ((SFG_GAME_RESOLUTION_X * SFG_GAME_RESOLUTION_X) + ++ (SFG_GAME_RESOLUTION_Y * SFG_GAME_RESOLUTION_Y)) / (4 * 512) ++ ); ++ } ++ + RCL_initRayConstraints(&SFG_game.rayConstraints); + SFG_game.rayConstraints.maxHits = SFG_RAYCASTING_MAX_HITS; + SFG_game.rayConstraints.maxSteps = SFG_RAYCASTING_MAX_STEPS; +diff --git a/settings.h b/settings.h +index 2c8820a..663fcec 100644 +--- a/settings.h ++++ b/settings.h +@@ -84,6 +84,13 @@ + #define SFG_SCREEN_RESOLUTION_Y 600 + #endif + ++/** ++ Sets the amount of screen warp for the CRT simulation effect. ++*/ ++#ifndef SFG_SCREEN_WARP ++ #define SFG_SCREEN_WARP 16 ++#endif ++ + /** + How quickly player turns left/right, in degrees per second. + */ diff --git a/mods/makeMapImages.c b/mods/makeMapImages.c new file mode 100644 index 0000000..581e740 --- /dev/null +++ b/mods/makeMapImages.c @@ -0,0 +1,296 @@ +/** Simple program to export Anarch maps as isometric images in PPM format. + The code is not very nice, the goal is just to get the images :) + + by drummyfish, released under CC0 */ + +#include +#include "../game.h" + +#define IMAGE_W 4096 // if changed you also have to change PPM header data +#define IMAGE_H 2500 + +#define CENTER_X (IMAGE_W / 2) +#define CENTER_Y (IMAGE_H / 6) + +#define TILE_H 25 // vertical height +#define TILE_TILT 2 // view angle, by how many H pixels we'll shift one down + +unsigned char image[IMAGE_H * IMAGE_W * 3]; + +int maxHeights[4]; // ceiling limits for different quadrants + +void drawPixel(int x, int y, unsigned char color, int multiply) +{ + if (x < 0 || x >= IMAGE_W || y < 0 || y >= IMAGE_H) + return; + + uint16_t rgb = paletteRGB565[color]; + + int index = (y * IMAGE_W + x) * 3; + + image[index] = ((((rgb >> 11) << 3) & 0xff) * multiply) / 128; + image[index + 1] = ((((rgb >> 5) << 2) & 0xff) * multiply) / 128; + image[index + 2] = (((rgb << 3) & 0xff) * multiply) / 128; +} + +void drawColum(int x, int y, int z1, int z2, int texture, int doorTexture, const uint8_t *sprite) +{ + if (texture == SFG_TILE_TEXTURE_TRANSPARENT) + return; + + int inc = z2 > z1 ? 1 : -1; + + int minZ = z1 < z2 ? z1 : z2; + + int brightness = (minZ / 2) % 128; + + if (brightness < 0) + brightness *= -1; + + brightness += 30; + + for (int i = 0; i < SFG_TEXTURE_SIZE; ++i) // platform + for (int j = -1 * i / TILE_TILT; j <= i / TILE_TILT; ++j) + { + drawPixel(x + i,y + minZ + j,SFG_currentLevel.floorColor,brightness); + drawPixel(x + 2 * SFG_TEXTURE_SIZE - 1 - i,y + minZ + j,SFG_currentLevel.floorColor,brightness); + } + + if (sprite != 0) + for (int sy = 0; sy < SFG_TEXTURE_SIZE; ++sy) + for (int sx = 0; sx < SFG_TEXTURE_SIZE; ++sx) + { + uint8_t color = SFG_getTexel(sprite,sx,sy); + + if (color != SFG_TRANSPARENT_COLOR) + drawPixel(x + sx + SFG_TEXTURE_SIZE / 2,y + minZ + sy - SFG_TEXTURE_SIZE,color,110); + } + + if (z1 == z2) + return; + + z1 += inc; + + int texY = 0; + + while (z1 != z2) // column (walls) + { + int ty = (texY * SFG_TEXTURE_SIZE) / TILE_H; + + uint8_t *t = (doorTexture < 0 || ty > SFG_TEXTURE_SIZE) ? + SFG_currentLevel.textures[texture] : + (SFG_wallTextures + doorTexture * SFG_TEXTURE_STORE_SIZE); + + for (int i = 0; i < SFG_TEXTURE_SIZE; ++i) + { + uint8_t color = SFG_getTexel(t,i,ty); + + if (color != SFG_TRANSPARENT_COLOR) + drawPixel(x + i,y + z1 + i / TILE_TILT,color,75); + + color = SFG_getTexel(t,SFG_TEXTURE_SIZE - i,ty); + + if (color != SFG_TRANSPARENT_COLOR) + drawPixel(x + 2 * SFG_TEXTURE_SIZE - 1 - i,y + z1 + i / TILE_TILT, + color,128); + } + + texY++; + z1 += inc; + } +} + +void tileIndexToXY(int n, int *x, int *y) +{ + int reverse = 0; + + if (n > SFG_MAP_SIZE * SFG_MAP_SIZE / 2) + { + reverse = 1; + n = SFG_MAP_SIZE * SFG_MAP_SIZE - 1 - n; + } + + *y = 0; + *x = 0; + + while (*y < n) + { + *y += 1; + n -= *y; + } + + while (n > 0) + { + *x += 1; + *y -= 1; + + n--; + } + + if (reverse) + { + *x = SFG_MAP_SIZE - 1 - *x; + *y = SFG_MAP_SIZE - 1 - *y; + } + + *x = SFG_MAP_SIZE - 1 - *x; +} + +void exportMap(int index) +{ + SFG_setAndInitLevel(index); + + for (int i = 0; i < IMAGE_H * IMAGE_W * 3; ++i) + image[i] = 0; + + unsigned char header[] = + {'P', '6', ' ', '4', '0', '9', '6', ' ', '2', '5', '0', '0', ' ', + '2', '5', '5', '\n'}; + + char fname[] = "map0.ppm"; + + fname[3] += index; + + FILE *f = fopen(fname,"wb"); + + fwrite(header,sizeof(header),1,f); + + int n = 0; + + for (int drawY = 0; drawY < SFG_MAP_SIZE; ++drawY) + { + for (int i = 0; i < 2; ++i) + { + int xLimit = (1 + 2 * drawY + i); + + if (drawY >= SFG_MAP_SIZE / 2) + xLimit = SFG_MAP_SIZE * 2 - xLimit; + +#define TW (2 * SFG_TEXTURE_SIZE) +#define TH (SFG_TEXTURE_SIZE / TILE_TILT) + + int startX = -1 * xLimit * TW / 2; + + for (int drawX = 0; drawX < xLimit; ++drawX) + { + uint8_t properties; + int tx, ty; + + tileIndexToXY(n,&tx,&ty); + + int maxHeightTiles = maxHeights[2 * ((ty * 2) / SFG_MAP_SIZE) + + (tx * 2) / SFG_MAP_SIZE]; + + SFG_TileDefinition tile = + SFG_getMapTile(SFG_currentLevel.levelPointer,tx,ty,&properties); + + int h = SFG_TILE_FLOOR_HEIGHT(tile); + + if (properties == SFG_TILE_PROPERTY_ELEVATOR) + { + h += SFG_TILE_CEILING_HEIGHT(tile); + h /= 2; + } + + if (maxHeightTiles * 4 < h) + h = maxHeightTiles * 4; + + h = (h * SFG_WALL_HEIGHT_STEP * TILE_H) / RCL_UNITS_PER_SQUARE; + + const uint8_t *sprite = 0; + + for (int i = 0; i < SFG_MAX_LEVEL_ELEMENTS; ++i) + { + SFG_LevelElement e = + SFG_currentLevel.levelPointer->elements[i]; + + if (e.type == SFG_LEVEL_ELEMENT_NONE) + break; + + if (e.coords[0] == tx && e.coords[1] == ty && !(e.type & 0x10)) + { + uint8_t ss; + + if (e.type & 0x20) + sprite = SFG_getMonsterSprite(e.type,0,0); + else + SFG_getItemSprite(e.type,&sprite,&ss); + + break; + } + } + + drawColum( + CENTER_X + startX + drawX * TW, + CENTER_Y + (2 * drawY + i) * TH - drawY,-1 * h, + 0,SFG_TILE_FLOOR_TEXTURE(tile), + (properties == SFG_TILE_PROPERTY_DOOR) ? + SFG_currentLevel.levelPointer->doorTextureIndex : -1, + sprite); + + int maxH = 16 * TILE_H; + + if (maxHeightTiles * TILE_H < maxH) + maxH = maxHeightTiles * TILE_H; + + maxH *= -1; + + h = SFG_TILE_FLOOR_HEIGHT(tile) + SFG_TILE_CEILING_HEIGHT(tile); + h = -1 * (h * SFG_WALL_HEIGHT_STEP * TILE_H) / RCL_UNITS_PER_SQUARE + 1; + + if (SFG_TILE_CEILING_HEIGHT(tile) < 31 && + maxH < h && properties != SFG_TILE_PROPERTY_ELEVATOR) + { + drawColum( + CENTER_X + startX + drawX * TW, + CENTER_Y + (2 * drawY + i) * TH - drawY,maxH, + h,SFG_TILE_CEILING_TEXTURE(tile),-1,0); + } + + n++; + } + } + } + + fwrite(image,1,IMAGE_W * IMAGE_H * 3,f); + + fclose(f); +} + +int main(void) +{ + SFG_init(); + +#define E(m,h1,h2,h3,h4)\ + printf("exporting %d\n",m);\ + maxHeights[0] = h1;\ + maxHeights[1] = h2;\ + maxHeights[2] = h3;\ + maxHeights[3] = h4;\ + exportMap(m); + + E(0,100,100,100,100) + E(1,7,5,7,5) + E(2,5,6,5,5) + E(3,7,7,6,6) + E(4,100,6,100,6) + E(5,100,100,100,100) + E(6,5,4,3,3) + E(7,7,7,7,7) + E(8,5,5,5,5) + E(9,100,100,100,100) + + return 0; +} + +// just create empty functions, game.h requires it: +int8_t SFG_keyPressed(uint8_t key) { return 0; } +void SFG_getMouseOffset(int16_t *x, int16_t *y) { } +uint32_t SFG_getTimeMs() { return 0; } +void SFG_sleepMs(uint16_t timeMs) { } +static inline void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex) { } +void SFG_playSound(uint8_t soundIndex, uint8_t volume) { } +void SFG_setMusic(uint8_t value) { } +void SFG_processEvent(uint8_t event, uint8_t data) { } +void SFG_save(uint8_t data[SFG_SAVE_SIZE]) { } +uint8_t SFG_load(uint8_t data[SFG_SAVE_SIZE]) { return 0; }