This commit is contained in:
Miloslav Číž 2023-01-01 01:52:11 +01:00
commit fb3266d8cd
3 changed files with 397 additions and 0 deletions

10
mods/README.txt Normal file
View File

@ -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.

91
mods/crt.diff Normal file
View File

@ -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.
*/

296
mods/makeMapImages.c Normal file
View File

@ -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 <stdio.h>
#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; }