Skip to content

Instantly share code, notes, and snippets.

@Sir-Irk
Last active March 25, 2017 12:56
Show Gist options
  • Select an option

  • Save Sir-Irk/e1a95354429a9e25335e772c911e1fc0 to your computer and use it in GitHub Desktop.

Select an option

Save Sir-Irk/e1a95354429a9e25335e772c911e1fc0 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <conio.h>
#include <time.h>
#define array_count(arr) (sizeof(arr) / sizeof((arr)[0]))
#undef max
#undef min
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
#define in_range(x, min, max) (((x) >= (min)) && ((x) < (max)))
typedef int32_t bool32_t
//I prefer signed length(unless on the off chance you are using
//a string bigger than half of memory on 32-bit systems. Which we are not)
inline ptrdiff_t
str_len(char *s)
{
ptrdiff_t result = 0;
for(; s[result] != '\0'; ++result);
return result;
}
inline int32_t
clamp(int32_t val, int32_t min, int32_t max)
{
if(val < min) return min;
if(val > max) return max;
return val;
}
typedef struct
{
uint32_t state;
int32_t position;
int32_t health;
int32_t hunger;
int32_t logs;
int32_t stones;
int32_t berries;
} player_t;
typedef struct
{
uint32_t state;
int32_t position;
int32_t health;
} camp_t;
//==========================================================================================================
//NOTE: Player and Camp state flags
//==========================================================================================================
//
enum
{
ps_onFire = 1 << 0,
ps_injured = 1 << 1,
ps_hungry = 1 << 2,
ps_tired = 1 << 3,
ps_happy = 1 << 4,
ps_wet = 1 << 5,
ps_cold = 1 << 6,
ps_hasCamp = 1 << 7,
ps_atCamp = 1 << 8,
ps_count = 9
};
enum
{
cs_onFire = 1 << 0,
cs_damaged = 1 << 1,
cs_wet = 1 << 2,
cs_count = 3
};
//===================================================================================================
#define MAX_STAT_VALUE 100
#define HUNGER_DAMAGE_THRESHOLD 70
#define HUNGER_INCREMENT 2
#define HUNGER_DAMAGE 5
#define COLD_DAMAGE 1
#define NUM_STONES_TO_BUILD_WALL 1
#define NUM_STONES_RECLAIMED_FROM_WALL 1
#define NUM_LOGS_TO_BUILD_DOOR 6
#define NUM_LOGS_RECLAIMED_FROM_DOOR ((NUM_LOGS_TO_BUILD_DOOR) / 2)
#define CAMP_DEAD_POSITION 0
//===================================================================================================
//NOTE: Helper macros for manipulating player state
#define ps_enter_camp(flags) flags |= ps_atCamp
#define ps_leave_camp(flags) flags &= ~ps_atCamp
#define ps_set_on_fire(flags) flags = (flags & ~ps_happy) | (ps_onFire)
#define ps_set_wet(flags) flags = (flags & ~ps_onFire) | ps_wet;
//#define ps_heal(flags) flags = (!(flags & (ps_onFire | ps_hungry)) && (flags & ps_atCamp)) ? (flags & ~(ps_injured | ps_wet)) : flags;
#define ps_eat_tasty_meal(flags) flags = (flags & ~ps_hungry) | ps_happy;
//NOTE: Helper macros for manipulating camp state
#define cs_set_damaged(flags) flags |= cs_damaged;
#define cs_set_on_fire(flags) flags |= cs_onFire;
#define cs_set_wet(flags) flags = (flags & ~cs_onFire) | cs_wet;
void
print_set_flags(uint32_t flags, int32_t flagCount, char** strings)
{
assert(flagCount < 32);
for(int32_t i = 0; i < flagCount; ++i)
if(flags & (1 << i)) printf("[%s]\n", strings[i]);
}
//==========================================================================================================
//NOTE: Map
//==========================================================================================================
#define playerChar 'O'
#define _mapDimX 40
#define _mapDimY 15
#define _actualTileDimX (_mapDimX + 1) // +1 For the newline character
#define _actualTileDimY _mapDimY
#define _actualMapSize ((_actualTileDimX) * (_actualTileDimY))
#define TILE_BLANK ' '
#define TILE_FIRE 'f'
#define TILE_WATER 'w'
#define TILE_LOG 'L'
#define TILE_STONE 'S'
#define TILE_BERRY '8'
#define TILE_CAMP 'C'
#define TILE_WALL 'x'
#define TILE_DOOR 'D'
static char *originalMap =
{
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"x x\n"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"
};
static bool32_t
is_walkable_tile(int32_t newPos, int32_t mapDimX, int32_t mapDimY)
{
if(newPos < mapDimX || newPos >= ((mapDimY-1) * mapDimX)) return false;
for(int32_t y = 1; y < mapDimY-1; ++y)
if(y * mapDimX == newPos || (y * mapDimX + (mapDimX-2) == newPos)) return false;
return true;
}
#define get_inner_dim_x(x) (x-3)
#define get_inner_dim_y(y) (x-2)
static int32_t
try_move(int32_t currentPosition, char *wallTiles, int32_t mapX, int32_t mapY, int32_t moveAmount)
{
int32_t result = currentPosition;
int32_t newPos = currentPosition + moveAmount;
if(wallTiles[newPos] != TILE_WALL) result = newPos;
return result;
}
//NOTE: healing can only happen at camp
inline int32_t
try_heal(player_t *player, int32_t amount)
{
int32_t result = player->health;
uint32_t flags = player->state;
if(!(flags & (ps_onFire | ps_hungry)) && (flags & ps_atCamp))
{
player->state = (flags & ~(ps_injured | ps_wet));
result = min(player->health + amount, MAX_STAT_VALUE);
}
return result;
}
inline uint32_t
try_happiness(uint32_t state)
{
uint32_t result;
uint32_t unhappyFlags = ps_onFire | ps_wet | ps_hungry | ps_tired;
result = (!(state & unhappyFlags)) ? (state | ps_happy) : (state & ~ps_happy);
return result;
}
#define BERRY_HUNGER_REDUCTION 60
#define BERRY_HEAL_AMOUNT 40
static int32_t
try_eat(player_t *player)
{
int32_t result = player->hunger;
if(player->hunger == 0 || player->berries == 0) return result;
player->berries = max(player->berries - 1, 0);
result = max(player->hunger - BERRY_HUNGER_REDUCTION, 0);
player->health = min(player->health + BERRY_HEAL_AMOUNT, MAX_STAT_VALUE);
return result;
}
static void
copy_inner_tiles(char *src, char *dst, int32_t mapDimX, int32_t mapDimY, char ignoreChar = -1)
{
for(int32_t y = 1; y < mapDimY - 1; ++y)
{
for(int32_t x = 1; x < mapDimX - 2; ++x)
{
int32_t index = y * mapDimX + x;
if(src[index] == ignoreChar) continue;
dst[index] = src[index];
}
}
}
#define LOGS_REQUIRED_TO_BUILD_CAMP 4
static bool32_t
can_build_camp(player_t *player)
{
return !(player->state & ps_hasCamp) && (player->logs >= LOGS_REQUIRED_TO_BUILD_CAMP)
}
static void
build_camp(player_t *player, camp_t *camp)
{
if(can_build_camp(player))
{
camp->position = player->position;
player->state |= ps_hasCamp;
player->logs -= LOGS_REQUIRED_TO_BUILD_CAMP;
}
}
//==========================================================================================================
//NOTE: Player commands
//==========================================================================================================
#define COMMAND_MOVE_UP 'w'
#define COMMAND_MOVE_DOWN 's'
#define COMMAND_MOVE_LEFT 'a'
#define COMMAND_MOVE_RIGHT 'd'
#define COMMAND_MAKE_CAMP 'c'
#define COMMAND_PLACE_WALL 'p'
#define COMMAND_PLACE_DOOR 'o'
#define COMMAND_REMOVE_WALL 'r'
#define COMMAND_HEAL 'g'
#define COMMAND_EAT 'e'
#define COMMAND_HELP 'h'
#define COMMAND_QUIT 'q'
static char global_commands[] =
{
COMMAND_MOVE_UP,
COMMAND_MOVE_DOWN,
COMMAND_MOVE_LEFT,
COMMAND_MOVE_RIGHT,
COMMAND_MAKE_CAMP,
COMMAND_PLACE_WALL,
COMMAND_PLACE_DOOR,
COMMAND_REMOVE_WALL,
COMMAND_HEAL,
COMMAND_EAT,
COMMAND_HELP,
COMMAND_QUIT,
};
static bool
is_valid_command(char c)
{
for(int32_t i = 0; i < array_count(global_commands); ++i)
if(c == global_commands[i]) return true;
return false;
}
//==========================================================================================================
//NOTE: size is assumed to include the null byte
static char *
create_tile_set(char *map, ptrdiff_t size)
{
char *result = (char *)calloc(size, 1);
assert(result);
memcpy(result, map, size);
return result;
}
static int32_t
generate_random_spawn_index(int32_t tilesInnerDimX, int32_t tilesInnerDimY)
{
int32_t totalInnerTiles = tilesInnerDimX * tilesInnerDimY;
int32_t randIndex = (rand() + (_mapDimX+2)) % totalInnerTiles;
return randIndex;
}
static void
spawn_object_random_chance(char *tiles, int32_t tilesInnerDimX, int32_t tilesInnerDimY, int32_t tileType)
{
if(rand() % 4 != 1) return;
int32_t totalInnerTiles = tilesInnerDimX * tilesInnerDimY;
int32_t randIndex = (rand() + (_mapDimX+2)) % totalInnerTiles;
tiles[randIndex] = tileType;
}
static bool32_t
index_exists(int32_t *indexes, int32_t indexComp, int32_t count)
{
for(int32_t i = 0; i < count; ++i)
{
if(indexes[i] == indexComp) return true;
}
return false;
}
static void
fire_spread(char *fireTiles, char *waterTiles, char *stoneTiles, char *wallTiles,
int32_t *fireTimers, int32_t dimX, int32_t dimY)
{
int32_t newFireTileIndexes [(_actualTileDimX)*_actualTileDimY] = {};
int32_t checkedTimerIndexes[(_actualTileDimX-3)*(_actualTileDimY-2)] = {};
int32_t newFireCount = 0;
int32_t checkedTimerCount = 0;
int32_t tileIndexOffsets [4] = { dimX, -dimX, 1, -1 };
int32_t timerIndexOffsets[4] = { dimX-3, -dimX-3, 1, -1 };
for(int32_t y = 1; y < dimY-2; ++y)
{
for(int32_t x = 1; x < dimX-3; ++x)
{
int32_t tileIndex = y * dimX + x;
int32_t timerIndex = (y-1) * (dimX-3) + (x-1);
if(fireTiles[tileIndex] == TILE_FIRE)
{
//NOTE: offsets up, down, left, right
for(int32_t off = 0; off < array_count(tileIndexOffsets); ++off)
{
int32_t tileIndexOffset = tileIndex + tileIndexOffsets [off];
int32_t timerIndexOffset = timerIndex + timerIndexOffsets[off];
if(stoneTiles[tileIndexOffset] == TILE_BLANK &&
waterTiles[tileIndexOffset] == TILE_BLANK &&
wallTiles [tileIndexOffset] == TILE_BLANK &&
!index_exists(newFireTileIndexes, tileIndexOffset, array_count(newFireTileIndexes)))
{
//NOTE: we don't want duplicated timer
//checks because we set a fireTimer to a negative
//value when the fire dies out. This is a hacky
//way to prevent the tile from immediately re-igniting
//Redundant timer checks will mess this up
bool32_t alreadyChecked = false;
for(int32_t i = 0; i < checkedTimerCount; ++i)
{
if(checkedTimerIndexes[i] == timerIndexOffset)
{
alreadyChecked = true;
break;
}
}
if(alreadyChecked) continue;
checkedTimerIndexes[checkedTimerCount++] = timerIndexOffset;
if(fireTimers[timerIndexOffset] < 0)
{
//NOTE: Increment so that eventually this tile
//will be re-ignitable(based on negative timer set when fire dies).
++fireTimers[timerIndexOffset];
continue;
}
newFireTileIndexes [newFireCount++] = tileIndexOffset;
}
}
}
}
}
assert(newFireCount <= array_count(newFireTileIndexes));
for(int32_t i = 0; i < newFireCount; ++i)
{
fireTiles[newFireTileIndexes[i]] = TILE_FIRE;
}
}
//NOTE: sets tiles from "bottom" to blank if they are overlapped by "top"
//NOTE: assumes tiles are size of original map(not just inner section)
static void
destroy_overlapped_tiles(char *top, char *bottom, int32_t tilesInnerDimX, int32_t tilesInnerDimY)
{
for(int32_t y = 1; y < tilesInnerDimY+1; ++y)
{
for(int32_t x = 1; x < tilesInnerDimX+1; ++x)
{
int32_t index = y * _actualTileDimX + x;
if(top[index] != TILE_BLANK && bottom[index] != TILE_BLANK)
{
bottom[index] = TILE_BLANK;
}
}
}
}
inline int32_t
take_damage(int32_t hp, int32_t damage)
{
return max(hp - damage, 0);
}
static void
display_stat_bar(char *str, int32_t value, int32_t numPerSegment, char segment)
{
char buf[MAX_STAT_VALUE + 1] = {};
int32_t index = 0;
printf("%s", str);
for(int32_t i = 0; i < value; i += numPerSegment)
buf[index++] = segment;
assert(index < array_count(buf));
buf[index] = '\0';
assert(MAX_STAT_VALUE <= 100);
if (value >= MAX_STAT_VALUE) { printf("[%d] %s\n" , value, buf); }
else if (value >= 10) { printf("[ %d] %s\n" , value, buf); }
else { printf("[ %d] %s\n", value, buf); }
}
#define take_resource(p, tiles, resource) \
do { \
tiles[p.position] = TILE_BLANK; \
p.resource++; \
}while(0)
//static int32_t
//generate_random_spawn_index(int32_t tilesInnerDimX, int32_t tilesInnerDimY)
static void
crappy_random_tile_gen(char *tiles, char c, int32_t chance, int32_t tilesInnerDimX, int32_t tilesInnerDimY)
{
for(int32_t y = 1; y < tilesInnerDimY+1; ++y)
{
for(int32_t x = 1; x < tilesInnerDimX+1; ++x)
{
if(rand() % chance == 1) tiles[y * _actualTileDimX + x] = c;
}
}
}
#define FIRE_LIFETIME 4
static void
update_fire_lives(char *fireTiles, int32_t *timers, int32_t tilesInnerDimX, int32_t tilesInnerDimY)
{
for(int32_t y = 1; y < tilesInnerDimY+1; ++y)
{
for(int32_t x = 1; x < tilesInnerDimX+1; ++x)
{
int32_t tileIndex = y * _actualTileDimX + x;
int32_t timerIndex = (y-1) * tilesInnerDimX + (x-1);
if(fireTiles[tileIndex] == TILE_FIRE)
{
++timers[timerIndex];
if(timers[timerIndex] >= FIRE_LIFETIME)
{
timers[timerIndex] = -2;
fireTiles[tileIndex] = TILE_BLANK;
}
}
}
}
}
static int32_t
try_place_wall(int32_t numStones, int32_t position, char *wallTiles, int32_t mapSize)
{
int32_t result = numStones;
assert(mapSize > 0);
assert(in_range(position, 0, mapSize));
if(numStones < NUM_STONES_TO_BUILD_WALL) return result;
if(wallTiles[position] == TILE_BLANK && numStones >= NUM_STONES_TO_BUILD_WALL)
{
wallTiles[position] = TILE_WALL;
result -= NUM_STONES_TO_BUILD_WALL;
}
return result;
}
//NOTE: Wall tiles have both walls and doors
static int32_t
try_place_door(int32_t numLogs, int32_t position, char *wallTiles,
int32_t actualTileDimX, int32_t actualTileDimY)
{
int32_t result = numLogs;
int32_t mapSize = actualTileDimX * actualTileDimY;
assert(mapSize > 0);
assert(in_range(position, 0, mapSize));
if(numLogs < NUM_LOGS_TO_BUILD_DOOR) return result;
//NOTE: only place a door if there are walls above and below or to the right and left
int32_t offsets[4] = {-actualTileDimX, actualTileDimX, -1, 1};
for(int32_t i = 0; i < array_count(offsets); i += 2)
{
int32_t offset1 = position + offsets[i];
int32_t offset2 = position + offsets[i+1];
if(in_range(offset1, 0, mapSize) && in_range(offset2, 0, mapSize))
{
if(wallTiles[offset1] == TILE_WALL && wallTiles[offset2] == TILE_WALL)
{
result -= NUM_LOGS_TO_BUILD_DOOR;
wallTiles[position] = TILE_DOOR;
break;
}
}
}
return result;
}
static void
try_remove_wall(player_t *player, char *wallTiles, int32_t positionToRemove,
int32_t actualTilesDimX, int32_t actualTilesDimY)
{
int32_t tilesSize = actualTilesDimX * actualTilesDimY;
assert(in_range(positionToRemove, 0, tilesSize));
if(!is_walkable_tile(positionToRemove, actualTilesDimX, actualTilesDimY)) return;
if(wallTiles[positionToRemove] == TILE_WALL)
{
player->stones += NUM_STONES_RECLAIMED_FROM_WALL;
wallTiles[positionToRemove] = TILE_BLANK;
}
else if(wallTiles[positionToRemove] == TILE_DOOR)
{
player->logs += NUM_LOGS_RECLAIMED_FROM_DOOR;
wallTiles[positionToRemove] = TILE_BLANK;
}
}
static bool32_t
fire_is_active(char *fireTiles, int32_t actualMapSize)
{
assert(actualMapSize > 0);
for(int32_t i = 0; i < actualMapSize; ++i)
{
if(fireTiles[i] == TILE_FIRE) return true;
}
return false;
}
static void
draw_game(player_t *player, camp_t *camp, char *map, char** playerStateStrings, int32_t pStateLength,
char **campStateStrings, int32_t cStateLength, int32_t turnsLasted)
{
printf("%s\n\n", map);
printf("==========================================\n");
display_stat_bar("HP :", player->health , 5, '+');
display_stat_bar("Hunger :", player->hunger , 5, '-');
display_stat_bar("Logs :", player->logs , 1, '+');
display_stat_bar("Stones :", player->stones , 1, '+');
display_stat_bar("Berries :", player->berries, 1, '+');
printf("==========================================\n");
printf("\n= Player State =\n\n");
print_set_flags(player->state, pStateLength, playerStateStrings);
printf("\n================\n");
if(camp->position != CAMP_DEAD_POSITION)
{
printf("\nCamp state: \n");
print_set_flags(camp->state, cStateLength, campStateStrings);
printf("HP: %d\n", camp->health);
}
printf("\nTurns Lasted : %d\n", turnsLasted);
}
typedef struct
{
char primary;
char secondary;
} entered_commands;
static entered_commands
get_next_command()
{
entered_commands result = {};
char commandBuffer[16] = {};
ptrdiff_t commandBufferSize = sizeof(commandBuffer);
if(fgets(commandBuffer, commandBufferSize, stdin) != NULL)
{
for(int32_t i = 0; i < commandBufferSize; ++i)
{
if(!is_valid_command(commandBuffer[i])) continue;
result.primary = commandBuffer[i];
if(result.primary == COMMAND_REMOVE_WALL)
{
int32_t index = i + 1;
if(index > commandBufferSize)
{
result.primary = 0;
break;
}
switch(commandBuffer[index])
{
case COMMAND_MOVE_UP :
case COMMAND_MOVE_DOWN :
case COMMAND_MOVE_RIGHT :
case COMMAND_MOVE_LEFT :
{
result.secondary = commandBuffer[index];
} break;
default:
{
result.primary = 0;
} break;
}
}
break;
}
}
return result;
}
static void
display_help()
{
printf("================================== HELP ===================================\n");
printf("Each turn you can either enter a command or simply advance the simulation\n");
printf("To enter a command, enter a character into the console and press enter.\n");
printf("Only the first letter will be used as a command unless you use a command\n");
printf("that uses two characters or more.\n\n");
printf("Commands : \n\n");
printf(" Move Up : %c\n", COMMAND_MOVE_UP);
printf(" Move Down : %c\n", COMMAND_MOVE_DOWN);
printf(" Move Left : %c\n", COMMAND_MOVE_LEFT);
printf(" Move Right : %c\n\n", COMMAND_MOVE_RIGHT);
printf(" Eat Food : %c\n", COMMAND_EAT);
printf(" Heal : %c (NOTE: only works if you are at camp)\n", COMMAND_HEAL);
printf(" Place Wall : %c\n", COMMAND_PLACE_WALL);
printf(" Place Door : %c (NOTE: only works if placed between two walls\n", COMMAND_PLACE_DOOR);
printf(" Place Camp : %c\n\n", COMMAND_MAKE_CAMP);
printf(" Remove Wall/Door : %c + (direction command). For example \"%c%c\" will remove\n"
" a wall that is above the player\n",
COMMAND_REMOVE_WALL, COMMAND_REMOVE_WALL, COMMAND_MOVE_UP);
printf("================================= Tile Types ===============================\n");
printf("Wall : %c", TILE_WALL);
printf(" Door : %c\n", TILE_DOOR);
printf("Water : %c", TILE_WATER);
printf(" Berry : %c\n", TILE_BERRY);
printf("Log : %c", TILE_LOG);
printf(" Stone : %c\n", TILE_STONE);
printf("Camp : %c", TILE_CAMP);
printf(" Fire : %c\n", TILE_FIRE);
printf("\n");
printf("===================================== Tips ====================================\n");
printf("Keep a close eye on your hunger stat. If it exceeds %d you will start taking\n",
HUNGER_DAMAGE_THRESHOLD);
printf("damage each turn. Collect and eat berrie to survive. Eating also heals. \n\n");
printf("Be careful about treading through water tiles. If you go 3 turns while wet\n");
printf("you will get cold and take damage each turn. You can enter your camp to get dry and warm\n\n");
printf("Watch out for wildfires! Fire will not spread to water, stone, wall or door tiles.\n");
printf("Your camp is not fireproof so it's a good idea to protect it. You can only build one camp!");
printf("Your camp has a few benefits. It will dry you off and warm, it will put out a fire"
"if you are burning alive, and it allows you to heal. \n");
printf("But you must not be hungry in order to use camp heal(eating also heals).\n\n");
printf("================================= Building Info ==============================\n");
printf("4 Logs to build a camp\n");
printf("6 Logs to build a door\n\n");
printf("1 stone to build a wall\n");
printf("==============================================================================\n");
}
int
main(void)
{
player_t player = {};
player.state = ps_happy;
player.health = 100;
player.hunger = 20;
player.position = 88;
camp_t camp = {};
camp.state = 0;
camp.health = 200;
camp.position = CAMP_DEAD_POSITION;
char *playerStateStrings[ps_count] =
{
"On Fire", "Injured" , "Hungry",
"Tired" , "Happy" , "Wet" ,
"Cold" , "Has Camp", "At camp",
};
char *campStateStrings[cs_count] =
{
"On Fire", "Damaged", "Wet"
};
int32_t actualTileDimX = _mapDimX + 1;
int32_t actualTileDimY = _mapDimY;
int32_t tilesInnerDimX = actualTileDimX - 3;
int32_t tilesInnerDimY = actualTileDimY - 2;
//NOTE: Each tile type has its own array of characters that is
// the size of the inner map(everything inside the boundary).
// This is not optimal at all but it is the easiest for prototyping.
// Performance will only matter if we try to use a big map with lots
// of tile types anyways.
ptrdiff_t mapSize = str_len(originalMap);
char *map = create_tile_set(originalMap, mapSize+1);
char *fireTiles = create_tile_set(originalMap, mapSize+1);
char *waterTiles = create_tile_set(originalMap, mapSize+1);
char *logTiles = create_tile_set(originalMap, mapSize+1);
char *stoneTiles = create_tile_set(originalMap, mapSize+1);
char *berryTiles = create_tile_set(originalMap, mapSize+1);
char *wallTiles = create_tile_set(originalMap, mapSize+1);
srand(time(NULL));
crappy_random_tile_gen(waterTiles, TILE_WATER, 10, tilesInnerDimX, tilesInnerDimY);
crappy_random_tile_gen(logTiles, TILE_LOG, 15, tilesInnerDimX, tilesInnerDimY);
crappy_random_tile_gen(stoneTiles, TILE_STONE, 25, tilesInnerDimX, tilesInnerDimY);
crappy_random_tile_gen(berryTiles, TILE_BERRY, 25, tilesInnerDimX, tilesInnerDimY);
destroy_overlapped_tiles(stoneTiles, logTiles, tilesInnerDimX, tilesInnerDimY);
int32_t fireSpreadDelay = 2;
int32_t fireSpreadTimer = 0;
int32_t fireDamage = 20;
bool32_t fireShouldSpread = false;
ptrdiff_t fireTimersSize = (tilesInnerDimY * tilesInnerDimX) * sizeof(int32_t);
int32_t *fireTimers = (int32_t *)malloc(fireTimersSize);
memset(fireTimers, 0, fireTimersSize);
bool32_t shouldQuit = false;
int32_t turnsLasted = 0;
int32_t turnsSpentWet = 0;
int32_t turnsSpentWetUntilColdness = 4;
int32_t fireGracePeriod = 64;
int32_t fireGraceCounter = 0;
bool32 firstTurn = true;
entered_commands command = {};
while(command.primary != 'q' && !shouldQuit)
{
if(!firstTurn)
{
command = get_next_command();
}
if(command.primary == COMMAND_HELP)
{
system("cls");
display_help();
printf("Press enter to continue...\r\n\r\n");
fflush(stdin);
//NOTE: May only work on windows
getch();
system("cls");
draw_game(&player, &camp, map, playerStateStrings, array_count(playerStateStrings),
campStateStrings, array_count(campStateStrings), turnsLasted);
continue;
}
spawn_object_random_chance(berryTiles, tilesInnerDimX, tilesInnerDimY, TILE_BERRY);
{
int32_t w = actualTileDimX;
int32_t h = actualTileDimY;
int32_t iw = tilesInnerDimX;
int32_t ih = tilesInnerDimY;
int32_t hMove = 1;
int32_t vMove = actualTileDimX;
int32_t mapSize = actualTileDimX * actualTileDimY;
player_t *p = &player;
switch(command.primary)
{
case COMMAND_MOVE_UP : { p->position = try_move(p->position, wallTiles, w, h, -vMove); } break;
case COMMAND_MOVE_DOWN : { p->position = try_move(p->position, wallTiles, w, h, vMove); } break;
case COMMAND_MOVE_LEFT : { p->position = try_move(p->position, wallTiles, w, h, -hMove); } break;
case COMMAND_MOVE_RIGHT : { p->position = try_move(p->position, wallTiles, w, h, hMove); } break;
case COMMAND_HEAL : { p->health = try_heal(p, MAX_STAT_VALUE); } break;
case COMMAND_EAT : { p->hunger = try_eat(p); } break;
case COMMAND_MAKE_CAMP : { build_camp(p, &camp); } break;
case COMMAND_PLACE_WALL : { p->stones = try_place_wall(p->stones, p->position, wallTiles, mapSize); } break;
case COMMAND_PLACE_DOOR : { p->logs = try_place_door(p->logs, p->position, wallTiles, w, h); } break;
case COMMAND_REMOVE_WALL:
{
int32_t removePos = 0;
switch(command.secondary)
{
case COMMAND_MOVE_UP : { removePos = p->position + -w; } break;
case COMMAND_MOVE_DOWN : { removePos = p->position + w; } break;
case COMMAND_MOVE_LEFT : { removePos = p->position + -1; } break;
case COMMAND_MOVE_RIGHT : { removePos = p->position + 1; } break;
}
try_remove_wall(p, wallTiles, removePos, w, h);
} break;
}
}
//NOTE: Burn resources that overlap with active fire tiles
destroy_overlapped_tiles(fireTiles, logTiles, tilesInnerDimX, tilesInnerDimY);
destroy_overlapped_tiles(fireTiles, berryTiles, tilesInnerDimX, tilesInnerDimY);
//NOTE: Reset then update map
copy_inner_tiles(originalMap, map, actualTileDimX, actualTileDimY);
copy_inner_tiles(berryTiles, map, actualTileDimX, actualTileDimY, TILE_BLANK);
copy_inner_tiles(logTiles, map, actualTileDimX, actualTileDimY, TILE_BLANK);
copy_inner_tiles(stoneTiles, map, actualTileDimX, actualTileDimY, TILE_BLANK);
copy_inner_tiles(fireTiles, map, actualTileDimX, actualTileDimY, TILE_BLANK);
copy_inner_tiles(waterTiles, map, actualTileDimX, actualTileDimY, TILE_BLANK);
copy_inner_tiles(wallTiles, map, actualTileDimX, actualTileDimY, TILE_BLANK);
assert(player.position >= 0 && player.position < mapSize);
assert(camp.position >= 0 && camp.position < mapSize);
switch(map[camp.position])
{
case TILE_FIRE : { cs_set_on_fire(camp.state); } break;
case TILE_WATER : { cs_set_wet(camp.state); } break;
}
if(camp.position != CAMP_DEAD_POSITION) map[camp.position] = TILE_CAMP;
//NOTE: check if player is on a non-blank tile and perform any necessary
// changes to the player's state flags. This should be done before
// writing the player's position to the map so as to avoid overwritting
// the special tile.
ps_leave_camp(player.state);
switch(map[player.position])
{
case TILE_FIRE : { ps_set_on_fire(player.state); } break;
case TILE_WATER : { ps_set_wet(player.state); } break;
case TILE_CAMP : { ps_enter_camp(player.state); } break;
case TILE_LOG : { take_resource(player, logTiles, logs); } break;
case TILE_STONE : { take_resource(player, stoneTiles, stones); } break;
case TILE_BERRY : { take_resource(player, berryTiles, berries); } break;
}
if(camp.position != CAMP_DEAD_POSITION)
{
if(camp.state & cs_onFire) camp.health = take_damage(camp.health, fireDamage);
}
if(camp.position != CAMP_DEAD_POSITION) map[camp.position] = TILE_CAMP;
map[player.position] = playerChar;
if(camp.health <= 0 && camp.position != CAMP_DEAD_POSITION)
{
camp.state = 0;
camp.position = CAMP_DEAD_POSITION;
player.state &= ~ps_hasCamp;
printf("You camp was destroyed!\n\n");
}
if(player.state & ps_onFire)
{
player.health = take_damage(player.health, fireDamage);
}
player.hunger = clamp(player.hunger + HUNGER_INCREMENT, 0, MAX_STAT_VALUE);
if(player.hunger >= HUNGER_DAMAGE_THRESHOLD)
{
player.health = take_damage(player.health, HUNGER_DAMAGE);
player.state |= ps_hungry;
}
else
{
player.state &= ~ps_hungry;
}
player.state = try_happiness(player.state);
if(fireGraceCounter >= fireGracePeriod)
{
if(rand() % 45 == 1 && !fire_is_active(fireTiles, _actualMapSize))
{
int32_t randIndex = generate_random_spawn_index(tilesInnerDimX, tilesInnerDimY);
if(waterTiles[randIndex] != TILE_WATER) fireTiles[randIndex] = TILE_FIRE;
fireShouldSpread = true;
}
}
else
{
++fireGraceCounter;
fireShouldSpread = false;
}
update_fire_lives(fireTiles, fireTimers, tilesInnerDimX, tilesInnerDimY);
if(fireShouldSpread)
{
if(fireSpreadTimer++ >= fireSpreadDelay)
{
fire_spread(fireTiles, waterTiles, stoneTiles, wallTiles, fireTimers, actualTileDimX, actualTileDimY);
fireSpreadTimer = 0;
}
}
if(player.state & ps_wet)
{
++turnsSpentWet;
if(turnsSpentWet >= turnsSpentWetUntilColdness) player.state |= ps_cold;
}
else
{
player.state &= ~ps_cold;
}
if(player.state & (ps_atCamp | ps_onFire)) player.state &= ~ps_cold;
if(player.state & ps_atCamp) player.state &= ~ps_wet;
//NOTE: order is important. We want to wait until all flags that negate
// ps_cold are processed before damaging the player.
if(player.state & ps_cold)
{
player.health = take_damage(player.health, COLD_DAMAGE);
}
uint32_t injuryFlags = ps_onFire;
if(player.state & injuryFlags || player.health <= 50)
player.state |= ps_injured;
else
player.state &= ~ps_injured;
//player.health = 100;
system("cls");
draw_game(&player, &camp, map, playerStateStrings, array_count(playerStateStrings),
campStateStrings, array_count(campStateStrings), turnsLasted);
++turnsLasted;
if(player.health <= 0)
{
printf("!==========================================!\n");
printf("Oh no, you died!\n");
printf("!==========================================!\n");
getch();
shouldQuit = true;
}
firstTurn = false;
}
free(fireTiles);
free(fireTimers);
free(waterTiles);
free(logTiles);
free(stoneTiles);
free(berryTiles);
free(wallTiles);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment