Author Topic: I started work on an RPG yesterday, 6/13. Any comments on my early build?  (Read 6069 times)

0 Members and 1 Guest are viewing this topic.

Offline astrosteve

  • Newbie
  • Posts: 3
    • View Profile
A quick introduction from me: I'm 43 and fooled around with Basic starting around 1984. It lasted until about 1995 or so, when I switched to C. I first programmed in VIC-20 Basic and eventually moved to an early version of QuickBASIC. (version 3.5, I think?) before eventually moving onto 4 then 4.5.  I never made anything too complex in Basic; just really simple games I could bash out in an hour or so.  I spent maybe 3-4 hours on my biggest game. More complex development happened in C. But I've always wanted to make a game that looked like it was from 1986-1987 or so. Real text heavy, few to no graphics. Lots of calculations going on under the hood.

So this is entirely text based. A few notes: I'm eventually going to use a custom window size, so I've made no attempt whatsoever to format the text. It looks awful and I know it. I've worked on it today and yesterday and it's about 375 lines. You can walk between three rooms, pick up a weapon and attack a skeleton. The attack routine is super buggy and no one can die. I just want to get the most basic systems in at this point, even if they're buggy. It's a fairly boring thing to play right now, what with the unkillable monsters.

I'm open to any suggestions. This is the most complex thing I've ever developed in Basic and I'm a bit overwhelmed right now.

Code: QB64: [Select]
  1. ' Steve's RPG-like thing.
  2. ' Written in 2018.
  3. ' Version 0.1-dev (pre-alpha)
  4.  
  5. ' Short term to do: figure out what's wrong with attack:, Add npc inventory, recognize death condition for npc, stop respawning npc every time player enters a room.
  6. ' Longer term goals: Get the non-weapon slots working. Show inventory. exchange weapons between slots. drop items.
  7.  
  8. OPTION _EXPLICIT ' Force variable declaration
  9.  
  10. DIM phrase AS STRING
  11. DIM object AS STRING ' For the parser, it's whatever is after the first word.
  12. DIM answer AS STRING
  13. DIM cmderror AS INTEGER ' This is for parse. cmdeerror set to 1 when a command successfully executes.
  14. DIM doneroom AS INTEGER
  15. DIM croom AS INTEGER ' the next room number to create
  16. DIM exitc AS INTEGER ' Exit code.
  17. DIM player AS playertype
  18. DIM WeaponTable(5) AS weapon ' Weapons in the game.
  19. DIM NPCTable(5) AS npc
  20. DIM pcinv AS inventory
  21. DIM curroom AS room ' Room player is currently in.
  22. DIM curnpc AS playertype ' Room NPC is currently in.
  23. DIM gameworld(5) AS room
  24.  
  25. doneroom = 0
  26. cmderror = 0
  27.  
  28. GOSUB roomsetup: ' Create starting room, intialize other rooms.
  29. GOSUB weapongen: ' Create the weapon table!
  30. GOSUB npcgen: ' Create the npc table!
  31. GOSUB playergen: ' Player Generation routine
  32. croom = 1 'starts in room 1.
  33. GOSUB setroom: ' Set initial room properties.
  34.  
  35. Main: ' Main loop
  36.  
  37. IF doneroom = 1 THEN GOSUB setroom:
  38.  
  39. PRINT RTRIM$(curroom.shortd)
  40. PRINT RTRIM$(curroom.longd)
  41. IF curroom.npc <> 0 THEN PRINT "There is a "; RTRIM$(NPCTable(curroom.npc).name); " here."
  42. IF curroom.weapon <> 0 THEN PRINT "A "; RTRIM$(WeaponTable(curroom.weapon).name); " is here."
  43.  
  44.  
  45. PRINT "Exits:"
  46. IF curroom.north <> 0 THEN PRINT "North"
  47. IF curroom.south <> 0 THEN PRINT "South"
  48. IF curroom.east <> 0 THEN PRINT "East"
  49. IF curroom.west <> 0 THEN PRINT "West"
  50.  
  51.  
  52.     INPUT "What do you do? ", res
  53.     GOSUB parse:
  54.     IF cmderror = 0 THEN PRINT "Invalid Command." ' This isn't working but it's to minor of a thing for me to dwell on at this stage.
  55. LOOP WHILE doneroom <> 1
  56.  
  57. GOTO Main: ' Shut up. I know.
  58.  
  59.  
  60.  
  61. parse:
  62. phrase = "null"
  63. object = "null"
  64. IF INSTR(res, " ") THEN
  65.     phrase = LEFT$(res, INSTR(res, " ") - 1)
  66.     object = MID$(res, INSTR(res, " ") + 1)
  67. IF phrase = "null" THEN ' If phrase wasn't changed from null, that means the command is only one word. Set the word to be processed.
  68.     phrase = res
  69. phrase = LCASE$(phrase) ' Set it to be lowercase to avoid parsing issues.
  70. IF object <> "null" THEN object = LCASE$(object) ' Only set to lowercase if it isn't null. ie, if an object was part of the command.
  71.  
  72. ' This will be replaced with a SELECT CASE eventually.
  73. IF phrase = "attack" THEN cmderror = 1: GOSUB attack:
  74. IF phrase = "dance" THEN cmderror = 1: GOSUB dance:
  75. IF phrase = "help" THEN cmderror = 1: GOSUB help:
  76. IF phrase = "look" THEN cmderror = 1: GOSUB look:
  77. IF phrase = "get" THEN cmderror = 1: GOSUB pickup:
  78. IF phrase = "xyzzy" THEN cmderror = 1: PRINT "Seriously? I don't think so."
  79. IF phrase = "quit" OR phrase = "exit" THEN cmderror = 1: GOSUB quit:
  80. IF phrase = "north" OR phrase = "n" THEN cmderror = 1: GOSUB roomchange:
  81. IF phrase = "south" OR phrase = "s" THEN cmderror = 1: GOSUB roomchange:
  82. IF phrase = "east" OR phrase = "e" THEN cmderror = 1: GOSUB roomchange:
  83. IF phrase = "west" OR phrase = "w" THEN cmderror = 1: GOSUB roomchange: ' I feel like there must be a better way to do this.
  84.  
  85.  
  86. attack: ' Attacking isn't balanced and needs extensive work, but the very basic framework of a round is in place.
  87. ' npc in room 3 seems to have too many HP. Attack is buggy and weird in general.
  88. DIM pcinit AS INTEGER ' Initiative for player
  89. DIM npcinit AS INTEGER ' NPC's initiative. May change one of those names, they're real easy to confuse or mess up.
  90. DIM turn AS INTEGER ' turn = 1 means player goes next, turn = 0 means npc goes next.
  91. DIM attack AS INTEGER ' Attack role. Used for player & npc
  92. DIM defense AS INTEGER ' Defense role. Also used for player & pc
  93. DIM damage AS INTEGER
  94.  
  95. IF curroom.npc = 0 THEN
  96.     PRINT "There is nothing here to attack."
  97.     RETURN
  98. IF curroom.npc <> 0 THEN
  99.  
  100.     pcinit = INT(RND * 18) + 1
  101.     npcinit = INT(RND * 18) + 1
  102.     PRINT "Entering fight mode."
  103.     PRINT "Your Initiative: "; pcinit; " npc's Initiative: "; npcinit
  104.  
  105.  
  106.     IF pcinit > npcinit THEN turn = 1
  107.     IF pcinit < npcinit THEN turn = 0
  108.  
  109.     FightLoop: ' Main fight loop. It's true turn based right now with no auto attack. npcs currently cannot initiate combat.
  110.     IF turn = 1 THEN ' This is always the players' turn
  111.         attack = rolldice%(1, 20)
  112.         ' There'll eventually me modifiers based on class/race
  113.         defense = rolldice%(1, 20)
  114.         ' Just d20 vs d20 now. Higher number wins.
  115.         IF attack > defense THEN
  116.             damage = rolldice%(WeaponTable(pcinv.weapon1).dice, WeaponTable(pcinv.weapon1).sides)
  117.             curnpc.hp = curnpc.hp - damage
  118.         END IF
  119.         IF attack < defense THEN
  120.             damage = 0
  121.             curnpc.hp = curnpc.hp - damage ' Done this way because there may evemtually be modifiers that add damage even if hit misses.
  122.         END IF
  123.         PRINT RTRIM$(curnpc.name); " took "; damage; " and has "; curnpc.hp; " left."
  124.         IF pcinit > npcinit THEN turn = 0 ' pcinit being higher means player went first. If it's lower, it means player is going second and doesn't need to trigger npc's turn.
  125.     END IF
  126.     IF turn = 0 THEN ' npc's turn
  127.         attack = rolldice%(1, 20)
  128.         ' There'll eventually me modifiers based on class/race
  129.         defense = rolldice%(1, 20)
  130.         ' Just d20 vs d20 now. Higher number wins.
  131.         IF attack > defense THEN
  132.             damage = rolldice%(WeaponTable(pcinv.weapon1).dice, WeaponTable(pcinv.weapon1).sides) ' Uses player's weapon for now.
  133.             player.hp = player.hp - damage ' Player damage
  134.         END IF
  135.         IF attack < defense THEN
  136.             damage = 0
  137.             player.hp = player.hp - damage ' Player damage
  138.         END IF
  139.         PRINT "You've taken "; damage; " to your hitpoints! You now have "; player.hp; " left."
  140.         IF pcinit < npcinit THEN turn = 1: GOTO FightLoop: ' I can't figure out how to do this without using goto.
  141.     END IF
  142.  
  143.  
  144.  
  145. dance:
  146. PRINT "You do that weird pointy dance people in leisure suits did in the 70s. Is this really becoming of an adventurer such as yourself?"
  147.  
  148. help:
  149. PRINT "Game in Development. Move with standard cardinal directions. Do not, under any circmstances, attempt to dance. Quit or Exit to quit."
  150. cmderror = 1
  151.  
  152. look:
  153. PRINT curroom.longd
  154. cmderror = 1
  155.  
  156. quit:
  157. INPUT "Are you sure? (y/N) ", answer
  158. IF LCASE$(answer) = "y" OR LCASE$(answer) = "yes" THEN END
  159.  
  160.  
  161. TYPE playertype
  162.     name AS STRING * 25
  163.     str AS INTEGER ' Strength.
  164.     dur AS INTEGER ' Durability, aka basis of hitpoints
  165.     hp AS INTEGER ' hit points
  166.  
  167. TYPE inventory
  168.     weapon1 AS INTEGER ' Corresponds to the WeaponTable entry.
  169.     weapon2 AS INTEGER
  170.     slot1 AS INTEGER ' Eventually there'll be a General Inventory table, not implemented yet.
  171.     slot2 AS INTEGER
  172.     slot3 AS INTEGER
  173.  
  174. TYPE weapon
  175.     name AS STRING * 20
  176.     sides AS INTEGER ' number of sides of damage dice. d4, d6, etc.
  177.     dice AS INTEGER ' number of dice.
  178.     desc AS STRING * 20 ' not used yet, but maybe some kind of descriptive name, such as "gleaming"
  179.  
  180. TYPE npc ' This is totally cosmetic. I'll figure out better stuff to put here later.
  181.     name AS STRING * 25 ' Race name. eg, hobgoblin, kobold, dragon, etc.
  182.     str AS INTEGER ' Strength.
  183.     dur AS INTEGER ' Durability, aka basis of hitpoints
  184.     hp AS INTEGER ' hit points
  185.  
  186. TYPE room
  187.     roomid AS INTEGER ' used for moving
  188.     shortd AS STRING * 30 ' short desc
  189.     longd AS STRING * 180 ' long desc
  190.     npc AS INTEGER ' Right now it supports 1 NPC per room. The number references the NPC Table. Will expand this in the future.
  191.     weapon AS INTEGER ' Game currently supports one weapon and one item in each room.
  192.     item AS INTEGER ' Item table not implemented yet.
  193.     north AS INTEGER ' Leads to this room number.
  194.     south AS INTEGER
  195.     east AS INTEGER
  196.     west AS INTEGER
  197.  
  198.  
  199. playergen:
  200. ' INPUT "What is your name? ", player.name
  201. player.str = rolldice%(3, 6)
  202. player.dur = rolldice%(3, 6)
  203. player.hp = rolldice%(1, 10) ' Roll 1d10
  204. PRINT "Your stats are -"
  205. PRINT "Strength: "; player.str; " Stamina: "; player.dur; " Hitpoints: "; player.hp
  206. pcinv.weapon1 = 2 ' Starting weapon is a short sword.
  207. PRINT "Press a key to start playing."
  208.     _LIMIT 5 ' Don't need it eating up all the CPU.
  209.  
  210. weapongen:
  211. ' I really want this to be a Constant so it can't be modified accidentally, but that doesn't seem possible.
  212.  
  213. WeaponTable(1).name = "Long Sword"
  214. WeaponTable(1).sides = 8
  215. WeaponTable(1).dice = 1
  216.  
  217. WeaponTable(2).name = "Short Sword"
  218. WeaponTable(2).sides = 6
  219. WeaponTable(2).dice = 1
  220.  
  221. WeaponTable(3).name = "Mace"
  222. WeaponTable(3).sides = 4
  223. WeaponTable(3).dice = 1
  224.  
  225. WeaponTable(4).name = "Dagger"
  226. WeaponTable(4).sides = 3
  227. WeaponTable(4).dice = 1
  228.  
  229. WeaponTable(5).name = "Lance"
  230. WeaponTable(5).sides = 6
  231. WeaponTable(5).dice = 2
  232.  
  233. npcgen: ' These numbers are all made up and are changing at some point.
  234. NPCTable(1).name = "Kobold"
  235. NPCTable(1).str = rolldice%(3, 6) ' 3d6 is standard ability roll
  236. NPCTable(1).dur = rolldice%(3, 6) ' 3d6
  237. NPCTable(1).hp = rolldice%(1, 6) ' 1d6 for HP
  238.  
  239.  
  240. NPCTable(2).name = "Hobgoblin"
  241. NPCTable(4).str = rolldice%(3, 6) ' 3d6 is standard ability roll
  242. NPCTable(4).dur = rolldice%(3, 6) ' 3d6
  243. NPCTable(4).hp = rolldice%(1, 6) ' 1d6
  244.  
  245. NPCTable(3).name = "Orc"
  246. NPCTable(4).str = rolldice%(3, 6) ' 3d6 is standard ability roll
  247. NPCTable(4).dur = rolldice%(3, 6) ' 3d6
  248. NPCTable(4).hp = rolldice%(1, 6) ' 1d6
  249.  
  250. NPCTable(4).name = "Skeleton"
  251. NPCTable(4).str = rolldice%(3, 6) ' 3d6 is standard ability roll
  252. NPCTable(4).dur = rolldice%(3, 6) ' 3d6
  253. NPCTable(4).hp = rolldice%(1, 6) ' 1d6
  254.  
  255. NPCTable(5).name = "Gold Dragon"
  256. NPCTable(4).str = rolldice%(3, 6) + 2 ' Dragon gets 3d6 + 2
  257. NPCTable(4).dur = rolldice%(3, 6) + 2
  258. NPCTable(4).hp = rolldice%(4, 8) + 5 ' 4d8+5 because dragons be strong.
  259.  
  260.  
  261. createnpc: ' Sets up NPCs based off NPCTable.
  262.  
  263. curnpc.name = NPCTable(curroom.npc).name
  264. curnpc.hp = NPCTable(curroom.npc).hp
  265. curnpc.dur = NPCTable(curroom.npc).dur
  266. curnpc.str = NPCTable(curroom.npc).str
  267.  
  268.  
  269. roomsetup:
  270.  
  271. gameworld(1).roomid = 1 ' Starting room. You always start in 1.
  272. gameworld(1).shortd = "The start of a dungeon of fear"
  273. gameworld(1).longd = "You see bones strewn about. This is surely the entrance to somewhere pretty horrible."
  274. gameworld(1).south = 2
  275. gameworld(1).west = 3
  276.  
  277. gameworld(2).roomid = 2
  278. gameworld(2).shortd = "Another room of roominess"
  279. gameworld(2).longd = "It's pretty scary here. You see a boogie woogie skeleton in the corner."
  280. gameworld(2).npc = 4 ' Skeleton
  281. gameworld(2).north = 1
  282.  
  283. gameworld(3).roomid = 3
  284. gameworld(3).shortd = "The Armory"
  285. gameworld(3).longd = "You stand in an old forgotten armory. Most the weapons look old and unusuable. This place hasn't been properly tended to in decades, if not centuries."
  286. gameworld(3).weapon = 5
  287. gameworld(3).east = 1
  288.  
  289.  
  290. setroom:
  291. curroom.roomid = gameworld(croom).roomid
  292. curroom.shortd = gameworld(croom).shortd
  293. curroom.longd = gameworld(croom).longd
  294. curroom.npc = gameworld(croom).npc
  295. curroom.weapon = gameworld(croom).weapon
  296. curroom.item = gameworld(croom).item
  297. curroom.north = gameworld(croom).north
  298. curroom.south = gameworld(croom).south
  299. curroom.east = gameworld(croom).east
  300. curroom.west = gameworld(croom).west
  301. IF curroom.npc <> 0 THEN GOSUB createnpc: ' Bring the room's npc, if any, into existence. Drawback is npc respawns every time you enter room. Will fix that later.
  302. doneroom = 0
  303.  
  304. roomchange:
  305. doneroom = 0 ' Make sure it's set to 0, which effectively means you can't exit.
  306. IF LEFT$(phrase, 1) = "n" AND curroom.north <> 0 THEN croom = curroom.north: doneroom = 1 ' Done this way as the parser accepts either "north" or "n" to move.
  307. IF LEFT$(phrase, 1) = "s" AND curroom.south <> 0 THEN croom = curroom.south: doneroom = 1
  308. IF LEFT$(phrase, 1) = "e" AND curroom.east <> 0 THEN croom = curroom.east: doneroom = 1
  309. IF LEFT$(phrase, 1) = "w" AND curroom.west <> 0 THEN croom = curroom.west: doneroom = 1
  310. IF doneroom <> 1 THEN PRINT "You cannot go that way."
  311.  
  312. pickup:
  313. IF curroom.weapon = 0 AND curroom.item = 0 THEN PRINT "There is nothing here to get."
  314.  
  315. IF curroom.weapon <> 0 THEN
  316.     IF object = RTRIM$(LCASE$(WeaponTable(curroom.weapon).name)) THEN ' Such ugly code. make it all lower case and cut the extra spaces off so it (potentially) matches object.
  317.         IF pcinv.weapon1 AND pcinv.weapon2 <> 0 THEN
  318.             PRINT "You have no room for the "; RTRIM$(WeaponTable(curroom.weapon).name)
  319.             RETURN
  320.         END IF
  321.         IF pcinv.weapon1 = 0 THEN
  322.             pcinv.weapon1 = curroom.weapon
  323.             PRINT "You have placed the "; RTRIM$(WeaponTable(curroom.weapon).name); " in slot 1."
  324.             RETURN
  325.         END IF
  326.         IF pcinv.weapon1 <> 0 AND pcinv.weapon2 = 0 THEN
  327.             pcinv.weapon2 = curroom.weapon
  328.             PRINT "You have placed the "; RTRIM$(WeaponTable(curroom.weapon).name); " in slot 2."
  329.         END IF
  330.     ELSE PRINT "There is no "; object; " here to pick up."
  331.     END IF
  332.  
  333. FUNCTION rolldice% (dice%, sides%)
  334.     DIM counter AS INTEGER
  335.     DIM total(dice%) AS INTEGER
  336.  
  337.     FOR counter = 1 TO dice%
  338.         total(counter) = INT(RND * sides%) + 1
  339.     NEXT counter
  340.     ' total should now contain an array of all dice rolls. Now we add them up.
  341.  
  342.     FOR counter = 1 TO dice%
  343.         rolldice% = rolldice% + total(counter)
  344.     NEXT counter
  345.     ' I feel like there's probably a much better way of doing this.
  346.  
  347.  

Offline commandvom

  • Newbie
  • Posts: 10
    • View Profile
Hi astrosteve!, I've been trying it, you're on the right track! Will it only be text mode? Do you have a story? It would be good images illustrating each room. If you like the idea, I could take some time to draw them (then I will show you what I mean with the images ...)

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Astrosteve,

I have alway been fascinated by 'text based' adventure type games. Just like a book, it conjurers up images that stirs up the imagination.

Many, many years ago, I used to read a magazine called "Proteus". It was purely an entire RPG that included a page for keeping track of your status. I had tried to make a Basic version, but back then, lacked the skill... actually, not much has changed... lol

I would encourage you to continue. Whether you use images or not, the MOST important part of any RPG, is the story. If the storyline is solid, you will hold the interest of any player, or until they either finish or mum tells them to go to bed... lol

Do not be afraid of lengthy descriptions of rooms, objects or characters. Although TOO lengthy may also be a turn off. If you want to stick with text, be daring and non conformist, try a little colour... Room title; Description; Commands; Speech etc... Unless you are a purist the white is just fine... lol

I think I will look for my first issue of Proteus....

Code on.

J
Logic is the beginning of wisdom.

Offline Omerta7486

  • Newbie
  • Posts: 33
  • √𝜋²
    • View Profile
You will have to build a strong command parser. Those text based games sometimes had libraries of dozens or even hundreds of commands. But, I'm sure you new that! Also, if you have leveling, or any stats, I recommend looking at the stat/combat calculations of your favorite RPGs. Especially, those from the 80s, and 90s. This will give you an idea of how to balance combat, even for a text RPG.

But, nice start! Very nostalgic. I got killed by the skeleton in the second room, because I wondered if I could fight it. -_- I could, but I couldn't win. Then, I danced after I died! XD I shall try it some more.
The knowledge that's seeking the favor of another means the murder of self.

Latest version of my game, here  Omertris: Invasion

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
I liked it... but I had one "bone" of contention... Fighting the skeleton with the lance.... The fight did not go well or for very long... When my status almost bottomed out, I was expecting to die with the next hit, but my 'hitpoints' went negative... I thought, "At last! A game where I did NOT get killed off!". Figured it might be a glitch and to just let you know... Joy short lived... *sigh*

J
Logic is the beginning of wisdom.

Offline Omerta7486

  • Newbie
  • Posts: 33
  • √𝜋²
    • View Profile
1. How the heck did you pick up the lance? Lol.

2. I beat the skeleton by pure luck. I had got lucky and had 17 strength, 13 stamina, and 10 HP.

3. It is not a bug, the game keeps going. I don't think there is a lose condition, yet.
The knowledge that's seeking the favor of another means the murder of self.

Latest version of my game, here  Omertris: Invasion

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
When the game finally starts.... From the first room: The skeleton is to the south but the lance is to the west.

I too had pure luck! Bad!

I figured hitpoint issue was either a glitch or incomplete... But it was good not dying... lol
Logic is the beginning of wisdom.