moved code here. still needs to update gh
This commit is contained in:
24
terrarium_bk/terrarium/Makefile
Normal file
24
terrarium_bk/terrarium/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c11
|
||||
|
||||
# The final executable name
|
||||
TARGET = terrarium
|
||||
|
||||
# Source files
|
||||
SRCS = main.c game.c ascii_plant_data.c
|
||||
|
||||
# Object files
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) *.o
|
||||
rm -f terrarium_save.dat
|
||||
|
||||
581
terrarium_bk/terrarium/ascii_plant_data.c
Normal file
581
terrarium_bk/terrarium/ascii_plant_data.c
Normal file
@@ -0,0 +1,581 @@
|
||||
/******************************************************************************
|
||||
* ascii_plant_data.c
|
||||
*
|
||||
* Defines 40 unique ASCII plants, each 10 lines of up to 20 chars.
|
||||
*
|
||||
* For demonstration, they’ll all be similar in shape, but each one will have
|
||||
* some small text or symbol changes so that no two are exactly alike.
|
||||
* You can replace these with more distinct art as desired.
|
||||
******************************************************************************/
|
||||
|
||||
#include "ascii_plant_data.h"
|
||||
|
||||
/*
|
||||
* We store all 40 plants’ ASCII lines in a 2D array of strings:
|
||||
* PLANTS[40][10]
|
||||
* where each string is up to 20 chars + null terminator.
|
||||
*
|
||||
* Each plant: 10 lines. Each line up to 20 chars wide.
|
||||
*/
|
||||
|
||||
/*
|
||||
Template shape (10 lines, 20 chars each):
|
||||
" . "
|
||||
" .. # "
|
||||
" . ==+*- . - "
|
||||
" :++*====.# "
|
||||
" ==++-=#*+ "
|
||||
" -%*=-#+#. "
|
||||
" .-+**#. "
|
||||
" .**+#. "
|
||||
" .:=*++ "
|
||||
" :--=. "
|
||||
We’ll introduce slight variations for each plant to ensure uniqueness.
|
||||
*/
|
||||
|
||||
static const char PLANT_ART[40][10][21] = {
|
||||
/* 0: Snake Plant */
|
||||
{
|
||||
" . ",
|
||||
" .. # ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# ",
|
||||
" S==++-=#*+ ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. S ",
|
||||
" .**+#. ",
|
||||
" .:=*++ S ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 1: Aloe Vera */
|
||||
{
|
||||
" . ",
|
||||
" .. A ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# A ",
|
||||
" ==++-=#*+ ",
|
||||
" A -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" .**+#. A ",
|
||||
" .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 2: Rubber fig */
|
||||
{
|
||||
" (Rubber) ",
|
||||
" .. # ",
|
||||
" R . ==+*- . - ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ R ",
|
||||
" -%*=-#+#. ",
|
||||
" R .-+**#. ",
|
||||
" .**+#. R ",
|
||||
" .:=*++ ",
|
||||
" :--=. R "
|
||||
},
|
||||
/* 3: Maidenhair fern */
|
||||
{
|
||||
" M ",
|
||||
" fern .. # ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# M ",
|
||||
" ==++-=#*+ f ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" .**+#. fern ",
|
||||
" .:=*++ M ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 4: Zebra Plant */
|
||||
{
|
||||
" Z e b r a ",
|
||||
" .. Z ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# Z ",
|
||||
" Z ==++-=#*+ ",
|
||||
" -%*=-#+#.Z ",
|
||||
" .-+**#. ",
|
||||
" .**+#. ",
|
||||
" Z .:=*++ ",
|
||||
" :--=. Z "
|
||||
},
|
||||
|
||||
/* 5: Jade plant */
|
||||
{
|
||||
" J ",
|
||||
" .. # ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# J ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. ",
|
||||
" Jade.-+**#. ",
|
||||
" .**+#. ",
|
||||
" pl. :=*++ ",
|
||||
" :--=. Jade "
|
||||
},
|
||||
/* 6: Yucca */
|
||||
{
|
||||
" Yucca ",
|
||||
" .. Y ",
|
||||
" . ==+*- . - Y ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ Y ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. Y ",
|
||||
" .**+#. ",
|
||||
" .:=*++ Y ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 7: Chain of Hearts */
|
||||
{
|
||||
" Chain <3 Hearts ",
|
||||
" .. # ",
|
||||
" . ==+*- . - <3 ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ <3 ",
|
||||
" <3 -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" .**+#. <3 ",
|
||||
" Chain.:=*++ ",
|
||||
" :--=. Hearts"
|
||||
},
|
||||
/* 8: ZZ plant */
|
||||
{
|
||||
" ZZZ ",
|
||||
" .. # Z ",
|
||||
" . ==+*- . - ",
|
||||
" Z :++*====.# ",
|
||||
" ==++-=#*+ ",
|
||||
" Z -%*=-#+#. ",
|
||||
" .-+**#. Z ",
|
||||
" ZZ .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. ZZ "
|
||||
},
|
||||
/* 9: Moonstones */
|
||||
{
|
||||
" (Moon) ",
|
||||
" .. # ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# M ",
|
||||
" ==++-=#*+ ",
|
||||
" oo -%*=-#+#. ",
|
||||
" .-+**#. oo ",
|
||||
" .**+#. ",
|
||||
" stones:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
|
||||
/* 10: Chinese Money plant */
|
||||
{
|
||||
" Chinese Money ",
|
||||
" .. # $ ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# $ ",
|
||||
" ==++-=#*+ $$ ",
|
||||
" $$ -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" $$ .**+#. ",
|
||||
" .:=*++ $$ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 11: String of pearls */
|
||||
{
|
||||
" String pearls ",
|
||||
" ... # o o ",
|
||||
" . ==+*- . - o ",
|
||||
" :++*====.# o ",
|
||||
" ==++-=#*+ o ",
|
||||
" -%*=-#+#. o ",
|
||||
" .-+**#. o ",
|
||||
" .**+#. o ",
|
||||
" .:=*++ o ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 12: Air plant */
|
||||
{
|
||||
" Air plant ",
|
||||
" .. # ~ ",
|
||||
" . ==+*- . - ~ ",
|
||||
" :++*====.# ",
|
||||
" A ==++-=#*+ ~ ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ~ ",
|
||||
" ~ .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 13: African milk tree */
|
||||
{
|
||||
" African milk tree ",
|
||||
" AMT .. # ",
|
||||
" . ==+*- . - AMT ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ AMT ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. AMT ",
|
||||
" .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 14: pine bonsai */
|
||||
{
|
||||
" pine bonsai ",
|
||||
" .. # ^ ",
|
||||
" . ==+*- . - ^ ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ^ ",
|
||||
" -%*=-#+#. ",
|
||||
" ^ .-+**#. ",
|
||||
" .**+#. ^ ",
|
||||
" ^ .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
|
||||
/* 15: Lotus */
|
||||
{
|
||||
" Lotus ",
|
||||
" .. # * ",
|
||||
" . ==+*- . - * ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ * ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" * .**+#. ",
|
||||
" .:=*++ * ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 16: Heart fern */
|
||||
{
|
||||
" Heart fern <3 ",
|
||||
" .. # ",
|
||||
" . ==+*- . - <3 ",
|
||||
" :++*====.# <3 ",
|
||||
" ==++-=#*+ ",
|
||||
" <3 -%*=-#+#. ",
|
||||
" .-+**#. <3 ",
|
||||
" .**+#. ",
|
||||
" <3 .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 17: Corkscrew rush */
|
||||
{
|
||||
" Corkscrew rush ~@ ",
|
||||
" .. # ~@ ",
|
||||
" . ==+*- . - ~@ ",
|
||||
" :++*====.# ",
|
||||
" ~@==++-=#*+ ",
|
||||
" -%*=-#+#. ~@ ",
|
||||
" .-+**#. ",
|
||||
" .**+#. ~@ ",
|
||||
" .:=*++ ",
|
||||
" :--=. ~@ "
|
||||
},
|
||||
/* 18: Weeping fig */
|
||||
{
|
||||
" Weeping fig ~ ",
|
||||
" .. # ~ ",
|
||||
" . ==+*- . - ",
|
||||
" ~ :++*====.# ",
|
||||
" ==++-=#*+ ~ ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ~ ",
|
||||
" .**+#. ",
|
||||
" ~ .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 19: Corkscrew albuca */
|
||||
{
|
||||
" Corkscrew albuca ",
|
||||
" Cka .. # ~@ ",
|
||||
" . ==+*- . - ~@ ",
|
||||
" :++*====.# ",
|
||||
" ~@==++-=#*+ ",
|
||||
" -%*=-#+#. ~@ ",
|
||||
" .-+**#. ",
|
||||
" ~@ .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. ~@ "
|
||||
},
|
||||
|
||||
/* 20: Fiddle leaf fig */
|
||||
{
|
||||
" Fiddle leaf fig F ",
|
||||
" .. # (leaf) ",
|
||||
" ==+*- . - F ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ (leaf) ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. F ",
|
||||
" .**+#. ",
|
||||
" .:=*++ (leaf) ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 21: Mikado */
|
||||
{
|
||||
" Mikado | ",
|
||||
" .. # | ",
|
||||
" . ==+*- . - | ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ | ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" .**+#. | ",
|
||||
" .:=*++ ",
|
||||
" :--=. | "
|
||||
},
|
||||
/* 22: Kebab bush */
|
||||
{
|
||||
" Kebab bush ~o ",
|
||||
" .. # o ",
|
||||
" . ==+*- . - o ",
|
||||
" :++*====.# ~ ",
|
||||
" ~ ==++-=#*+ o ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ~ o ",
|
||||
" .**+#. ",
|
||||
" o .:=*++ ~ ",
|
||||
" :--=. o "
|
||||
},
|
||||
/* 23: Dwarf Papyrus */
|
||||
{
|
||||
" Dwarf Papyrus ^ ",
|
||||
" .. # ^ ",
|
||||
" . ==+*- . - ^ ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ^ ",
|
||||
" ^ -%*=-#+#. ",
|
||||
" .-+**#. ^ ",
|
||||
" .**+#. ",
|
||||
" ^ .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 24: Hobbit Crassula */
|
||||
{
|
||||
" Hobbit Crassula (H) ",
|
||||
" .. # ",
|
||||
" . ==+*- . - (H) ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. (H) ",
|
||||
" .-+**#. ",
|
||||
" (H) .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. (H) "
|
||||
},
|
||||
|
||||
/* 25: Bunny ear cactus */
|
||||
{
|
||||
" Bunny ear cactus * ",
|
||||
" .. # (ears) ",
|
||||
" . ==+*- . - * ",
|
||||
" :++*====.# (ears) ",
|
||||
" ==++-=#*+ * ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. (ears) ",
|
||||
" .**+#. * ",
|
||||
" .:=*++ ",
|
||||
" :--=. * "
|
||||
},
|
||||
/* 26: ghost echoversia */
|
||||
{
|
||||
" ghost echoversia # ",
|
||||
" g.. # # ",
|
||||
" . ==+*- . - # ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ # ",
|
||||
" -%*=-#+#. # ",
|
||||
" .-+**#. # ",
|
||||
" .**+#. # ",
|
||||
" .:=*++ # ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 27: chinese lantern */
|
||||
{
|
||||
" chinese lantern ",
|
||||
" .. # (lantern) ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# (lamp) ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. (lamp) ",
|
||||
" .-+**#. ",
|
||||
" .**+#. (lantern) ",
|
||||
" .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 28: ginseng ficus */
|
||||
{
|
||||
" ginseng ficus Gf ",
|
||||
" .. # Gf ",
|
||||
" . ==+*- . - Gf ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. Gf ",
|
||||
" .-+**#. ",
|
||||
" .**+#. Gf ",
|
||||
" .:=*++ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 29: venus flytrap */
|
||||
{
|
||||
" venus flytrap V ",
|
||||
" .. # x ",
|
||||
" . ==+*- . - x ",
|
||||
" :++*====.# ",
|
||||
" V ==++-=#*+ x ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. x ",
|
||||
" V .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. x "
|
||||
},
|
||||
|
||||
/* 30: Flamingo flower */
|
||||
{
|
||||
" Flamingo flower F ",
|
||||
" .. # (fl) ",
|
||||
" . ==+*- . - F ",
|
||||
" :++*====.# ",
|
||||
" (fl)==++-=#*+ F ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. (fl) ",
|
||||
" .**+#. ",
|
||||
" F .:=*++ ",
|
||||
" :--=. (fl) "
|
||||
},
|
||||
/* 31: Japanese maple bonsai */
|
||||
{
|
||||
" Japanese maple bns ",
|
||||
" .. # Jmb ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# Jmb ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. ",
|
||||
" Jmb .-+**#. ",
|
||||
" .**+#. ",
|
||||
" .:=*++ Jmb ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 32: Fshbone cactus */
|
||||
{
|
||||
" Fshbone cactus ~^ ",
|
||||
" .. # ~^ ",
|
||||
" . ==+*- . - ~^ ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ~^ ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ~^ ",
|
||||
" .**+#. ",
|
||||
" .:=*++ ~^ ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 33: Paddle plant */
|
||||
{
|
||||
" Paddle plant ",
|
||||
" .. # == ",
|
||||
" . ==+*- . - == ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ == ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. ",
|
||||
" == .**+#. ",
|
||||
" .:=*++ == ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 34: Donkey's tail */
|
||||
{
|
||||
" Donkey's tail ~ ",
|
||||
" .. # ~ ",
|
||||
" . ==+*- . - ~ ",
|
||||
" :++*====.# ",
|
||||
" ~ ==++-=#*+ ",
|
||||
" -%*=-#+#. ~ ",
|
||||
" .-+**#. ",
|
||||
" ~ .**+#. ",
|
||||
" .:=*++ ",
|
||||
" :--=. ~ "
|
||||
},
|
||||
|
||||
/* 35: Common ivy */
|
||||
{
|
||||
" Common ivy ivy ",
|
||||
" .. # iv ",
|
||||
" . ==+*- . - iv ",
|
||||
" iv:++*====.# ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. iv ",
|
||||
" .-+**#. ",
|
||||
" .**+#. ",
|
||||
" iv .:=*++ ",
|
||||
" :--=. ivy "
|
||||
},
|
||||
/* 36: Chinese Crassula */
|
||||
{
|
||||
" Chinese Crassula C ",
|
||||
" .. # (Cr) ",
|
||||
" . ==+*- . - (Cr) ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. (Cr) ",
|
||||
" .-+**#. ",
|
||||
" .**+#. (Cr) ",
|
||||
" .:=*++ ",
|
||||
" :--=. (Cr) "
|
||||
},
|
||||
/* 37: Blue Chalksticks */
|
||||
{
|
||||
" Blue Chalksticks B ",
|
||||
" .. # Bc ",
|
||||
" . ==+*- . - Bc ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ Bc ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. Bc ",
|
||||
" .**+#. ",
|
||||
" .:=*++ Bc ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 38: Angel's tears */
|
||||
{
|
||||
" Angel's tears @A ",
|
||||
" .. # tears ",
|
||||
" . ==+*- . - @A ",
|
||||
" :++*====.# ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. @A ",
|
||||
" .-+**#. ",
|
||||
" .**+#. tears ",
|
||||
" .:=*++ @A ",
|
||||
" :--=. "
|
||||
},
|
||||
/* 39: White clover */
|
||||
{
|
||||
" White clover (wc) ",
|
||||
" .. # (wc) ",
|
||||
" . ==+*- . - ",
|
||||
" :++*====.# (wc) ",
|
||||
" ==++-=#*+ ",
|
||||
" -%*=-#+#. ",
|
||||
" .-+**#. (wc) ",
|
||||
" .**+#. ",
|
||||
" .:=*++ (wc)",
|
||||
" :--=. "
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* getPlantArt
|
||||
*
|
||||
* Returns the (line)-th line of ASCII art for plantIndex
|
||||
******************************************************************************/
|
||||
const char* getPlantArt(int plantIndex, int line) {
|
||||
if(plantIndex < 0 || plantIndex >= 40) {
|
||||
/* Safety: return empty string if out of range */
|
||||
return " ";
|
||||
}
|
||||
if(line < 0 || line >= 10) {
|
||||
return " ";
|
||||
}
|
||||
return PLANT_ART[plantIndex][line];
|
||||
}
|
||||
|
||||
14
terrarium_bk/terrarium/ascii_plant_data.h
Normal file
14
terrarium_bk/terrarium/ascii_plant_data.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/******************************************************************************
|
||||
* ascii_plant_data.h
|
||||
*
|
||||
* Exposes function(s) to retrieve the 10-line ASCII art for each of the 40 plants.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef ASCII_PLANT_DATA_H
|
||||
#define ASCII_PLANT_DATA_H
|
||||
|
||||
/* Returns the line-th line (0..9) of ASCII art for plant index (0..39). */
|
||||
const char* getPlantArt(int plantIndex, int line);
|
||||
|
||||
#endif
|
||||
|
||||
BIN
terrarium_bk/terrarium/ascii_plant_data.o
Normal file
BIN
terrarium_bk/terrarium/ascii_plant_data.o
Normal file
Binary file not shown.
259
terrarium_bk/terrarium/game.c
Normal file
259
terrarium_bk/terrarium/game.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/******************************************************************************
|
||||
* game.c
|
||||
*
|
||||
* Implements core game logic: initialization, update, rendering, input, etc.
|
||||
******************************************************************************/
|
||||
/*
|
||||
* game.c - Contains the implementation of the Terrarium game logic
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include "game.h"
|
||||
|
||||
/* System / standard headers */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* for system() */
|
||||
#include <time.h> /* for clock_gettime, etc. */
|
||||
#include <unistd.h> /* for usleep() */
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <string.h> /* for strncpy() */
|
||||
|
||||
/******************************************************************************
|
||||
* Terminal-handling variables/functions (non-blocking input, etc.)
|
||||
*****************************************************************************/
|
||||
static struct termios old, current;
|
||||
|
||||
/* Initialize new terminal i/o settings */
|
||||
void initTermios(int echo) {
|
||||
tcgetattr(0, &old);
|
||||
current = old;
|
||||
current.c_lflag &= ~ICANON; /* disable buffered i/o */
|
||||
if (echo) {
|
||||
current.c_lflag |= ECHO;
|
||||
} else {
|
||||
current.c_lflag &= ~ECHO;
|
||||
}
|
||||
tcsetattr(0, TCSANOW, ¤t);
|
||||
}
|
||||
|
||||
/* Restore old terminal i/o settings */
|
||||
void resetTermios(void) {
|
||||
tcsetattr(0, TCSANOW, &old);
|
||||
}
|
||||
|
||||
/* Check if a key has been pressed (non-blocking) */
|
||||
int kbhit(void) {
|
||||
struct termios oldt, newt;
|
||||
int ch;
|
||||
int oldf;
|
||||
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
|
||||
|
||||
ch = getchar();
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf);
|
||||
|
||||
if(ch != EOF) {
|
||||
ungetc(ch, stdin);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read one character (like getch in Windows) */
|
||||
char getch(void) {
|
||||
return getchar();
|
||||
}
|
||||
|
||||
/* Clear the screen (Unix-like) */
|
||||
void clearScreen(void) {
|
||||
system("clear");
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Elapsed Time (we store the old time in a static variable)
|
||||
*****************************************************************************/
|
||||
double getElapsedTime(void) {
|
||||
static struct timespec lastTime = {0, 0};
|
||||
struct timespec now;
|
||||
|
||||
/* If first call, initialize lastTime */
|
||||
if (lastTime.tv_sec == 0 && lastTime.tv_nsec == 0) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
lastTime = now;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* current time */
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
double elapsed = (now.tv_sec - lastTime.tv_sec) +
|
||||
(now.tv_nsec - lastTime.tv_nsec) / 1000000000.0;
|
||||
lastTime = now;
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Game Logic & Rendering
|
||||
*****************************************************************************/
|
||||
|
||||
/* Forward-declarations (private to this file) */
|
||||
static double calcTotalProduction(const GameState *gs);
|
||||
static void buyPlant(GameState *gs);
|
||||
|
||||
/* initGame: brand-new game (if load fails or for fresh start) */
|
||||
void initGame(GameState *gs) {
|
||||
gs->oxygen = 0.0;
|
||||
gs->plantCost = BASE_PLANT_COST;
|
||||
gs->plantCount = 0;
|
||||
|
||||
/* The 40 plants (names). Adjust as you wish. */
|
||||
const char* plantNames[MAX_PLANTS] = {
|
||||
"Snake Plant", "Aloe Vera", "Rubber fig", "Maidenhair fern", "Zebra Plant",
|
||||
"Jade plant", "Yucca", "Chain of Hearts", "ZZ plant", "Moonstones",
|
||||
"Chinese Money plant", "String of pearls.", "Air plant", "African milk tree",
|
||||
"pine bonsai", "Lotus", "Heart fern", "Corkscrew rush", "Weeping fig", "Corkscrew albuca",
|
||||
"Fiddle leaf fig", "Mikado", "Kebab bush", "Dwarf Papyrus", "Hobbit Crassula",
|
||||
"Bunny ear cactus", "ghost echoversia", "chinese lantern", "ginseng ficus", "venus flytrap",
|
||||
"Flamingo flower", "Japanese maple bonsai", "Fshbone cactus", "Paddle plant",
|
||||
"Donkey's tail", "Common ivy", "Chinese Crassula", "Blue Chalksticks", "Angel's tears", "White clover"
|
||||
};
|
||||
|
||||
for(int i = 0; i < MAX_PLANTS; i++) {
|
||||
strncpy(gs->plants[i].name, plantNames[i], sizeof(gs->plants[i].name) - 1);
|
||||
gs->plants[i].name[sizeof(gs->plants[i].name)-1] = '\0';
|
||||
gs->plants[i].active = 0;
|
||||
gs->plants[i].productionRate = 0.0;
|
||||
}
|
||||
|
||||
/* Start with the first plant active, if desired */
|
||||
gs->plants[0].active = 1;
|
||||
gs->plants[0].productionRate = BASE_OXYGEN_PER_SECOND;
|
||||
gs->plantCount = 1;
|
||||
}
|
||||
|
||||
/* loadGame: tries to read from a file. If it fails, calls initGame(). */
|
||||
void loadGame(GameState *gs) {
|
||||
FILE *f = fopen("terrarium_save.dat", "rb");
|
||||
if(!f) {
|
||||
initGame(gs);
|
||||
return;
|
||||
}
|
||||
if(fread(gs, sizeof(GameState), 1, f) != 1) {
|
||||
fclose(f);
|
||||
initGame(gs);
|
||||
return;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
/* Basic sanity check */
|
||||
if(gs->plantCount < 1) {
|
||||
initGame(gs);
|
||||
}
|
||||
}
|
||||
|
||||
/* saveGame: writes the current game state to a file. */
|
||||
void saveGame(const GameState *gs) {
|
||||
FILE *f = fopen("terrarium_save.dat", "wb");
|
||||
if(!f) return;
|
||||
fwrite(gs, sizeof(GameState), 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* updateGame: called each loop to add oxygen from production rate. */
|
||||
void updateGame(GameState *gs, double elapsedSeconds) {
|
||||
double totalProd = calcTotalProduction(gs);
|
||||
gs->oxygen += totalProd * elapsedSeconds;
|
||||
}
|
||||
|
||||
/* renderGame: draws shelves + plants in top 3/4, info in bottom 1/4. */
|
||||
void renderGame(const GameState *gs) {
|
||||
clearScreen();
|
||||
|
||||
printf("========================================\n");
|
||||
printf(" T E R R A R I U M \n");
|
||||
printf("========================================\n\n");
|
||||
|
||||
/*
|
||||
* We have 5 shelves (SHELF_COUNT=5).
|
||||
* Each shelf displays 8 plants => total 40 plants (PLANTS_PER_SHELF=8).
|
||||
*/
|
||||
int plantIdx = 0;
|
||||
for(int shelf = 0; shelf < SHELF_COUNT; shelf++) {
|
||||
for(int slot = 0; slot < PLANTS_PER_SHELF; slot++) {
|
||||
if(plantIdx < MAX_PLANTS && gs->plants[plantIdx].active) {
|
||||
/* Print the plant name (20 chars padded, e.g.) */
|
||||
printf("[%2d] %-20s | ", plantIdx+1, gs->plants[plantIdx].name);
|
||||
} else {
|
||||
printf("[ ] %-20s | ", "(empty)");
|
||||
}
|
||||
plantIdx++;
|
||||
}
|
||||
printf("\n--------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
/* BOTTOM 1/4: info, oxygen, etc. */
|
||||
printf("\nOxygen: %.2f O2\n", gs->oxygen);
|
||||
printf("Total Production: %.3f O2/s\n", calcTotalProduction(gs));
|
||||
printf("Next Plant Cost: %.2f O2\n\n", gs->plantCost);
|
||||
|
||||
printf("[SPACE] Tap the first plant for +%.1f O2 (if active)\n", TAP_BONUS_OXYGEN);
|
||||
printf("[b] Buy a new plant\n");
|
||||
printf("[q] Quit\n\n");
|
||||
}
|
||||
|
||||
/* handleInput: user keystrokes (tap or buy, etc.) */
|
||||
void handleInput(GameState *gs, char input) {
|
||||
switch(input) {
|
||||
case ' ':
|
||||
/* Tap only works on the first plant if active */
|
||||
if(gs->plants[0].active) {
|
||||
gs->oxygen += TAP_BONUS_OXYGEN;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
case 'B':
|
||||
buyPlant(gs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal helper: calculates sum of production rates */
|
||||
static double calcTotalProduction(const GameState *gs) {
|
||||
double total = 0.0;
|
||||
for(int i = 0; i < MAX_PLANTS; i++) {
|
||||
if(gs->plants[i].active) {
|
||||
total += gs->plants[i].productionRate;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Internal helper: buy the next plant if we can afford it */
|
||||
static void buyPlant(GameState *gs) {
|
||||
if(gs->plantCount >= MAX_PLANTS) {
|
||||
return; /* can't buy more than 40 */
|
||||
}
|
||||
if(gs->oxygen >= gs->plantCost) {
|
||||
gs->oxygen -= gs->plantCost;
|
||||
/* activate next plant in line */
|
||||
int index = gs->plantCount;
|
||||
gs->plants[index].active = 1;
|
||||
gs->plants[index].productionRate = BASE_OXYGEN_PER_SECOND; /* or custom logic */
|
||||
gs->plantCount++;
|
||||
|
||||
/* Increase cost for next time */
|
||||
gs->plantCost *= COST_MULTIPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
67
terrarium_bk/terrarium/game.h
Normal file
67
terrarium_bk/terrarium/game.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/******************************************************************************
|
||||
* game.h
|
||||
*
|
||||
* Exposes the core game logic and data.
|
||||
******************************************************************************/
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
/*
|
||||
* This header declares functions/data for the Terrarium game.
|
||||
* Include it from both main.c and game.c.
|
||||
*/
|
||||
|
||||
#define MAX_PLANTS 40
|
||||
#define SHELF_COUNT 5 /* We'll display 5 shelves total */
|
||||
#define PLANTS_PER_SHELF 8 /* 8 plants per shelf => 40 plants total */
|
||||
#define GAME_TICK_INTERVAL_MS 200 /* Loop tick (ms) */
|
||||
|
||||
#define BASE_OXYGEN_PER_SECOND 0.1
|
||||
#define TAP_BONUS_OXYGEN 1.0
|
||||
#define BASE_PLANT_COST 10.0
|
||||
#define COST_MULTIPLIER 1.2
|
||||
|
||||
/*
|
||||
* Data Structures
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char name[64]; /* e.g. "Snake Plant" */
|
||||
int active; /* 1 if purchased/visible, 0 if not */
|
||||
double productionRate; /* O2 per second contributed by this plant */
|
||||
} Plant;
|
||||
|
||||
/* Main Game State container */
|
||||
typedef struct {
|
||||
double oxygen; /* Current total oxygen currency */
|
||||
double plantCost; /* Cost to buy the next plant */
|
||||
Plant plants[MAX_PLANTS]; /* All 40 plants */
|
||||
int plantCount; /* How many plants are active */
|
||||
} GameState;
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
|
||||
/* Game lifecycle */
|
||||
void initGame(GameState *gs);
|
||||
void loadGame(GameState *gs);
|
||||
void saveGame(const GameState *gs);
|
||||
|
||||
/* Game logic */
|
||||
void updateGame(GameState *gs, double elapsedSeconds);
|
||||
void renderGame(const GameState *gs);
|
||||
void handleInput(GameState *gs, char input);
|
||||
|
||||
/* Timing: returns elapsed seconds since last call */
|
||||
double getElapsedTime(void);
|
||||
|
||||
/* Terminal / I/O helpers */
|
||||
void initTermios(int echo);
|
||||
void resetTermios(void);
|
||||
int kbhit(void);
|
||||
char getch(void);
|
||||
void clearScreen(void);
|
||||
|
||||
#endif /* GAME_H */
|
||||
|
||||
BIN
terrarium_bk/terrarium/game.o
Normal file
BIN
terrarium_bk/terrarium/game.o
Normal file
Binary file not shown.
57
terrarium_bk/terrarium/main.c
Normal file
57
terrarium_bk/terrarium/main.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* main.c - Entry point of the Terrarium program
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* for system() */
|
||||
#include <unistd.h> /* for usleep() */
|
||||
|
||||
#include "game.h"
|
||||
|
||||
int main(void) {
|
||||
GameState gs;
|
||||
|
||||
/* Load a previous save or start fresh. */
|
||||
loadGame(&gs);
|
||||
|
||||
/* Setup terminal for non-blocking input, no echo */
|
||||
initTermios(0);
|
||||
|
||||
int running = 1;
|
||||
while (running) {
|
||||
/* Time since the last loop in seconds */
|
||||
double elapsedSeconds = getElapsedTime();
|
||||
|
||||
/* Update game logic (oxygen accumulation, etc.) */
|
||||
updateGame(&gs, elapsedSeconds);
|
||||
|
||||
/* Render the shelves and the info area */
|
||||
renderGame(&gs);
|
||||
|
||||
/* Check user input (non-blocking) */
|
||||
if (kbhit()) {
|
||||
char c = getch();
|
||||
if (c == 'q' || c == 'Q') {
|
||||
running = 0; /* quit */
|
||||
} else {
|
||||
handleInput(&gs, c);
|
||||
}
|
||||
}
|
||||
|
||||
/* Control frame/tick rate (~5 times a second) */
|
||||
usleep(GAME_TICK_INTERVAL_MS * 1000);
|
||||
|
||||
/* Auto-save each loop (optional) */
|
||||
saveGame(&gs);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
resetTermios();
|
||||
clearScreen();
|
||||
printf("Thanks for playing Terrarium!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BIN
terrarium_bk/terrarium/main.o
Normal file
BIN
terrarium_bk/terrarium/main.o
Normal file
Binary file not shown.
BIN
terrarium_bk/terrarium/terrarium
Executable file
BIN
terrarium_bk/terrarium/terrarium
Executable file
Binary file not shown.
388
terrarium_bk/terrarium/terrarium.c
Normal file
388
terrarium_bk/terrarium/terrarium.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/******************************************************************************
|
||||
* Terrarium - An ASCII-based terminal idle game in C
|
||||
*
|
||||
* - Top 3/4 of the screen: shelves to visualize plants with their names.
|
||||
* - Bottom 1/4 of the screen: oxygen total, controls, etc.
|
||||
* - Tapping (space bar) works only for the first plant to give bonus oxygen.
|
||||
* - Press 'b' to buy new plants, which appear on the next available shelf.
|
||||
* - Press 'q' to quit.
|
||||
* - Auto-saves and auto-loads from "terrarium_save.dat".
|
||||
*
|
||||
* Compile (Linux/Unix):
|
||||
* gcc terrarium.c -o terrarium -lm
|
||||
*
|
||||
* Run:
|
||||
* ./terrarium
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
/******************************************************************************
|
||||
* Platform/Terminal-Handling Code (Non-Blocking Input, Clear Screen, etc.)
|
||||
*****************************************************************************/
|
||||
|
||||
static struct termios old, current;
|
||||
|
||||
/* Initialize new terminal i/o settings */
|
||||
void initTermios(int echo) {
|
||||
tcgetattr(0, &old); /* grab old terminal i/o settings */
|
||||
current = old;
|
||||
current.c_lflag &= ~ICANON; /* disable buffered i/o */
|
||||
if (echo) {
|
||||
current.c_lflag |= ECHO; /* set echo mode */
|
||||
} else {
|
||||
current.c_lflag &= ~ECHO; /* set no echo mode */
|
||||
}
|
||||
tcsetattr(0, TCSANOW, ¤t); /* apply the new settings */
|
||||
}
|
||||
|
||||
/* Restore old terminal i/o settings */
|
||||
void resetTermios(void) {
|
||||
tcsetattr(0, TCSANOW, &old);
|
||||
}
|
||||
|
||||
/* Check if a key has been pressed (non-blocking) */
|
||||
int kbhit(void) {
|
||||
struct termios oldt, newt;
|
||||
int ch;
|
||||
int oldf;
|
||||
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
|
||||
|
||||
ch = getchar();
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
fcntl(STDIN_FILENO, F_SETFL, oldf);
|
||||
|
||||
if(ch != EOF) {
|
||||
ungetc(ch, stdin);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read one character without echo */
|
||||
char getch() {
|
||||
return getchar();
|
||||
}
|
||||
|
||||
/* Clear the screen (Unix-like) */
|
||||
void clearScreen() {
|
||||
system("clear");
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Game Constants
|
||||
*****************************************************************************/
|
||||
|
||||
#define MAX_PLANTS 10 /* Max number of plants we can display */
|
||||
#define SHELF_COUNT 5 /* How many shelves to visualize */
|
||||
#define GAME_TICK_INTERVAL_MS 200 /* Game loop tick interval in ms */
|
||||
|
||||
#define BASE_OXYGEN_PER_SECOND 0.1
|
||||
#define TAP_BONUS_OXYGEN 1.0
|
||||
#define BASE_PLANT_COST 10.0
|
||||
#define COST_MULTIPLIER 1.2
|
||||
|
||||
/* Save file name */
|
||||
#define SAVE_FILE "terrarium_save.dat"
|
||||
|
||||
/******************************************************************************
|
||||
* Data Structures
|
||||
*****************************************************************************/
|
||||
|
||||
/* Each Plant has a name and a productionRate. We could expand this in the future. */
|
||||
typedef struct {
|
||||
char name[32];
|
||||
int active; /* 1 if this plant is purchased and displayed, 0 otherwise */
|
||||
double productionRate; /* Oxygen production contribution from this plant */
|
||||
} Plant;
|
||||
|
||||
typedef struct {
|
||||
double oxygen; /* Current total oxygen */
|
||||
double plantCost; /* Cost to buy the next plant */
|
||||
Plant plants[MAX_PLANTS]; /* Our plants (0th is the "first" plant that can be tapped) */
|
||||
int plantCount; /* How many plants are active/purchased */
|
||||
} GameState;
|
||||
|
||||
/******************************************************************************
|
||||
* Function Declarations
|
||||
*****************************************************************************/
|
||||
|
||||
void initGame(GameState *gs);
|
||||
void loadGame(GameState *gs);
|
||||
void saveGame(const GameState *gs);
|
||||
|
||||
void updateGame(GameState *gs, double elapsedSeconds);
|
||||
void renderGame(const GameState *gs);
|
||||
|
||||
void handleInput(GameState *gs, char input);
|
||||
void buyPlant(GameState *gs);
|
||||
|
||||
double calcTotalProduction(const GameState *gs);
|
||||
double getElapsedTime(struct timespec *lastTick);
|
||||
|
||||
/******************************************************************************
|
||||
* Main Function
|
||||
*****************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
GameState gs;
|
||||
|
||||
/* Attempt to load a previous save. If no save file is found, initialize. */
|
||||
loadGame(&gs);
|
||||
|
||||
/* Terminal setup for non-blocking input */
|
||||
initTermios(0); // 0 = no echo
|
||||
|
||||
/* Timing for the game loop */
|
||||
struct timespec lastTick;
|
||||
clock_gettime(CLOCK_MONOTONIC, &lastTick);
|
||||
|
||||
int running = 1;
|
||||
while(running) {
|
||||
/* 1) Compute elapsed time */
|
||||
double elapsedSeconds = getElapsedTime(&lastTick);
|
||||
|
||||
/* 2) Update game logic */
|
||||
updateGame(&gs, elapsedSeconds);
|
||||
|
||||
/* 3) Render the game */
|
||||
clearScreen();
|
||||
renderGame(&gs);
|
||||
|
||||
/* 4) Check user input (non-blocking) */
|
||||
if(kbhit()) {
|
||||
char input = getch();
|
||||
if(input == 'q' || input == 'Q') {
|
||||
running = 0;
|
||||
} else {
|
||||
handleInput(&gs, input);
|
||||
}
|
||||
}
|
||||
|
||||
/* 5) Sleep to control game speed */
|
||||
usleep(GAME_TICK_INTERVAL_MS * 1000);
|
||||
|
||||
/* 6) Auto-save occasionally (for example, every loop).
|
||||
* You could also do it less frequently to avoid disk writes. */
|
||||
saveGame(&gs);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
resetTermios();
|
||||
clearScreen();
|
||||
printf("Thanks for playing Terrarium!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Initialize Game State (default if no save is found)
|
||||
*****************************************************************************/
|
||||
void initGame(GameState *gs) {
|
||||
gs->oxygen = 0.0;
|
||||
gs->plantCost = BASE_PLANT_COST;
|
||||
|
||||
/* Initialize all plants as inactive, except the first one. */
|
||||
for(int i = 0; i < MAX_PLANTS; i++){
|
||||
snprintf(gs->plants[i].name, sizeof(gs->plants[i].name), "Plant #%d", i+1);
|
||||
gs->plants[i].active = 0;
|
||||
gs->plants[i].productionRate = 0.0;
|
||||
}
|
||||
|
||||
/* Start with one plant active (the first one) */
|
||||
gs->plants[0].active = 1;
|
||||
gs->plants[0].productionRate = BASE_OXYGEN_PER_SECOND;
|
||||
gs->plantCount = 1;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Load Game State from SAVE_FILE (if it exists). Otherwise, init fresh.
|
||||
*****************************************************************************/
|
||||
void loadGame(GameState *gs) {
|
||||
FILE *f = fopen(SAVE_FILE, "rb");
|
||||
if(!f) {
|
||||
/* No save found, start a new game */
|
||||
initGame(gs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read the saved struct. For a real game, you'd want version checks, etc. */
|
||||
fread(gs, sizeof(GameState), 1, f);
|
||||
fclose(f);
|
||||
|
||||
/* Sanity check that at least one plant is active. If not, re-init. */
|
||||
if(gs->plantCount < 1) {
|
||||
initGame(gs);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Save Game State to SAVE_FILE
|
||||
*****************************************************************************/
|
||||
void saveGame(const GameState *gs) {
|
||||
FILE *f = fopen(SAVE_FILE, "wb");
|
||||
if(!f) {
|
||||
return; /* if we can't open it, just skip saving */
|
||||
}
|
||||
fwrite(gs, sizeof(GameState), 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Update Game State
|
||||
* - Accumulate oxygen based on the total production rate * elapsed time
|
||||
*****************************************************************************/
|
||||
void updateGame(GameState *gs, double elapsedSeconds) {
|
||||
double totalProd = calcTotalProduction(gs);
|
||||
gs->oxygen += totalProd * elapsedSeconds;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Render/Draw the game to the terminal
|
||||
*
|
||||
* We’ll reserve the top 3/4 of the screen for shelves (with ASCII pots/plants),
|
||||
* and the bottom 1/4 for stats, cost, etc.
|
||||
*
|
||||
* For simplicity, let's assume a typical 24- or 25-line terminal height.
|
||||
* We'll do 15-16 lines for shelves, then ~8-9 lines for the bottom portion.
|
||||
*
|
||||
*****************************************************************************/
|
||||
void renderGame(const GameState *gs) {
|
||||
/* We'll define how many lines we want for shelves, ignoring actual
|
||||
terminal height detection for simplicity. Adjust as you like. */
|
||||
const int shelfLines = 15;
|
||||
|
||||
/* Each shelf is represented by two lines:
|
||||
1) The plant pot / name
|
||||
2) The "--------" shelf line
|
||||
We'll show up to SHELF_COUNT shelves from top to bottom.
|
||||
*/
|
||||
int shelfIndex = 0;
|
||||
int plantIndex = 0;
|
||||
|
||||
/* Print the top banner */
|
||||
printf("========================================\n");
|
||||
printf(" T E R R A R I U M \n");
|
||||
printf("========================================\n\n");
|
||||
|
||||
/* We have SHELF_COUNT shelves to display. Let’s map each shelf to a plant index if available. */
|
||||
for(shelfIndex = 0; shelfIndex < SHELF_COUNT; shelfIndex++) {
|
||||
/* If there's a plant for this shelfIndex, show it.
|
||||
We'll just go in the order plants are purchased (0,1,2,...)
|
||||
and display them as long as they are active, up to the number of shelves. */
|
||||
if(plantIndex < MAX_PLANTS && gs->plants[plantIndex].active) {
|
||||
/* Show an ASCII pot and the plant’s name */
|
||||
printf(" [%s]\n", gs->plants[plantIndex].name);
|
||||
printf(" ---------------------- (shelf #%d)\n", shelfIndex+1);
|
||||
plantIndex++;
|
||||
} else {
|
||||
/* Empty shelf */
|
||||
printf(" [empty]\n");
|
||||
printf(" ---------------------- (shelf #%d)\n", shelfIndex+1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we display the bottom portion (the UI area). We'll show the last 1/4 lines. */
|
||||
/* Let's just put a few blank lines to ensure separation. */
|
||||
printf("\n");
|
||||
|
||||
/* Bottom section with info and controls */
|
||||
printf("Oxygen: %.2f O2\n", gs->oxygen);
|
||||
printf("Total Production Rate: %.3f O2/sec\n", calcTotalProduction(gs));
|
||||
printf("Next Plant Cost: %.2f O2\n\n", gs->plantCost);
|
||||
|
||||
printf("[SPACE] Tap the first plant for +%.1f O2 (only if the first plant exists)\n", TAP_BONUS_OXYGEN);
|
||||
printf("[b] Buy a new plant\n");
|
||||
printf("[q] Quit\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Handle User Input
|
||||
*****************************************************************************/
|
||||
void handleInput(GameState *gs, char input) {
|
||||
switch(input) {
|
||||
case ' ':
|
||||
/* Space bar: Tapping only affects the first plant if active. */
|
||||
if(gs->plants[0].active) {
|
||||
gs->oxygen += TAP_BONUS_OXYGEN;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
case 'B':
|
||||
/* Buy a new plant if possible */
|
||||
buyPlant(gs);
|
||||
break;
|
||||
default:
|
||||
/* ignore other keys */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Buy a new plant (if we can afford it and haven't reached MAX_PLANTS)
|
||||
*****************************************************************************/
|
||||
void buyPlant(GameState *gs) {
|
||||
if(gs->plantCount >= MAX_PLANTS) {
|
||||
/* No more plants can be bought */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we have enough oxygen for the next plant */
|
||||
if(gs->oxygen >= gs->plantCost) {
|
||||
gs->oxygen -= gs->plantCost;
|
||||
|
||||
/* Activate the next plant and set its production rate */
|
||||
int index = gs->plantCount; /* next available slot */
|
||||
gs->plants[index].active = 1;
|
||||
/* For demonstration: each new plant has the same base production,
|
||||
or you could do something fancier. */
|
||||
gs->plants[index].productionRate = BASE_OXYGEN_PER_SECOND;
|
||||
|
||||
gs->plantCount++;
|
||||
|
||||
/* Increase the cost for the next plant */
|
||||
gs->plantCost *= COST_MULTIPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Calculate the Total Production from all active plants
|
||||
*****************************************************************************/
|
||||
double calcTotalProduction(const GameState *gs) {
|
||||
double total = 0.0;
|
||||
for(int i = 0; i < MAX_PLANTS; i++) {
|
||||
if(gs->plants[i].active) {
|
||||
total += gs->plants[i].productionRate;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Get elapsed time in seconds since the last tick
|
||||
*****************************************************************************/
|
||||
double getElapsedTime(struct timespec *lastTick) {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
double elapsed = (now.tv_sec - lastTick->tv_sec)
|
||||
+ (now.tv_nsec - lastTick->tv_nsec) / 1000000000.0;
|
||||
*lastTick = now;
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
BIN
terrarium_bk/terrarium/terrarium_save.dat
Normal file
BIN
terrarium_bk/terrarium/terrarium_save.dat
Normal file
Binary file not shown.
Reference in New Issue
Block a user