Skip to content

Commit

Permalink
Merge pull request #15 from benpollarduk/conversation-update
Browse files Browse the repository at this point in the history
Added tests, updated documentation
  • Loading branch information
benpollarduk authored Jan 29, 2024
2 parents 4236448 + bf3c0e9 commit e904ec2
Show file tree
Hide file tree
Showing 27 changed files with 645 additions and 41 deletions.
10 changes: 5 additions & 5 deletions docs/mkdocs/docs/non-playable-character.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ goblin.conversation = Conversation(
Paragraph("This is a the first line."),
Paragraph("This is a question.).also {
it.responses = listOf(
Response("This is the first response., 1),
Response("This is the second response.", 2),
Response("This is the third response.", 3)
Response("This is the first response., Jump(1)),
Response("This is the second response.", Jump(2)),
Response("This is the third response.", Jump(3))
)
},
Paragraph("You picked first response, return to start of conversation.", -2),
Paragraph("You picked second response, return to start of conversation., -2),
Paragraph("You picked first response, return to start of conversation.", GoTo(1)),
Paragraph("You picked second response, return to start of conversation., GoTo(1)),
Paragraph("This is the third response.") {
it.player.kill()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.github.benpollarduk.ktaf.assets.characters.NonPlayableCharacter
import com.github.benpollarduk.ktaf.conversations.Conversation
import com.github.benpollarduk.ktaf.conversations.Paragraph
import com.github.benpollarduk.ktaf.conversations.Response
import com.github.benpollarduk.ktaf.conversations.instructions.GoTo
import com.github.benpollarduk.ktaf.conversations.instructions.Jump
import com.github.benpollarduk.ktaf.utilities.templates.AssetTemplate

internal class Parrot : AssetTemplate<NonPlayableCharacter> {
Expand All @@ -17,14 +19,14 @@ internal class Parrot : AssetTemplate<NonPlayableCharacter> {
Paragraph("Squarrrkkk"),
Paragraph("Will you feed me?").also {
it.responses = listOf(
Response("I don't have any food to feed you with!", 1),
Response("Sure here's some food!", 2),
Response("I'll try bring you some.", 3)
Response("I don't have any food to feed you with!", Jump(1)),
Response("Sure here's some food!", Jump(2)),
Response("I'll try bring you some.", Jump(3))
)
},
Paragraph("Bring some next time then!", -1),
Paragraph("You don't have any food! Errkggg!", -2),
Paragraph("Thanks a lot! Erckgah!", -3)
Paragraph("Bring some next time then!", GoTo(1)),
Paragraph("You don't have any food! Errkggg!", GoTo(1)),
Paragraph("Thanks a lot! Erckgah!", GoTo(1))
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.github.benpollarduk.ktaf.conversations

import com.github.benpollarduk.ktaf.assets.interaction.Reaction
import com.github.benpollarduk.ktaf.assets.interaction.ReactionResult
import com.github.benpollarduk.ktaf.conversations.instructions.EndOfParagraphInstruction
import com.github.benpollarduk.ktaf.extensions.ensureFinishedSentence
import com.github.benpollarduk.ktaf.extensions.toSpeech
import com.github.benpollarduk.ktaf.logic.Game
Expand All @@ -13,8 +14,7 @@ import com.github.benpollarduk.ktaf.logic.Game
public class Conversation(
private val paragraphs: List<Paragraph> = emptyList()
) {
private var paragraphIndex: Int = 0
private var selectedResponseDelta: Int? = null
private var selectedResponseInstruction: EndOfParagraphInstruction? = null
private val mutableLog: MutableList<LogItem> = mutableListOf()

/**
Expand All @@ -30,19 +30,26 @@ public class Conversation(
get() = mutableLog.toList()

/**
* Shift this [Conversation] to a new [Paragraph]. The [delta] specifies the next [Paragraph], which is retrieved
* from the [paragraphs] array. If the [delta] shifts the conversation out of bounds then null is returned.
* Shift this [Conversation] to a new [Paragraph]. The [index] specifies the next [Paragraph], which is retrieved
* from the [paragraphs] array. If the [index] shifts the conversation out of bounds then the current paragraph is
* returned.
*/
private fun shift(delta: Int): Paragraph? {
paragraphIndex += delta

return if (paragraphIndex >= 0 && paragraphIndex < paragraphs.count()) {
paragraphs[paragraphIndex]
private fun shift(index: Int): Paragraph? {
return if (index >= 0 && index < paragraphs.count()) {
paragraphs[index]
} else {
currentParagraph
}
}

/**
* Update the [currentParagraph] based on an [instruction].
*/
private fun updateCurrentParagraph(instruction: EndOfParagraphInstruction) {
val current = currentParagraph ?: return
currentParagraph = shift(instruction.getIndexOfNext(current, paragraphs))
}

/**
* Trigger the next line in this [Conversation] to obtain a [Reaction].
*/
Expand All @@ -58,13 +65,13 @@ public class Conversation(
currentParagraph = paragraphs.firstOrNull()
}
current.canRespond -> {
selectedResponseDelta?.let { delta ->
currentParagraph = shift(delta)
selectedResponseDelta = null
selectedResponseInstruction?.let {
updateCurrentParagraph(it)
selectedResponseInstruction = null
} ?: return Reaction(ReactionResult.INTERNAL, "Awaiting response.")
}
else -> {
currentParagraph = shift(current.delta)
updateCurrentParagraph(current.instruction)
}
}

Expand All @@ -86,7 +93,7 @@ public class Conversation(
return@respond Reaction(ReactionResult.ERROR, "Invalid response.")
}
mutableLog.add(LogItem(Participant.PLAYER, response.line.ensureFinishedSentence()))
selectedResponseDelta = response.delta
selectedResponseInstruction = response.instruction
return@respond next(game)
} ?: return Reaction(ReactionResult.ERROR, "No paragraph.")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.github.benpollarduk.ktaf.conversations

import com.github.benpollarduk.ktaf.conversations.instructions.EndOfParagraphInstruction
import com.github.benpollarduk.ktaf.conversations.instructions.Next

/**
* A paragraph within a [Conversation]. Must contain a [line], but can also have an optional [action] that is invoked
* as a response to this paragraph being triggered. The [delta] is a relative pointer to the next element within the
* [Conversation].
* as a response to this paragraph being triggered. The [instruction] is applied to direct the conversation after this
* paragraph.
*/
public class Paragraph(
public val line: String,
public val delta: Int = 1,
public val instruction: EndOfParagraphInstruction = Next(),
public val name: String = "",
public val action: ConversationAction = { }
) {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package com.github.benpollarduk.ktaf.conversations

import com.github.benpollarduk.ktaf.conversations.instructions.EndOfParagraphInstruction
import com.github.benpollarduk.ktaf.conversations.instructions.Next

/**
* A response which forms part of a [Conversation]. The [line] forms the body of the response and the [delta] is a
* relative pointer to the next element within the [Conversation].
* A response which forms part of a [Conversation]. The [line] forms the body of the response and the [instruction] is
* applied to direct the conversation after this paragraph.
*/
public data class Response(public val line: String, public val delta: Int = 1)
public data class Response(
public val line: String,
public val instruction: EndOfParagraphInstruction = Next()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* Provides a contract for providing instructions for ends of paragraphs.
*/
public fun interface EndOfParagraphInstruction {
/**
* Get the index of the next paragraph from a [current] paragraph and an array of [paragraphs].
*/
public fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs to the start.
*/
public class First : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
return 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs based on an absolute [index].
*/
public class GoTo(public val index: Int) : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
return if (index < 0) {
0
} else if (index >= paragraphs.size) {
paragraphs.size - 1
} else {
index
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs based on a delta.
*/
public class Jump(public val delta: Int) : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
val index = paragraphs.indexOf(current)
val offset = index + delta
return if (offset < 0) {
0
} else if (offset >= paragraphs.size) {
paragraphs.size - 1
} else {
offset
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs to the end.
*/
public class Last : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
return paragraphs.size - 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs to the next paragraph.
*/
public class Next : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
val index = paragraphs.indexOf(current)
return if (index == -1) {
0
} else if (index < paragraphs.size - 1) {
index + 1
} else {
paragraphs.size - 1
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs to the previous paragraph.
*/
public class Previous : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
val index = paragraphs.indexOf(current)
return if (index > 0) {
index - 1
} else {
0
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that repeats.
*/
public class Repeat : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
val index = paragraphs.indexOf(current)
return if (index < 0) {
0
} else {
index
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.benpollarduk.ktaf.conversations.instructions

import com.github.benpollarduk.ktaf.conversations.Paragraph

/**
* An end of paragraph instruction that shifts paragraphs based on a name.
*/
public class ToName(public val name: String) : EndOfParagraphInstruction {
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int {
val target = paragraphs.firstOrNull {
it.name.equals(name, true)
}

return if (target != null) {
paragraphs.indexOf(target)
} else {
0
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.github.benpollarduk.ktaf.assets.locations.Room
import com.github.benpollarduk.ktaf.conversations.Conversation
import com.github.benpollarduk.ktaf.conversations.Paragraph
import com.github.benpollarduk.ktaf.conversations.Response
import com.github.benpollarduk.ktaf.conversations.instructions.Previous
import com.github.benpollarduk.ktaf.io.IOConfiguration
import com.github.benpollarduk.ktaf.logic.Game
import com.github.benpollarduk.ktaf.logic.GameInformation
Expand Down Expand Up @@ -46,7 +47,7 @@ internal object TestGame : GameTemplate() {
Paragraph("Here is a question?").also { paragraph ->
paragraph.responses = listOf(
Response("Continue"),
Response("Repeat", -1)
Response("Repeat", Previous())
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.github.benpollarduk.ktaf.assets.characters.NonPlayableCharacter
import com.github.benpollarduk.ktaf.assets.interaction.ReactionResult
import com.github.benpollarduk.ktaf.commands.conversation.Respond
import com.github.benpollarduk.ktaf.conversations.Response
import com.github.benpollarduk.ktaf.conversations.instructions.Next
import com.github.benpollarduk.ktaf.logic.GameTestHelper
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
Expand All @@ -12,7 +13,7 @@ class RespondTest {
@Test
fun `given no converser when invoke then return error`() {
// Given
val command = Respond(Response("", 1))
val command = Respond(Response("", Next()))

// When
val result = command.invoke(GameTestHelper.getBlankGame())
Expand All @@ -26,7 +27,7 @@ class RespondTest {
// Given
val game = GameTestHelper.getBlankGame()
val npc = NonPlayableCharacter("", "")
val command = Respond(Response("", 1))
val command = Respond(Response("", Next()))
game.startConversation(npc)

// When
Expand All @@ -40,7 +41,7 @@ class RespondTest {
fun `given converser when invoke with valid response then return internal`() {
// Given
val game = GameTestHelper.getBlankGame()
val response = Response("", 1)
val response = Response("", Next())
val npc = NonPlayableCharacter("", "")
val command = Respond(response)
game.startConversation(npc)
Expand Down
Loading

0 comments on commit e904ec2

Please sign in to comment.