Skip to content

Commit

Permalink
Add sfx, close #9, bump v0.1.0 (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-dray authored Jan 3, 2023
1 parent da4e9f9 commit 7d964f0
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 41 deletions.
13 changes: 8 additions & 5 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
Package: r.oguelike
Title: Play a Roguelike 'Game' in the Console
Version: 0.0.0.9009
Version: 0.1.0
Authors@R:
person("Matt", "Dray", , "mwdray@gmail.com", role = c("aut", "cre"))
Description: Play a simple ASCII-only tile-based toy in the console, inspired
heavily by roguelike games like Rogue (1980). Proof of concept.
Description: Play with a simple ASCII-only tile-based toy in the console,
inspired heavily by roguelike games like Rogue (1980). Proof of concept.
License: MIT + file LICENSE
URL: https://github.com/matt-dray/r.oguelike, https://matt-dray.github.io/r.oguelike/
URL: https://github.com/matt-dray/r.oguelike,
https://matt-dray.github.io/r.oguelike/,
https://www.rostrum.blog/tags/r.oguelike/
BugReports: https://github.com/matt-dray/r.oguelike/issues
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.2
Imports:
crayon,
keypress
keypress,
sonify
Suggests:
covr,
testthat (>= 3.0.0)
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# r.oguelike 0.1.0

* Breaking: the `is_colour` argument to `start_game()` has been renamed with `has_colour`, which is a better verb for this purpose and matches the new `has_sfx` argument.
* Added sound effects (#9), including `has_sfx` argument to `start_game()`.
* Tweaked README given recent changes.

# r.oguelike 0.0.0.9009

* Fixed bug: ensured collecting one apple adds only one apple to the inventory (#31).
Expand Down
26 changes: 20 additions & 6 deletions R/play.R
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#' @param is_organic Logical. Join the room start points with corridors before
#' iterative growth steps (\code{TRUE}, the default), or after
#' (\code{FALSE})? See details.
#' @param is_colour Logical. Should the characters in the output be coloured
#' @param has_colour Logical. Should the characters in the output be coloured
#' using \code{\link[crayon]{crayon}} (\code{TRUE}, the default)?
#' @param has_sfx Logical. Should sound effects be used? Defaults to
#' \code{TRUE}.
#'
#' @details
#' Use the WASD keys to move up, left, down and right. Use the '1' key to eat
Expand Down Expand Up @@ -62,7 +64,8 @@ start_game <- function(
iterations = 4L,
is_snake = FALSE,
is_organic = TRUE,
is_colour = TRUE
has_colour = TRUE,
has_sfx = TRUE
) {

# Checks ----
Expand All @@ -84,9 +87,14 @@ start_game <- function(
stop("Arguments 'n_row' and 'n_col' must be 10 or greater.", call. = FALSE)
}

if (!is.logical(is_snake) | !is.logical(is_organic) | !is.logical(is_colour)) {
if (
!is.logical(is_snake) |
!is.logical(is_organic) |
!is.logical(has_colour) |
!is.logical(has_sfx)
) {
stop(
"Arguments 'is_snake', 'is_organic' and 'is_colour' must be logical.",
"Arguments 'is_snake', 'is_organic', 'has_colour' and 'has_sfx' must be logical.",
call. = FALSE
)
}
Expand Down Expand Up @@ -137,7 +145,7 @@ start_game <- function(

if (supports_keypress) system2("clear")
if (is_rstudio) cat("\014")
.cat_map(game_map, is_colour)
.cat_map(game_map, has_colour)
.cat_stats(turns, hp, gold, food)
message(status_msg)

Expand Down Expand Up @@ -182,6 +190,7 @@ start_game <- function(
food <- food - 1
hp <- hp + 1
status_msg <- "Ate apple (+1 HP)"
.sfx_apple_eat(has_sfx)
}

if (hp == max_hp) {
Expand All @@ -194,7 +203,7 @@ start_game <- function(

# Player actions ----

game_map <- .move_player(game_map, kp, player_loc)
game_map <- .move_player(game_map, kp, player_loc, has_sfx)
player_loc <- which(game_map == "@")

if (kp %in% c("up", "down", "left", "right")) {
Expand All @@ -204,11 +213,13 @@ start_game <- function(
if (length(gold_loc) > 0 && player_loc == gold_loc) {
gold <- gold + gold_rand
status_msg <- paste0("Found gold (+", gold_rand, " $)")
.sfx_gold(has_sfx)
}

if (length(food_loc) > 0 && player_loc == food_loc) {
food <- food + 1
status_msg <- "Collected apple (+1 a)"
.sfx_apple_collect(has_sfx)
}

if (length(enemy_loc) != 0 && player_loc == enemy_loc) {
Expand All @@ -222,6 +233,7 @@ start_game <- function(

if (enemy_hp == 0) {
status_msg <- paste0("Enemy defeated! (-", start_hp - hp," HP)")
.sfx_enemy_defeat(has_sfx)
enemy_loc <- NULL
is_battle <- FALSE
}
Expand Down Expand Up @@ -262,6 +274,7 @@ start_game <- function(
if (enemy_hp == 0) {
status_msg <- paste0("Enemy defeated! (-", start_hp - hp," HP)")
game_map[enemy_loc] <- "@" # overwrite position with player symbol
.sfx_enemy_defeat(has_sfx)
is_battle <- FALSE
}

Expand All @@ -288,6 +301,7 @@ start_game <- function(

if (turns == 0) {
message("You died (max turns)! Try again.")
.sfx_end(has_sfx)
is_alive <- FALSE
}

Expand Down
8 changes: 7 additions & 1 deletion R/utils-movement.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
#' 'right' to move, '1' to eat an apple, '0' to exit).
#' @param player_loc Numeric. Matrix index of the tile occupied by the player
#' character.
#' @param has_sfx Logical. Should sound effects be used? Defaults to
#' \code{TRUE}.
#' @noRd
.move_player <- function(
room,
kp = c("up", "down", "left", "right", "1", "0"),
player_loc
player_loc,
has_sfx
) {

if (!inherits(room, "matrix")) {
Expand Down Expand Up @@ -41,6 +44,9 @@

if (room[move_to] != "#") {
player_loc <- move_to
.sfx_move(has_sfx)
} else {
.sfx_edge(has_sfx)
}

}
Expand Down
6 changes: 3 additions & 3 deletions R/utils-print.R
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#' Concatenate And Print A Matrix Of The Game Map
#' @param room Matrix. 2D map layout.
#' @param is_colour Logical. Should the characters in the output be coloured
#' @param has_colour Logical. Should the characters in the output be coloured
#' using \code{\link[crayon]{crayon}} (\code{TRUE}, the default)?
#' @noRd
.cat_map <- function(game_map, is_colour = TRUE) {
.cat_map <- function(game_map, has_colour) {

if (!inherits(game_map, "matrix")) {
stop("Argument 'game_map' must be a matrix.")
}

if (is_colour) {
if (has_colour) {

game_map[which(game_map == ".")] <- crayon::black(".")
game_map[which(game_map == "#")] <- crayon::red("#")
Expand Down
66 changes: 66 additions & 0 deletions R/utils-sfx.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#' Sound Effect: Move Player
#' @param has_sfx Logical. Should sound effects be used? Defaults to \code{TRUE}.
#' @noRd
.sfx_move <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 1, y = 1, duration = 0.001)
}
}

#' Sound Effect: Bump Edge
#' @param has_sfx Logical. Should sound effects be used? Defaults to \code{TRUE}.
#' @noRd
.sfx_edge <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 1, y = 1, duration = 0.01, flim = c(100, 110))
}
}

#' Sound Effect: Collect Apple
#' @param has_sfx Logical. Should sound effects be used? Defaults to \code{TRUE}.
#' @noRd
.sfx_apple_collect <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 0:1, y = c(0, 1), duration = 0.05)
}
}

#' Sound Effect: Eat Apple
#' @param has_sfx Logical. Should sound effects be used? Defaults to \code{TRUE}.
#' @noRd
.sfx_apple_eat <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 0:1, y = c(1, 0), duration = 0.05)
}
}

#' Sound Effect: Collect Gold
#' @noRd
.sfx_gold <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 1, y = 1, duration = 0.05, flim = c(800, 800))
sonify::sonify(x = 1, y = 1, duration = 0.05, flim = c(1000, 1000))
}
}

#' Sound Effect: Defeat Enemy
#' @param has_sfx Logical. Should sound effects be used? Defaults to \code{TRUE}.
#' @noRd
.sfx_enemy_defeat <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 0:1, y = rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify::sonify(x = 0:1, y = rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify::sonify(x = 0:1, y = rep(1, 2), duration = 0.1, flim = c(800, 800))
}
}

#' Sound Effect: End Game
#' @param has_sfx Logical. Should sound effects be used? Defaults to \code{TRUE}.
#' @noRd
.sfx_end <- function(has_sfx) {
if (has_sfx) {
sonify::sonify(x = 0:1, y = rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify::sonify(x = 0:1, y = rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify::sonify(x = 0:1, y = rep(1, 2), duration = 0.1, flim = c(400, 400))
}
}
48 changes: 25 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,46 @@ Binder](http://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/matt-dra

<!-- badges: end -->

A toy demo of a tile-based [roguelike game](https://en.wikipedia.org/wiki/Roguelike) for R.
A tile-based [roguelike toy](https://en.wikipedia.org/wiki/Roguelike) for R's console.

Visit [the documentation website](https://matt-dray.github.io/r.oguelike/reference/start_game.html) or read more [in the inaugural blogpost](https://www.rostrum.blog/2022/04/25/r.oguelike-dev/). See [the issues](https://github.com/matt-dray/r.oguelike/issues) for future plans or to suggest improvements.
This is a proof-of-concept. Read more [in the blogposts](https://www.rostrum.blog/tags/r.oguelike/). Suggest improvements or fixes in [the issues](https://github.com/matt-dray/r.oguelike/issues).

You can install {r.oguelike} from GitHub via {remotes} (packages [{crayon}](https://github.com/r-lib/crayon) and [{keypress}](https://github.com/gaborcsardi/keypress) are also installed):
You can install {r.oguelike} from GitHub via [{remotes}](https://CRAN.R-project.org/package=remotes) (CRAN packages [{crayon}](https://CRAN.R-project.org/package=crayon), [{keypress}](https://CRAN.R-project.org/package=keypress) and [{sonify}](https://CRAN.R-project.org/package=sonify) are also installed):

``` r
if (!require(remotes)) install.packages("remotes")
install_github("matt-dray/r.oguelike")
remotes::install_github("matt-dray/r.oguelike")
```

You could also [launch an instance of RStudio in the browser](https://mybinder.org/v2/gh/matt-dray/play-r.oguelike/main?urlpath=rstudio), thanks to [Binder](https://mybinder.org/), with {r.oguelike} preinstalled.
You could also [launch an instance of RStudio in the browser](https://mybinder.org/v2/gh/matt-dray/play-r.oguelike/main?urlpath=rstudio) with {r.oguelike} preinstalled, thanks to [Binder](https://mybinder.org/).

Use `start_game()` to begin. You can adjust the default parameters; see `?start_game` or [visit the documentation website](https://matt-dray.github.io/r.oguelike/reference/start_game.html) for details.
Use `start_game()` to begin. See `?start_game` for details on how to adjust the starting parameters.

``` r
r.oguelike::start_game(
max_turns = 25,
iterations = 3,
n_row = 15,
n_col = 20,
n_rooms = 4,
max_turns = 25
n_row = 15,
n_col = 20,
n_rooms = 4
)
```

The console will clear and you’ll see a map, with an inventory bar, status message and prompt for input. Output will appear in colour with the argument `colour = TRUE` (the default).
The console will clear and then you’ll see a dungeon map, an inventory bar, a status message and a prompt for player input. Output will appear in colour if your console supports it.

```
# # # # # # # # # # # # # # # # # # # #
# # # # # # # . . . . . . . . # # # # #
# # # # # # # # . . . . $ . . # # # # #
# # # # # # # # . # # . . . . # # # # #
# . # # # # # # # # # . . . # # # # # #
# # # # # # . . . . . . $ . . # # # # #
# # # . . . . . . # # . . . . # # # # #
# . . . # # # # # # # . . . # # # # # #
# . . # # # # # # # # . . . # # # # # #
# . . . # # # # # # # . . . . . # . # #
# . . # # # # # # # . . . . . . . . # #
# . . # # # # # # # # . . . . . . # # #
# . @ . . . # # . # # # # . . . . . # #
# . . . . . . . . . . . . . . . . # # #
# . . . . . . . . . E . . . . . . # # #
# . . . . . . . . . E . # # . . . # # #
# . . a . . . . . . . . . . . . # # # #
# # . . . . # # . # # # # . # # # # # #
# # # # # # # # # # # # # # # # # # # #
Expand All @@ -60,25 +60,25 @@ Press W, A, S or D then Enter to move, 1 to eat apple, 0 to exit
Input:
```

The dungeon map (`#` for walls and `.` for floor tiles) and placement of objects (`@` is the player, `E` is an enemy, `$` is gold and `a` is an apple) are randomised. [See the accompanying blogpost](https://www.rostrum.blog/2022/05/01/dungeon/) for more about how these dungeons are generated.
The dungeon map (`#` for walls and `.` for floor tiles) is [procedurally-generated](https://www.rostrum.blog/2022/05/01/dungeon/) and the placement of objects (`@` is the player, `E` is an enemy, `$` is gold and `a` is an apple) is randomised.

You can move the player character (`@`) with your arrow keys instead of the <kbd>W</kbd>, <kbd>A</kbd>, <kbd>S</kbd> or <kbd>D</kbd> keys if you’re using a terminal that supports [the {keypress} package](https://github.com/gaborcsardi/keypress) (RStudio doesn't).
To move the player character (`@`), type one of [<kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd>](https://en.wikipedia.org/wiki/Arrow_keys#WASD_keys) at the prompt and hit <kbd>Enter</kbd>. If your terminal supports [{keypress}](https://github.com/gaborcsardi/keypress) (RStudio doesn't) then you'll be able to type a single arrow key instead.

After pressing <kbd>s</kbd> then <kbd>Enter</kbd> (or the down arrow key, if supported), the player character moves one space down and the status message updates. Note the enemy (`E`) is also heading in your direction...
After instructing the player character to move down, for example, the screen refreshes to show `@` is in a new location and the status message has been updated. Simple sound effects will play depending on the outcome of the move.

```
# # # # # # # # # # # # # # # # # # # #
# # # # # # # . . . . . . . . # # # # #
# # # # # # # # . . . . $ . . # # # # #
# # # # # # # # . # # . . . . # # # # #
# . # # # # # # # # # . . . # # # # # #
# # # # # # . . . . . . $ . . # # # # #
# # # . . . . . . # # . . . . # # # # #
# . . . # # # # # # # . . . # # # # # #
# . . # # # # # # # # . . . # # # # # #
# . . . # # # # # # # . . . . . # . # #
# . . # # # # # # # . . . . . . . . # #
# . . # # # # # # # # . . . . . . # # #
# . . . . . # # . # # # # . . . . . # #
# . @ . . . . . . . . . . . . . . # # #
# . . . . . . . . E . . . . . . . # # #
# . . . . . . . . E . . # # . . . # # #
# . . a . . . . . . . . . . . . # # # #
# # . . . . # # . # # # # . # # # # # #
# # # # # # # # # # # # # # # # # # # #
Expand All @@ -87,7 +87,9 @@ Moved down
Input:
```

Collect the gold (`$`). Auto-battle the randomly-moving enemy (`E`). Collect an apple (`a`) for your inventory, then eat it with a keypress input of `1`. You’ll die if you run out of `HP` or if you reach the maximum number of turns allowed (`T`). You can quit the game with `0`.
Also note that the enemy's (`E`) position has changed and it's [heading in your direction...](https://www.rostrum.blog/2022/06/10/basic-search/)

What now? Collect the gold (`$`). Auto-battle the chasing enemy (`E`). Collect an apple (`a`) for your inventory, then eat it with a keypress input of `1` to replenish health. You’ll die if you run out of `HP` or if you reach the maximum number of turns allowed (`T`). You can quit the game with `0`.

## Code of Conduct

Expand Down
3 changes: 2 additions & 1 deletion man/r.oguelike-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions man/start_game.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7d964f0

Please sign in to comment.