-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from benpollarduk/attributes
Attributes
- Loading branch information
Showing
16 changed files
with
638 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Attributes | ||
|
||
## Overview | ||
All examinable objects can have attributes. Attributes provide a way of adding a lot of depth to games. For example, | ||
attributes could be used to buy and sell items, contain a characters XP or HP or even provide a way to add durability | ||
to items. | ||
|
||
## Use | ||
To add to an existing attribute or to create a new one use the **add** function. | ||
|
||
```kotlin | ||
var player = PlayableCharacter("Player", "") | ||
player.attributes.add("$", 10) | ||
``` | ||
|
||
To subtract from an existing attribute use the **subtract** function. | ||
|
||
```kotlin | ||
player.attributes.subtract("$", 10) | ||
``` | ||
|
||
Attributes values can be capped. In this example the $ attribute is limited to a range of 0 - 100. Adding or | ||
subtracting will not cause the value of the attribute to change outside of this range. | ||
|
||
```kotlin | ||
var cappedAttribute = Attribute("$", "Dollars.", 0, 100) | ||
player.attributes.add(cappedAttribute, 50) | ||
``` | ||
|
||
## An example - buying an Item from a NonPlayableCharacter. | ||
The following is an example of buying an Item from NonPlayableCharacter. Here a trader has a spade. The player can only | ||
buy the spade if they have at least $5. The conversation will jump to the correct paragraph based on if they choose to | ||
buy the spade or not. If the player chooses to buy the spade and has enough $ the transaction is made and the spade | ||
changes hands. | ||
|
||
```kotlin | ||
val currency = "$" | ||
val spade = Item("Spade", "") | ||
|
||
val player = PlayableCharacter("Player", "").apply { | ||
attributes.add(currency, 10) | ||
} | ||
|
||
val trader = NonPlayableCharacter("Trader", "").apply { | ||
acquireItem(spade) | ||
conversation = Conversation( | ||
listOf( | ||
Paragraph("What will you buy").also { | ||
it.responses = listOf( | ||
Response("Spade.", ByCallback { | ||
if (player.attributes.getValue(currency) >= 5) { | ||
ToName("BoughtSpade") | ||
} else { | ||
ToName("NotEnough") | ||
} | ||
}), | ||
Response("Nothing.", Last()), | ||
) | ||
}, | ||
Paragraph("Here it is.", First(), "BoughtSpade") { | ||
player.attributes.subtract(currency, 5) | ||
attributes.add(currency, 5) | ||
give(spade, player) | ||
}, | ||
Paragraph("You don't have enough money.", First(), "NotEnough"), | ||
Paragraph("Fine.") | ||
), | ||
) | ||
} | ||
``` | ||
This is just one example of using attributes to add depth to a game. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
ktaf/src/main/kotlin/com/github/benpollarduk/ktaf/assets/attributes/Attribute.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.github.benpollarduk.ktaf.assets.attributes | ||
|
||
/** | ||
* An attribute with a specified [name], [description] and an optional [minimum] and [maximum] value. | ||
*/ | ||
public data class Attribute( | ||
public val name: String, | ||
public val description: String, | ||
public val minimum: Int = Int.MIN_VALUE, | ||
public val maximum: Int = Int.MAX_VALUE | ||
) |
167 changes: 167 additions & 0 deletions
167
ktaf/src/main/kotlin/com/github/benpollarduk/ktaf/assets/attributes/AttributeManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package com.github.benpollarduk.ktaf.assets.attributes | ||
|
||
import com.github.benpollarduk.ktaf.extensions.insensitiveEquals | ||
|
||
/** | ||
* Provides a class for managing [Attribute]. | ||
*/ | ||
public class AttributeManager { | ||
private val attributes: MutableMap<Attribute, Int> = mutableMapOf() | ||
|
||
/** | ||
* The number of [Attribute] managed by this [AttributeManager]. | ||
*/ | ||
public val count: Int | ||
get() = attributes.size | ||
|
||
/** | ||
* Ensure an attribute with a specified [name] exists. If it doesn't it is added. | ||
*/ | ||
private fun ensureAttributeExists(name: String) { | ||
val attribute = attributes.keys.firstOrNull { it.name.insensitiveEquals(name) } | ||
|
||
if (attribute != null) { | ||
return | ||
} | ||
|
||
val newAttribute = Attribute(name, "") | ||
attributes[newAttribute] = 0 | ||
} | ||
|
||
/** | ||
* Ensure a specified [attribute] exists. If it doesn't it is added. | ||
*/ | ||
private fun ensureAttributeExists(attribute: Attribute) { | ||
val match = attributes.keys.firstOrNull { it.name.insensitiveEquals(attribute.name) } | ||
|
||
if (match != null) { | ||
return | ||
} | ||
|
||
attributes[attribute] = 0 | ||
} | ||
|
||
/** | ||
* Get an [Attribute] that matches a specified [key]. | ||
*/ | ||
private fun getMatch(key: Attribute): Attribute { | ||
ensureAttributeExists(key) | ||
return attributes.keys.first { it.name == key.name } | ||
} | ||
|
||
/** | ||
* Cap a [value] between a [min] and [max] value. | ||
*/ | ||
private fun capValue(value: Int, min: Int, max: Int): Int { | ||
return when { | ||
value < min -> min | ||
value > max -> max | ||
else -> value | ||
} | ||
} | ||
|
||
/** | ||
* Get all [Attribute] managed by this [AttributeManager]. | ||
*/ | ||
public fun getAttributes(): Array<Attribute> { | ||
return attributes.keys.toTypedArray() | ||
} | ||
|
||
/** | ||
* Get all [Attribute] and values managed by this [AttributeManager]. | ||
*/ | ||
public fun toMap(): Map<Attribute, Int> { | ||
return attributes.toMap() | ||
} | ||
|
||
/** | ||
* Get the value of an attribute from a specified [attributeName]. | ||
*/ | ||
public fun getValue(attributeName: String): Int { | ||
val attribute = attributes.keys.firstOrNull { it.name.insensitiveEquals(attributeName) } ?: return 0 | ||
return getValue(attribute) | ||
} | ||
|
||
/** | ||
* Get the value of an [attribute]. | ||
*/ | ||
public fun getValue(attribute: Attribute): Int { | ||
return attributes[attribute] ?: 0 | ||
} | ||
|
||
/** | ||
* Add an attribute with a specified [attributeName] and [value]. | ||
*/ | ||
public fun add(attributeName: String, value: Int) { | ||
ensureAttributeExists(attributeName) | ||
val attribute = attributes.keys.first { it.name.insensitiveEquals(attributeName) } | ||
attributes[attribute] = capValue( | ||
attributes[attribute]?.plus(value) ?: 0, | ||
attribute.minimum, | ||
attribute.maximum | ||
) | ||
} | ||
|
||
/** | ||
* Add an [attribute] with a specified [value]. | ||
*/ | ||
public fun add(attribute: Attribute, value: Int) { | ||
val matchedAttribute = getMatch(attribute) | ||
attributes[matchedAttribute] = capValue( | ||
attributes[matchedAttribute]?.plus(value) ?: 0, | ||
matchedAttribute.minimum, | ||
matchedAttribute.maximum | ||
) | ||
} | ||
|
||
/** | ||
* Subtract a [value] from an attribute with a specified [attributeName]. | ||
*/ | ||
public fun subtract(attributeName: String, value: Int) { | ||
ensureAttributeExists(attributeName) | ||
val attribute = attributes.keys.first { it.name.insensitiveEquals(attributeName) } | ||
attributes[attribute] = capValue( | ||
attributes[attribute]?.minus(value) ?: 0, | ||
attribute.minimum, | ||
attribute.maximum | ||
) | ||
} | ||
|
||
/** | ||
* Subtract a [value] from an [attribute]. | ||
*/ | ||
public fun subtract(attribute: Attribute, value: Int) { | ||
val matchedAttribute = getMatch(attribute) | ||
attributes[matchedAttribute] = capValue( | ||
attributes[matchedAttribute]?.minus(value) ?: 0, | ||
matchedAttribute.minimum, | ||
matchedAttribute.maximum | ||
) | ||
} | ||
|
||
/** | ||
* Remove an attribute with a specified [attributeName]. | ||
*/ | ||
public fun remove(attributeName: String) { | ||
val attribute = attributes.keys.firstOrNull { it.name.insensitiveEquals(attributeName) } | ||
|
||
if (attribute != null) { | ||
attributes.remove(attribute) | ||
} | ||
} | ||
|
||
/** | ||
* Remove an [attribute]. | ||
*/ | ||
public fun remove(attribute: Attribute) { | ||
val matchedAttribute = getMatch(attribute) | ||
attributes.remove(matchedAttribute) | ||
} | ||
|
||
/** | ||
* Remove all [attributes]. | ||
*/ | ||
public fun removeAll() { | ||
attributes.clear() | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
ktaf/src/main/kotlin/com/github/benpollarduk/ktaf/conversations/instructions/ByCallback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 based on a callback. | ||
*/ | ||
public class ByCallback(public val callback: () -> EndOfParagraphInstruction) : EndOfParagraphInstruction { | ||
override fun getIndexOfNext(current: Paragraph, paragraphs: List<Paragraph>): Int { | ||
return callback().getIndexOfNext(current, paragraphs) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.