Compare commits

...

3 Commits

Author SHA1 Message Date
lee2sman a72a646f23 general updates day 3 2021-03-07 03:41:47 -05:00
lee2sman 2f174161f1 dungeonfile.txt 2021-03-07 03:41:26 -05:00
lee2sman 69ffd510b6 update README.md 2021-03-07 03:41:03 -05:00
5 changed files with 232 additions and 44 deletions

View File

@ -4,18 +4,93 @@
# Devlog
## Day 3
Last night when I solved a vexing (but simple) bug that was causing weird rendering of the dungeon I had decided that I was next ready to work on the actual gameplay mechanics! Well, today I started out actually by implementing the date function instead. My original idea had been to make a game somewhat indebted to the game-ritual Vesper.5 by Michael Brough, which allows the player to only move once a day. So I started out by implementing a function to get the date, check if it's the same as the last date appended to a savedate file, and then report back whether it's a new day or the same day. If it's a new day, you can move the player, otherwise, you can't.
Then I promptly turned this off so I can work on and debug and play the game as I develop it! Maybe I'll turn this back on later, maybe not. Will see how it turns out. The goal would be to turn this back on when I release.
So now I started to go through emojis to look for people, and then after to look for items to add to the game procedurally. I started realizing I don't really want to add monsters. I'm still after a personal quest to make something more like a roguelike walking sim, something that doesn't rely on attacking for its game mechanics. I love wandering in Caves of Qud, exploring, seeing what comes up, and having conversations. Can I make it something more like that? It does have quests, which helps move the action forward, and it has danger/risk.
Simulating a giant ecosystem I think is outside the scope of what I can accomplish or work on in a week, though I could extend this after the 7drl finishes. I started thinking about the almost roguelike Robotfindskitten, by Leonard Richardson, originally from 1997/2000. It's described as "yet another zen simulation."
[robotfindskitten](http://www.robotfindskitten.org/)
The gameplay dynamics are simple. You are a lowly robot wandering around. There are lots of letters (aka objects or maybe other creatures) that you see but can't identify until you bump into them, the same way that you bump into monsters in other roguelikes. And that's it. When you find kitten or quit, then the game ends.
Here's a list of items I came across in my current playthrough just now, and the letters that were representing them, which appears entirely arbitrary:
* . - An unripe orange
* L - For a moment, you find something in your hands. But it disappears.
* Q - This is an anagram.
* g - Ten yards of avocado-green shag carpet.
* l - a traffic signal. It appears to have been recently vandalized.
* o - an aromatherapy candle bright with healing light
* > - a ketchup bottle (nearly empty)
* < - an old bootable business card, unfortunately cracked down the middle
* P - "appears to be" an ancient Roman breastplate
* = - a mere collection of pixels
* P - a warranted genuine Snark
* t - a black screen filled with colorful ASCII characters
* i - a rusty mellon baller
* ) - an oil portrait of you, about to find kitten
* b - he letters O and R
* A - a stupid mask, fashioned after a beagle
* r - robotfindskitten!
A quick animated ascii text of the robot moving to the kitten over a few frames, and then the program ends and quits!
This is a petite game that leverages its procedural algorithm so that the player's goal is really to explore the items just for the satisfaction of seeing what items get generated and how they're described. That's basically the entirety of the experience, and its pretty minimal at that. But it's an occasional thrill.
Likewise, in the Roguelike Caves of Qud, one of my favorite things to do is to go visit the graveyard that lies above Joppa. Reading the tombstone epitaphs brings a thrill, not because it's a serious experience but because of the zaniness of what's written there, and the variety of things that appear. Beyond that, the world of Qud rewards exploration for the lore, books, factions, and histories discovered there.
My own game will likely be more Robotfindskitten-like, but let's see.
Before bed at 3am I started thinking about how being able to make a single move might not be the most compelling. Even if you could amke a single move, then continue to interact with a person or object at that space. What if it was more like you could play in a single section each day, then a gate opens and you can move to the next 'room' or area. I'm thinking about how when you finish enough tasks in untitled goose game it opens the gate to allow you to walk to the next mini area.
I'm also thinking about Tiny Villages. I love the villages in Qud, and even the primitive town maps of roguelikes Larn and Moria. Perhaps I can make small generated outposts and mini towns that can be explored each day, with robotfindskitten items as well as occasional graves or magical items. And a gate or passageway allows one to proceed to the next area (and closes off previous one?) when it's a new day.
```
//----------------create zone---------------
let buildings = ['🏛️','⛺']
let landscapes = ['🏔️''','🌿','🌱','🌾','🌻','🌵']
let plants = ['🌹','🌺','🌻','🌼','🌷','🎋']
let shrines = ['⛩️','🗿']
let items = ['🍄','🌰','
```
I'm wondering if i'll find enough hut, house, stall, building emojis I want to use. Might have to switch to a tileset? But if that's the case, I'll need a different approach to drawing the screen. I guess I'll need to do that anayway. Maybe time for Curses or Blessed.
[Blessed library](https://www.npmjs.com/package/blessed)
Or maybe like the original Robotfindskitten, the individual letter characters can be just as arbitrary, their description when you walk into them doing the work of describing a world (in our mind) much more than the limited graphic representation of the emoji.
## Day 2
I taught, had meetings, cooked and had a nice dinner with friends in my household (we're in a "pod"), spent a few hours actually cleaning up and backing up some hard drives tonight, so I spent less time on this than I had hoped today, but I did get stuff done, and I tracked down an annoying bug.
Today I explored terrain. I kind of decided to keep exploring emojis for my tileset. Perhaps this is a mistake. Emojis, at least to me, are often "ugly aesthetic." But I use them! One thing that's nice is that they are probably present on most people's systems. And there are already a variety of clear options for the player avatars, enemy "sprites", terrain, objects.
```
🌲🌲🎄🌲🎄🌳🌲🎄🌳🌲🎄🌳🌳🎄🎄🌳🎄🎄🌲🌳
🌲🌳🎄🎄🌳🌳🌳🎄🌳🌲🌲🌲🎄🌲🌳🎄🌲🌳🎄🌳
🌲🌲👵🎄🌳🌲🌲🌲🌲🎄🌲🎄🌳🌳🎄🌳🌳🎄🌳🌲
🌳🌳🎄🌳🎄🌲🌲🌳🌳🎄🌲🌳🎄🎄🌳🌲🌳🎄🌳🌳
🌲🎄🎄🎄🎄🌲🌳🌳🎄🌳🌳🌲🎄🌳🌲🌲🌲🎄🌲🌲
```
For example, here's a clear trophy like in Indiana Jones and the Temple Doom. Pretty clear that this would lead to the end of the game or be a big prize to get in any game.
Today I mostly explored terrain. I kind of decided to keep exploring emojis for my tileset. Perhaps this is a mistake. Emojis, at least to me, are often "ugly aesthetic." But I use them! One thing that's nice is that they are probably present on most people's systems. And there are already a variety of clear options for the player avatars, enemy "sprites", terrain, objects.
For example, here's a clear trophy like in Indiana Jones and the Temple of Doom. Very roguelike-y to me, and pretty clear that this would lead to the end of the game or be a big prize to get in any game.
```
🏆
```
So if I proceed with emojis for my tileset, I'm thinking about avatars for the player. Does this get selected by the player? Or does the player select a class like in so many other roguelikes, and that determines the avatar? If the player does end up playing this game over days, weeks and months, it's important that they feel connected to their avatar on screen, and I think that would be aided by the player getting to choose their own avatar. Here's some of the options I'm looking at. Notice that these each speak different vocabularies slightly, from the oldschool emoticon smiley and happy faces
So if I proceed with emojis for my tileset, I'm thinking about avatars for the player. Does this get selected by the player? Or does the player select a class like in so many other roguelikes, and that determines the avatar? If the player does end up playing this game over days, weeks and months, it's important that they feel connected to their avatar on screen, and I think that would be aided by the player getting to choose their own avatar. Here's some of the options I'm looking at. Notice that these each speak different vocabularies.
```
const avatars = ['👳','👶','🧛','🤺','🕵','👲','🧕','👵','👧','🧔','👸','☹','☺',🤠','@']
@ -35,7 +110,12 @@ node dotd -l
That's probably a bit silly but i'm going with it for now. I'm using yargs to parse the flags in the CLI.
Just had an interesting idea that perhaps the player submits a short program file like 'right right right up up right' so that you have to program your player-robot, and that you submit that and it runs. Would that be interesting? Rather than turn-based? I get too many ideas but should probably just proceed with standard, and then modify later if I don't have a clear goal other than 'wouldn't it be cool if....". Taking a break now for a bit to exercise, answer email, cook dinner, shabbat, and will probably get back to this in the late evening.
Just had an interesting idea that perhaps the player submits a short program file like 'right right right up up right' so that you have to program your player-robot, and that you submit that and it runs. Would that be interesting? Rather than turn-based? I get too many ideas but should probably just proceed with standard, and then modify later if I don't have a clear goal other than 'wouldn't it be cool if....".
Tonight after coming back to things I noticed an annoying bug. When opening a saved game file and loading the terrain would be slightly messed up. One char to the left or right of the player wouldn't render correctly, and the player would be a few spots over from where they should actually be. I tried all sorts of things and after some brute force debugging and console.log'ing I realized I was rendering the terrain at the wrong time, moved it until after saving out the terrain/game file (so it doesn't over-write anything in the dungeon permanently). And now it works fine. One question: should i be saving all of the terrain tiles? Quite possibly. So then maybe i should save it out! Or save it to a json table? Seems silly. The issue of why/how to save would come up if tile persistence of terrain is necessary. If a player moves left and walks over a tree, should that same exact tree be there after they walk off? I think so. That's of course the standard course of fare. I mean, I only have different tree terrain not because they act differently but because of 'visual interest'. Would a player notice if the trees were scattered each time they loaded if a day had passed? Maybe? Anyway, I'm tired and will revisit this decision in the next few days. Am assuming I should store all tiles in all positions. It's not particularly computationally difficult.
Next step when I come back is to add gameplay mechanics. I think I'd like to have tents, markets, lore?, conversations? monsters? What else?
## Day 1

View File

@ -1,8 +1,9 @@
# TODO
## Bugs
- [ ] after creating dungeon, the player 'jumps' but investigating the gameFile everything looks ok in there. hmmm. it looks like it's not displaying the player where i think they should be located.
- [ ] not all emoji are same width so different size players cause map row to get off of lined up grid
## Roadmap
- [ ] what's the game mechanic / concept? should be one clear concept
- [.] saveFile should actually save json file (as text) with map and all the tiles in each position:
- [X] player position,
@ -10,4 +11,4 @@
- [ ] item positions,
- [ ] terrain positions
- [ ] inventory file to save inventory
- [ ] save date and check for today's date (if so, you cannot move, but can you do something else?)
- [X] save date and check for today's date (if so, you cannot move, but can you do something else?)

171
dotd.js
View File

@ -6,16 +6,19 @@ const argv = yargs(hideBin(process.argv)).argv
const fs = require("fs");
const dungeonFile = "dungeonfile.txt";
const gameFile = "gamefile.txt";
const dateFile = "datefile.txt";
//globals
const width = 20;
const height = 5;
let dungeon;
let today;
let player = {"position": {
"x": null,
"y": null,
},
"avatar": "@"
"avatar": "@",
"movedToday": false
};
let parseArgs = () => {
@ -24,12 +27,13 @@ let parseArgs = () => {
createDungeon();
choosePlayer();
placePlayer();
//placeItems();
} else {
loadDungeon();
loadPlayer();
movePlayer();
//loadItems();
}
createTerrain();
}
let loadDungeon = () => {
@ -53,33 +57,46 @@ let loadPlayer = () => {
}
let movePlayer = () => {
if (argv.r || argv.right){
if (player.position.x < width-1){
console.log('moved right');
player.position.x++;
} else {
console.log("can't go that way");
}
} else if (argv.l || argv.left){
if (player.position.x > 0){
console.log('moved left');
player.position.x--;
} else {
console.log("can't go that way");
}
} else if (argv.u || argv.up){
if (player.position.y>0){
console.log('moved up');
player.position.y--;
} else {
console.log("can't go that way");
}
} else if (argv.d || argv.down){
if (player.position.y<height-1){
console.log('moved down');
player.position.y++;
} else {
console.log("can't go that way");
//if moving
if (argv.r || argv.right || argv.l || argv.left || argv.u || argv.up || argv.d || argv.down) { //check to see if moving first
if(!player.movedToday){ //check to see if player did not move yet
if (argv.r || argv.right){
if (player.position.x < width-1){
console.log('moved right');
player.position.x++;
player.movedToday = true;
} else {
console.log("can't go that way");
}
} else if (argv.l || argv.left){
if (player.position.x > 0){
console.log('moved left');
player.position.x--;
player.movedToday = true;
} else {
console.log("can't go that way");
}
} else if (argv.u || argv.up){
if (player.position.y>0){
console.log('moved up');
player.position.y--;
player.movedToday = true;
} else {
console.log("can't go that way");
}
} else if (argv.d || argv.down){
if (player.position.y<height-1){
console.log('moved down');
player.position.y++;
player.movedToday = true;
} else {
console.log("can't go that way");
}
}
} else { //you already moved
console.log("already moved today");
}
}
@ -113,6 +130,67 @@ let placePlayer = () => {
//dungeon[player.position.y][player.position.x] = "@"
}
//-------------CREATE ITEMS---------------------------------------
let createItems = () => {
//how many items spawn on screen?
//maybe spawn on dungeon overall?
}
let placeItems = () => {
}
//-----------------PEOPLE----------------------------------------
let people =
[
{
"name": "wizard",
"emoji": "🧙",
"life": 3
},
{
"name": "woman",
"emoji": "🧝",
"life": 3
},
{
"name": "man",
"emoji": "🧔",
"life": 3
},
{
"name": "woman",
"emoji": "👵",
"life": 3
},
{
"name": "man",
"emoji": "👴",
"life": 3
},
{
"name": "person",
"emoji": "🧓",
"life": 3
}
}
//----------------create zone---------------
let buildings = ['🏛️','⛺']
let landscapes = ['🏔️''','🌿','🌱','🌾','🌻','🌵']
let plants = ['🌹','🌺','🌻','🌼','🌷','🎋']
let shrines = ['⛩️','🗿']
let items = ['🍄','🌰','
let debug = () => {
//console.log(dungeon)
console.log("terminal width: "+process.stdout.columns+" height: "+process.stdout.rows);
@ -146,8 +224,15 @@ let createTerrain = () => {
let dungeonWithItemsToStrings = () => {
//specify player location
dungeon[player.position.y][player.position.x] = player.avatar;
//specify item location
//
//for (let i = 0; i < items.length; i++){
// dungeon[item[i].position.y][item[i].position.x] = item[i].icon;
//}
let dungeonStr = "";
for (let i = 0; i < height; i++){
dungeonStr+=(dungeon[i].join("")+"\n");
@ -160,19 +245,41 @@ let drawDungeon = dungeonStr => {
console.log(dungeonStr); //display
}
let writeToFile = dungeonStr => {
fs.writeFileSync(dungeonFile, dungeonStr); //save to disk
let writeToFile = dungeonStr => { //save to disk
fs.writeFileSync(dungeonFile, dungeonStr);
let playerData = JSON.stringify(player);
fs.writeFileSync(gameFile, playerData); //save to disk
fs.writeFileSync(gameFile, playerData);
fs.appendFileSync(dateFile, today+'\n');
}
let checkDay = () => {
const savedDays = fs.readFileSync(dateFile).toString().split("\n");;
const lastDay = savedDays[savedDays.length-2]; //loads day last played
console.log("Last movement: "+lastDay);
const fullDate = new Date();
today = fullDate.getFullYear()+"-"+fullDate.getMonth()+"-"+fullDate.getDate();
console.log("Today: "+today);
if (lastDay === today){
//console.log('already moved today');
} else {
player.movedToday = false
}
}
let main = () => {
checkDay();
parseArgs();
let dungeonStr = dungeonToStrings();
writeToFile(dungeonStr);
createTerrain(); //do this after writing to file
let dungeonWithItems = dungeonWithItemsToStrings();
drawDungeon(dungeonWithItems);

View File

@ -1,5 +1,5 @@
🌳🌲🌳🌳🎄🌳🎄🌲🎄🌳🌳🌳🌳🌲🌲🌲🌲🎄🌲🌳
🌲🎄🌲🌳🌲🎄🎄🎄🎄🌳🎄🌳🎄🎄🌲🎄🌲🌲🌳🌳
🌲🎄🌲🎄🌳🌳🌲🎄🌲🌳🎄🌳🌲🎄🎄🎄🎄🌲🌲🎄
🎄🌲🎄🌳🎄🌳🌲🎄🌳🌲🎄🌳🎄🌳🌳🎄🎄🌲🎄🌲
🌲🌳🎄🌳🌲🌳🎄🌳🎄🌳🌳🌳🌳🎄🌲🎄🌲🎄🎄🎄
....................
....................
....................
....................
....................

View File

@ -1 +1 @@
{"position":{"x":8,"y":1},"avatar":"@"}
{"position":{"x":6,"y":4},"avatar":"@","movedToday":false}