So much lint

This commit is contained in:
nelle 2023-10-30 11:23:31 -06:00
parent 2e1cbf32c1
commit f4c056c233
29 changed files with 886 additions and 85 deletions

View file

@ -1,7 +1,7 @@
# PotRogue
### A WIP, opensource, roguelike project built in [Kotlin](https://kotlinlang.org/), utilizing [Zircon](https://hexworks.org/projects/zircon/).
The plan is to be able to self-contain the entire game on a DVD/Blue-Ray once "completed." thatd be so cool and so epic.
### For now, please make issues on [The mirror repo](https://next.forgejo.org/Ouroboros/potrogue/issues) as ForgeFed has not yet been implemented in mainline ForgeJo, and this instance does not have an open registration
## Installation

View file

@ -11,7 +11,9 @@ val version: String by project
plugins {
kotlin("jvm") version "1.9.10"
`kotlin-dsl`
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.gitlab.arturbosch.detekt") version("1.23.1")
}
repositories {

784
config/detekt/detekt.yml Normal file
View file

@ -0,0 +1,784 @@
build:
maxIssues: 0
excludeCorrectable: false
weights:
# complexity: 2
# LongParameterList: 1
# style: 1
# comments: 1
config:
validation: true
warningsAsErrors: false
checkExhaustiveness: false
# when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
excludes: ''
processors:
active: true
exclude:
- 'DetektProgressListener'
# - 'KtFileCountProcessor'
# - 'PackageCountProcessor'
# - 'ClassCountProcessor'
# - 'FunctionCountProcessor'
# - 'PropertyCountProcessor'
# - 'ProjectComplexityProcessor'
# - 'ProjectCognitiveComplexityProcessor'
# - 'ProjectLLOCProcessor'
# - 'ProjectCLOCProcessor'
# - 'ProjectLOCProcessor'
# - 'ProjectSLOCProcessor'
# - 'LicenseHeaderLoaderExtension'
console-reports:
active: true
exclude:
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
- 'FindingsReport'
- 'FileBasedFindingsReport'
# - 'LiteFindingsReport'
output-reports:
active: true
exclude:
# - 'TxtOutputReport'
# - 'XmlOutputReport'
# - 'HtmlOutputReport'
# - 'MdOutputReport'
# - 'SarifOutputReport'
comments:
active: true
AbsentOrWrongFileLicense:
active: false
licenseTemplateFile: 'license.template'
licenseTemplateIsRegex: false
CommentOverPrivateFunction:
active: false
CommentOverPrivateProperty:
active: false
DeprecatedBlockTag:
active: false
EndOfSentenceFormat:
active: false
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
KDocReferencesNonPublicProperty:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
OutdatedDocumentation:
active: false
matchTypeParameters: true
matchDeclarationsOrder: true
allowParamOnConstructorProperties: false
UndocumentedPublicClass:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
searchInNestedClass: true
searchInInnerClass: true
searchInInnerObject: true
searchInInnerInterface: true
searchInProtectedClass: false
UndocumentedPublicFunction:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
searchProtectedFunction: false
UndocumentedPublicProperty:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
searchProtectedProperty: false
complexity:
active: true
CognitiveComplexMethod:
active: false
threshold: 15
ComplexCondition:
active: true
threshold: 4
ComplexInterface:
active: false
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
ignoreOverloaded: false
CyclomaticComplexMethod:
active: true
threshold: 15
ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false
nestingFunctions:
- 'also'
- 'apply'
- 'forEach'
- 'isNotNull'
- 'ifNull'
- 'let'
- 'run'
- 'use'
- 'with'
LabeledExpression:
active: false
ignoredLabels: []
LargeClass:
active: true
threshold: 600
LongMethod:
active: true
threshold: 60
LongParameterList:
active: true
functionThreshold: 6
constructorThreshold: 7
ignoreDefaultParameters: false
ignoreDataClasses: true
ignoreAnnotatedParameter: []
MethodOverloading:
active: false
threshold: 6
NamedArguments:
active: false
threshold: 3
ignoreArgumentsMatchingNames: false
NestedBlockDepth:
active: true
threshold: 4
NestedScopeFunctions:
active: false
threshold: 1
functions:
- 'kotlin.apply'
- 'kotlin.run'
- 'kotlin.with'
- 'kotlin.let'
- 'kotlin.also'
ReplaceSafeCallChainWithRun:
active: false
StringLiteralDuplication:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
threshold: 3
ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^'
TooManyFunctions:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
thresholdInFiles: 11
thresholdInClasses: 11
thresholdInInterfaces: 11
thresholdInObjects: 11
thresholdInEnums: 11
ignoreDeprecated: false
ignorePrivate: false
ignoreOverridden: false
coroutines:
active: true
GlobalCoroutineUsage:
active: false
InjectDispatcher:
active: true
dispatcherNames:
- 'IO'
- 'Default'
- 'Unconfined'
RedundantSuspendModifier:
active: true
SleepInsteadOfDelay:
active: true
SuspendFunSwallowedCancellation:
active: false
SuspendFunWithCoroutineScopeReceiver:
active: false
SuspendFunWithFlowReturnType:
active: true
empty-blocks:
active: true
EmptyCatchBlock:
active: true
allowedExceptionNameRegex: '_|(ignore|expected).*'
EmptyClassBlock:
active: true
EmptyDefaultConstructor:
active: true
EmptyDoWhileBlock:
active: true
EmptyElseBlock:
active: true
EmptyFinallyBlock:
active: true
EmptyForBlock:
active: true
EmptyFunctionBlock:
active: true
ignoreOverridden: false
EmptyIfBlock:
active: true
EmptyInitBlock:
active: true
EmptyKtFile:
active: true
EmptySecondaryConstructor:
active: true
EmptyTryBlock:
active: true
EmptyWhenBlock:
active: true
EmptyWhileBlock:
active: true
exceptions:
active: true
ExceptionRaisedInUnexpectedLocation:
active: true
methodNames:
- 'equals'
- 'finalize'
- 'hashCode'
- 'toString'
InstanceOfCheckForException:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
NotImplementedDeclaration:
active: false
ObjectExtendsThrowable:
active: false
PrintStackTrace:
active: true
RethrowCaughtException:
active: true
ReturnFromFinally:
active: true
ignoreLabeled: false
SwallowedException:
active: true
ignoredExceptionTypes:
- 'InterruptedException'
- 'MalformedURLException'
- 'NumberFormatException'
- 'ParseException'
allowedExceptionNameRegex: '_|(ignore|expected).*'
ThrowingExceptionFromFinally:
active: true
ThrowingExceptionInMain:
active: false
ThrowingExceptionsWithoutMessageOrCause:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
exceptions:
- 'ArrayIndexOutOfBoundsException'
- 'Exception'
- 'IllegalArgumentException'
- 'IllegalMonitorStateException'
- 'IllegalStateException'
- 'IndexOutOfBoundsException'
- 'NullPointerException'
- 'RuntimeException'
- 'Throwable'
ThrowingNewInstanceOfSameException:
active: true
TooGenericExceptionCaught:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
exceptionNames:
- 'ArrayIndexOutOfBoundsException'
- 'Error'
- 'Exception'
- 'IllegalMonitorStateException'
- 'IndexOutOfBoundsException'
- 'NullPointerException'
- 'RuntimeException'
- 'Throwable'
allowedExceptionNameRegex: '_|(ignore|expected).*'
TooGenericExceptionThrown:
active: true
exceptionNames:
- 'Error'
- 'Exception'
- 'RuntimeException'
- 'Throwable'
naming:
active: true
BooleanPropertyNaming:
active: false
allowedPattern: '^(is|has|are)'
ClassNaming:
active: true
classPattern: '[A-Z][a-zA-Z0-9]*'
ConstructorParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
privateParameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
EnumNaming:
active: true
enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
ForbiddenClassName:
active: false
forbiddenName: []
FunctionMaxLength:
active: false
maximumFunctionNameLength: 30
FunctionMinLength:
active: false
minimumFunctionNameLength: 3
FunctionNaming:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
functionPattern: '[a-z][a-zA-Z0-9]*'
excludeClassPattern: '$^'
FunctionParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
InvalidPackageDeclaration:
active: true
rootPackage: ''
requireRootInDeclaration: false
LambdaParameterNaming:
active: false
parameterPattern: '[a-z][A-Za-z0-9]*|_'
MatchingDeclarationName:
active: true
mustBeFirst: true
MemberNameEqualsClassName:
active: true
ignoreOverridden: true
NoNameShadowing:
active: true
NonBooleanPropertyPrefixedWithIs:
active: false
ObjectPropertyNaming:
active: true
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
PackageNaming:
active: true
packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
TopLevelPropertyNaming:
active: true
constantPattern: '[A-Z][_A-Z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
VariableMaxLength:
active: false
maximumVariableNameLength: 64
VariableMinLength:
active: false
minimumVariableNameLength: 1
VariableNaming:
active: true
variablePattern: '[a-z][A-Za-z0-9]*'
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
performance:
active: true
ArrayPrimitive:
active: true
CouldBeSequence:
active: false
threshold: 3
ForEachOnRange:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
SpreadOperator:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
UnnecessaryPartOfBinaryExpression:
active: false
UnnecessaryTemporaryInstantiation:
active: true
potential-bugs:
active: true
AvoidReferentialEquality:
active: true
forbiddenTypePatterns:
- 'kotlin.String'
CastNullableToNonNullableType:
active: false
CastToNullableType:
active: false
Deprecation:
active: false
DontDowncastCollectionTypes:
active: false
DoubleMutabilityForCollection:
active: true
mutableTypes:
- 'kotlin.collections.MutableList'
- 'kotlin.collections.MutableMap'
- 'kotlin.collections.MutableSet'
- 'java.util.ArrayList'
- 'java.util.LinkedHashSet'
- 'java.util.HashSet'
- 'java.util.LinkedHashMap'
- 'java.util.HashMap'
ElseCaseInsteadOfExhaustiveWhen:
active: false
ignoredSubjectTypes: []
EqualsAlwaysReturnsTrueOrFalse:
active: true
EqualsWithHashCodeExist:
active: true
ExitOutsideMain:
active: false
ExplicitGarbageCollectionCall:
active: true
HasPlatformType:
active: true
IgnoredReturnValue:
active: true
restrictToConfig: true
returnValueAnnotations:
- 'CheckResult'
- '*.CheckResult'
- 'CheckReturnValue'
- '*.CheckReturnValue'
ignoreReturnValueAnnotations:
- 'CanIgnoreReturnValue'
- '*.CanIgnoreReturnValue'
returnValueTypes:
- 'kotlin.sequences.Sequence'
- 'kotlinx.coroutines.flow.*Flow'
- 'java.util.stream.*Stream'
ignoreFunctionCall: []
ImplicitDefaultLocale:
active: true
ImplicitUnitReturnType:
active: false
allowExplicitReturnType: true
InvalidRange:
active: true
IteratorHasNextCallsNextMethod:
active: true
IteratorNotThrowingNoSuchElementException:
active: true
LateinitUsage:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
ignoreOnClassesPattern: ''
MapGetWithNotNullAssertionOperator:
active: true
MissingPackageDeclaration:
active: false
excludes: ['**/*.kts']
NullCheckOnMutableProperty:
active: false
NullableToStringCall:
active: false
PropertyUsedBeforeDeclaration:
active: false
UnconditionalJumpStatementInLoop:
active: false
UnnecessaryNotNullCheck:
active: false
UnnecessaryNotNullOperator:
active: true
UnnecessarySafeCall:
active: true
UnreachableCatchBlock:
active: true
UnreachableCode:
active: true
UnsafeCallOnNullableType:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
UnsafeCast:
active: true
UnusedUnaryOperator:
active: true
UselessPostfixExpression:
active: true
WrongEqualsTypeParameter:
active: true
style:
active: true
AlsoCouldBeApply:
active: false
BracesOnIfStatements:
active: false
singleLine: 'never'
multiLine: 'always'
BracesOnWhenStatements:
active: false
singleLine: 'necessary'
multiLine: 'consistent'
CanBeNonNullable:
active: false
CascadingCallWrapping:
active: false
includeElvis: true
ClassOrdering:
active: false
CollapsibleIfStatements:
active: false
DataClassContainsFunctions:
active: false
conversionFunctionPrefix:
- 'to'
allowOperators: false
DataClassShouldBeImmutable:
active: false
DestructuringDeclarationWithTooManyEntries:
active: true
maxDestructuringEntries: 3
DoubleNegativeLambda:
active: false
negativeFunctions:
- reason: 'Use `takeIf` instead.'
value: 'takeUnless'
- reason: 'Use `all` instead.'
value: 'none'
negativeFunctionNameParts:
- 'not'
- 'non'
EqualsNullCall:
active: true
EqualsOnSignatureLine:
active: false
ExplicitCollectionElementAccessMethod:
active: false
ExplicitItLambdaParameter:
active: true
ExpressionBodySyntax:
active: false
includeLineWrapping: false
ForbiddenAnnotation:
active: false
annotations:
- reason: 'it is a java annotation. Use `Suppress` instead.'
value: 'java.lang.SuppressWarnings'
- reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.'
value: 'java.lang.Deprecated'
- reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.'
value: 'java.lang.annotation.Documented'
- reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.'
value: 'java.lang.annotation.Target'
- reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.'
value: 'java.lang.annotation.Retention'
- reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.'
value: 'java.lang.annotation.Repeatable'
- reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265'
value: 'java.lang.annotation.Inherited'
ForbiddenComment:
active: true
comments:
- reason: 'Forbidden FIXME todo marker in comment, please fix the problem.'
value: 'FIXME:'
- reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.'
value: 'STOPSHIP:'
- reason: 'Forbidden TODO todo marker in comment, please do the changes.'
value: 'TODO:'
allowedPatterns: ''
ForbiddenImport:
active: false
imports: []
forbiddenPatterns: ''
ForbiddenMethodCall:
active: false
methods:
- reason: 'print does not allow you to configure the output stream. Use a logger instead.'
value: 'kotlin.io.print'
- reason: 'println does not allow you to configure the output stream. Use a logger instead.'
value: 'kotlin.io.println'
ForbiddenSuppress:
active: false
rules: []
ForbiddenVoid:
active: true
ignoreOverridden: false
ignoreUsageInGenerics: false
FunctionOnlyReturningConstant:
active: true
ignoreOverridableFunction: true
ignoreActualFunction: true
excludedFunctions: []
LoopWithTooManyJumpStatements:
active: true
maxJumpCount: 1
MagicNumber:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts']
ignoreNumbers:
- '-1'
- '0'
- '1'
- '2'
ignoreHashCodeFunction: true
ignorePropertyDeclaration: false
ignoreLocalVariableDeclaration: false
ignoreConstantDeclaration: true
ignoreCompanionObjectPropertyDeclaration: true
ignoreAnnotation: false
ignoreNamedArgument: true
ignoreEnums: false
ignoreRanges: false
ignoreExtensionFunctions: true
MandatoryBracesLoops:
active: false
MaxChainedCallsOnSameLine:
active: false
maxChainedCalls: 5
MaxLineLength:
active: true
maxLineLength: 120
excludePackageStatements: true
excludeImportStatements: true
excludeCommentStatements: false
excludeRawStrings: true
MayBeConst:
active: true
ModifierOrder:
active: true
MultilineLambdaItParameter:
active: false
MultilineRawStringIndentation:
active: false
indentSize: 4
trimmingMethods:
- 'trimIndent'
- 'trimMargin'
NestedClassesVisibility:
active: true
NewLineAtEndOfFile:
active: true
NoTabs:
active: false
NullableBooleanCheck:
active: false
ObjectLiteralToLambda:
active: true
OptionalAbstractKeyword:
active: true
OptionalUnit:
active: false
PreferToOverPairSyntax:
active: false
ProtectedMemberInFinalClass:
active: true
RedundantExplicitType:
active: false
RedundantHigherOrderMapUsage:
active: true
RedundantVisibilityModifierRule:
active: false
ReturnCount:
active: true
max: 2
excludedFunctions:
- 'equals'
excludeLabeled: false
excludeReturnFromLambda: true
excludeGuardClauses: false
SafeCast:
active: true
SerialVersionUIDInSerializableClass:
active: true
SpacingBetweenPackageAndImports:
active: false
StringShouldBeRawString:
active: false
maxEscapedCharacterCount: 2
ignoredCharacters: []
ThrowsCount:
active: true
max: 2
excludeGuardClauses: false
TrailingWhitespace:
active: false
TrimMultilineRawString:
active: false
trimmingMethods:
- 'trimIndent'
- 'trimMargin'
UnderscoresInNumericLiterals:
active: false
acceptableLength: 4
allowNonStandardGrouping: false
UnnecessaryAbstractClass:
active: true
UnnecessaryAnnotationUseSiteTarget:
active: false
UnnecessaryApply:
active: true
UnnecessaryBackticks:
active: false
UnnecessaryBracesAroundTrailingLambda:
active: false
UnnecessaryFilter:
active: true
UnnecessaryInheritance:
active: true
UnnecessaryInnerClass:
active: false
UnnecessaryLet:
active: false
UnnecessaryParentheses:
active: false
allowForUnclearPrecedence: false
UntilInsteadOfRangeTo:
active: false
UnusedImports:
active: false
UnusedParameter:
active: true
allowedNames: 'ignored|expected'
UnusedPrivateClass:
active: true
UnusedPrivateMember:
active: true
allowedNames: ''
UnusedPrivateProperty:
active: true
allowedNames: '_|ignored|expected|serialVersionUID'
UseAnyOrNoneInsteadOfFind:
active: true
UseArrayLiteralsInAnnotations:
active: true
UseCheckNotNull:
active: true
UseCheckOrError:
active: true
UseDataClass:
active: false
allowVars: false
UseEmptyCounterpart:
active: false
UseIfEmptyOrIfBlank:
active: false
UseIfInsteadOfWhen:
active: false
ignoreWhenContainingVariableDeclaration: false
UseIsNullOrEmpty:
active: true
UseLet:
active: false
UseOrEmpty:
active: true
UseRequire:
active: true
UseRequireNotNull:
active: true
UseSumOfInsteadOfFlatMapSize:
active: false
UselessCallOnNotNull:
active: true
UtilityClassWithPublicConstructor:
active: true
VarCouldBeVal:
active: true
ignoreLateinitVar: false
WildcardImport:
active: true
excludeImports:
- 'java.util.*'

View file

@ -5,14 +5,13 @@ import group.ouroboros.potrogue.data.config.GameConfig
import group.ouroboros.potrogue.view.StartView
import org.hexworks.zircon.api.SwingApplications
//Important Values
const val GAME_ID = "PotRogue";
const val GAME_VER = "0.1.0-DEV";
// Important Values
const val GAME_ID = "PotRogue"
const val GAME_VER = "0.1.0-DEV"
fun main(args: Array<String>) {
fun main() {
Config()
//Start Application
// Start Application
val grid = SwingApplications.startTileGrid(GameConfig.buildAppConfig())
StartView(grid).dock()
}

View file

@ -15,7 +15,7 @@ import org.hexworks.zircon.api.data.base.BaseBlock
class GameBlock(
private var defaultTile: Tile = WALL,
// We added currentEntities which is just a mutable list of Entity objects which is empty by default
// We added currentEntities, which is just a mutable list of Entity objects, which is empty by default
private val currentEntities: MutableList<GameEntity<EntityType>> = mutableListOf(),
) : BaseBlock<Tile>(
emptyTile = Tile.empty(),
@ -44,10 +44,12 @@ class GameBlock(
get() = Maybe.ofNullable(currentEntities.firstOrNull { it.occupiesBlock })
val isOccupied: Boolean
get() = occupier.isPresent // Note how we tell whether a block is occupied by checking for the presence of an occupier
get() = occupier.isPresent
// Note how we tell whether a block is occupied by checking for the presence of an occupier
// Exposed a getter for entities which takes a snapshot (defensive copy) of the current entities and returns them.
// We do this because we dont want to expose the internals of GameBlock which would make currentEntities mutable to the outside world
// We do this because we dont want to expose the internals of
// GameBlock which would make currentEntities mutable to the outside world
val entities: Iterable<GameEntity<EntityType>>
get() = currentEntities.toList()
@ -67,15 +69,12 @@ class GameBlock(
private fun updateContent() {
val entityTiles = currentEntities.map { it.tile }
content = when {
// Checking if the player is at this block. If yes it is displayed on top
// Checking if the player is at this block. If yes, it is displayed on top
entityTiles.contains(PLAYER) -> PLAYER
// Otherwise the first Entity is displayed if present
// Otherwise, the first Entity is displayed if present
entityTiles.isNotEmpty() -> entityTiles.first()
// Or the default tile if not
else -> defaultTile
}
}
}

View file

@ -13,7 +13,8 @@ import org.hexworks.amethyst.api.builder.EntityBuilder
import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.amethyst.api.newEntityOfType
// We add a function which calls Entities.newEntityOfType and pre-fills the generic type parameter for Context with GameContext.
// We add a function which calls Entities.newEntityOfType
// and pre-fills the generic type parameter for Context with GameContext.
fun <T : EntityType> newGameEntityOfType(
type: T,
init: EntityBuilder<T, GameContext>.() -> Unit
@ -34,7 +35,7 @@ object EntityFactory {
// We add a function for creating a newPlayer and call newGameEntityOfType with our previously created Player type.
fun newPlayer() = newGameEntityOfType(Player) {
// We specify our Attributes, Behaviors and Facets. We only have Attributes so far though.
// We specify our Attributes, Behaviors, and Facets. We only have Attributes so far though.
attributes(
EntityPosition(),
EntityTile(GameTileRepository.PLAYER),

View file

@ -10,7 +10,8 @@ import org.hexworks.zircon.api.data.Tile
import org.hexworks.zircon.api.graphics.Symbols
object GameTileRepository {
//Factory for creating tile objects, we use basic CharacterTiles here but Zircon can indeed use GraphicalTiles(textured) which will come later.
// Factory for creating tile objects, we use basic CharacterTiles here,
// but Zircon can indeed use GraphicalTiles(textured) which will come later.
//Empty Tile
val EMPTY: CharacterTile = Tile.empty()

View file

@ -44,7 +44,7 @@ class WorldBuilder (private val worldSize: Size3D) {
var rocks = 0
// Here we iterate over a list of the current position and all its neighbors
pos.sameLevelNeighborsShuffled().plus(pos).forEach { neighbor ->
// And we only care about the positions which have a corresponding block (when they are not outside of the game world)
// And we only care about the positions which have a corresponding block (when they are not outside the game world)
blocks.whenPresent(neighbor) { block ->
if (block.isFloor) {
floors++

View file

@ -21,7 +21,7 @@ class Config {
if(fileExists){
FileInputStream(file).use { prop.load(it) }
}
//Otherwise create necesssary directories
//Otherwise create necessary directories
//TODO: Check for directories individually as well?
else{
Files.createDirectories(Paths.get("./run"))
@ -42,17 +42,17 @@ class Config {
//Adds comments
val out: OutputStream = FileOutputStream(file)
prop.store(out, "PotRogue Configuartion File, restart game if changed value.")
prop.store(out, "PotRogue Configuration File, restart game if changed value.")
}
}
//Convert values from the config file to in-code variables, so we can use them later, also make them public.
val WINDOW_WIDTH: Int = (prop.getProperty("WINDOW_WIDTH")).toInt()
val windowWidth: Int = (prop.getProperty("WINDOW_WIDTH")).toInt()
val WINDOW_HEIGHT: Int = (prop.getProperty("WINDOW_HEIGHT")).toInt()
val windowHeight: Int = (prop.getProperty("WINDOW_HEIGHT")).toInt()
val DUNGEON_LEVELS: Int = (prop.getProperty("DUNGEON_LEVELS")).toInt()
val dungeonLevels: Int = (prop.getProperty("DUNGEON_LEVELS")).toInt()
val SIDEBAR_WIDTH: Int = (prop.getProperty("SIDEBAR_WIDTH")).toInt()
val sidebarWidth: Int = (prop.getProperty("SIDEBAR_WIDTH")).toInt()
val LOG_AREA_HEIGHT: Int = (prop.getProperty("LOG_AREA_HEIGHT")).toInt()
val logAreaHeight: Int = (prop.getProperty("LOG_AREA_HEIGHT")).toInt()
}

View file

@ -12,16 +12,16 @@ object GameConfig {
var TILESET = CP437TilesetResources.rogueYun16x16()
val THEME = ColorThemes.cyberpunk()
val WORLD_SIZE = Size3D.create(Config().WINDOW_WIDTH * 3, Config().WINDOW_HEIGHT * 3 , Config().DUNGEON_LEVELS)
val WORLD_SIZE = Size3D.create(Config().windowWidth * 3, Config().windowHeight * 3 , Config().dungeonLevels)
val GAME_AREA_SIZE = Size3D.create(
xLength = Config().WINDOW_WIDTH - Config().SIDEBAR_WIDTH,
yLength = Config().WINDOW_HEIGHT - Config().LOG_AREA_HEIGHT,
zLength = Config().DUNGEON_LEVELS
xLength = Config().windowWidth - Config().sidebarWidth,
yLength = Config().windowHeight - Config().logAreaHeight,
zLength = Config().dungeonLevels
)
fun buildAppConfig() = AppConfig.newBuilder()
.withDefaultTileset(TILESET)
.withSize(Config().WINDOW_WIDTH, Config().WINDOW_HEIGHT)
.withSize(Config().windowWidth, Config().windowHeight)
.withTitle("$GAME_ID | $GAME_VER")
.withIcon("assets/icon.png")
.build()

View file

@ -5,19 +5,22 @@ import org.hexworks.cobalt.databinding.api.extension.toProperty
import org.hexworks.zircon.api.data.Position3D
class EntityPosition(
// We add initialPosition as a constructor parameter to our class and its default value is unknown.
// Whats this? Position3D comes from Zircon and can be used to represent a point in 3D space (as we have discussed before),
// and unknown impelments the Null Object Pattern for us.
// Whats this? Position3D comes from Zircon
// and can be used to represent a point in 3D space (as we have discussed before),
// and unknown implements the Null Object Pattern for us.
initialPosition: Position3D = Position3D.unknown()
) : BaseAttribute() {
// Here we create a private Property from the initialPosition.
// Whats a Property you might ask? Well, it is used for data binding.
// A Property is a wrapper for a value that can change over time.
// It can be bound to other Property objects so their values change together and you can also add change listeners to them.
// Property comes from the Cobalt library we use and it works in a very similar way as properties work in JavaFX.
// It can be bound to other Property objects
// so their values change together, and you can also add change listeners to them.
// Property comes from the Cobalt library we use, and it works in a very similar way as properties work in JavaFX.
private val positionProperty = initialPosition.toProperty()
// We create a Kotlin delegate from our Property.
// This means that position will be accessible to the outside world as if it was a simple field, but it takes its value from our Property under the hood.
// This means that position will be accessible to the outside world
// as if it was a simple field, but it takes its value from our Property under the hood.
var position: Position3D by positionProperty.asDelegate()
}

View file

@ -3,5 +3,5 @@ package group.ouroboros.potrogue.entity.attributes
import org.hexworks.amethyst.api.base.BaseAttribute
import org.hexworks.zircon.api.data.Tile
// EntityTile is an Attribute which holds the Tile of an Entity we use to display it in our world
// EntityTile is an Attribute that holds the Tile of an Entity we use to display it in our world
data class EntityTile(val tile: Tile = Tile.empty()) : BaseAttribute()

View file

@ -5,7 +5,7 @@ import group.ouroboros.potrogue.extensions.GameMessage
import org.hexworks.amethyst.api.entity.EntityType
// Our EntityAction is different from a regular GameMessage in a way that it also has a target.
// So an EntityAction represents source trying to perform an action on target.
// So an EntityAction represents a source trying to perform an action on target.
// We have two generic type parameters, S and T.
// S is the EntityType of the source, T is the EntityType of the target.
@ -17,8 +17,8 @@ interface EntityAction <S : EntityType, T : EntityType> : GameMessage {
// The component1, component2 … componentN methods implement destructuring in Kotlin.
// Since destructuring is positional as weve seen previously by implementing the
// component* functions we can control how an EntityAction can be destructured.
// In our case with these 3 operator functions we can destructure any EntityActions like this:
// component* functions, we can control how an EntityAction can be destructured.
// In our case with these 3 operator functions, we can destructure any EntityActions like this:
//
//val (context, source, target) = entityAction
operator fun component1() = context

View file

@ -12,14 +12,18 @@ object CameraMover : BaseFacet<GameContext, MoveCamera>(MoveCamera::class) {
override suspend fun receive(message: MoveCamera): Response {
val (context, source, previousPosition) = message
val world = context.world
// The players position on the screen can be calculated by subtracting the Worlds visibleOffset from the players position.
// The visibleOffset is the top left position of the visible part of the World relative to the top left corner of the whole World (which is 0, 0).
// The players position on the screen can be calculated
// by subtracting the Worlds visibleOffset from the players position.
// The visibleOffset is the top left position of the
// visible part of the World relative to the top left corner of the whole World (which is 0, 0).
val screenPos = source.position - world.visibleOffset
// We calculate the center position of the visible part of the world here
val halfHeight = world.visibleSize.yLength / 2
val halfWidth = world.visibleSize.xLength / 2
val currentPosition = source.position
// And we only move the camera if we moved in a certain direction (left for example) and the Entitys position on the screen is left of the middle position.
// And we only move the camera if we moved in a certain direction
// (left, for example) and the Entitys position on the screen is left of the middle position.
// The logic is the same for all directions, but we use the corresponding x or y coordinate
when {
previousPosition.y > currentPosition.y && screenPos.y < halfHeight -> {

View file

@ -9,11 +9,11 @@ import org.hexworks.amethyst.api.entity.EntityType
import org.hexworks.zircon.api.uievent.KeyCode
import org.hexworks.zircon.api.uievent.KeyboardEvent
// InputReceiver is pretty simple, it just checks for WASD, and acts accordingly
// InputReceiver checks for WASD, and acts accordingly
object InputReceiver : BaseBehavior<GameContext>() {
override suspend fun update(entity: Entity<EntityType, GameContext>, context: GameContext): Boolean {
// We destructure our context object so its properties are easy to access.
// We destructure our context object so its properties are easier to access.
// Destructuring is positional, so here _ means that we dont care about that specific property.
val (_, _, uiEvent, player) = context
val currentPos = player.position

View file

@ -29,7 +29,7 @@ object Movable : BaseFacet<GameContext, MoveTo>(MoveTo::class) {
* This might be familiar for Python folks and what it does is that it unpacks the values from an object which supports it. So writing this:
* val (context, entity, position) = myObj
*
* is the equivalent of writing this:
* Is the equivalent of writing this:
* val context = myObj.context
* val entity = myObj.entity
* val position = myObj.position

View file

@ -12,16 +12,17 @@ val AnyGameEntity.occupiesBlock: Boolean
// We define this function as an extension function on AnyGameEntity.
// This means that from now on we can call tryActionsOn on any of our entities!
// It is also a suspend fun because the receiveMessage function we call later is also a suspending function.
// Suspending is part of the Kotlin Coroutines API and it is a deep topic.
// It is also suspending fun because the receiveMessage function we call later is also a suspending function.
// Suspending is part of the Kotlin Coroutines API, and it is a deep topic.
// Were not going to cover it here as we dont take advantage of it
suspend fun AnyGameEntity.tryActionsOn(context: GameContext, target: AnyGameEntity): Response {
var result: Response = Pass
// We can only try the actions of an entity which has at least one, so we try to find the attribute.
findAttributeOrNull(EntityActions::class)?.let {
// if we find the attribute we just create the actions for our context/source/target combination
// if we find the attribute, we just create the actions for our context/source/target combination
it.createActionsFor(context, this, target).forEach { action ->
// And we then send the message to the target for immediate processing and if the message is Consumed it means that
// And we then send the message to the target for
// immediate processing, and if the message is Consumed, it means that
if (target.receiveMessage(action) is Consumed) {
result = Consumed
// We can break out of the forEach block.

View file

@ -2,14 +2,19 @@ package group.ouroboros.potrogue.extensions
import org.hexworks.zircon.api.data.Position3D
// We add the extension function to Position3D. We do it by defining a function not with a simple name, but by the format: fun <target class>.<function name>: return type { // ....
// We add the extension function to Position3D.
// We do it by defining a function not with a simple name, but by the format:
// fun <target class>.<function name>: return type { // ....
fun Position3D.sameLevelNeighborsShuffled(): List<Position3D> {
return (-1..1).flatMap { x ->
// We use functional programming here. flatMap and map works in a similar way as you might've been used to it in Java 8s Stream API.
// We use functional programming here.
// flatMap and map work in a similar way as you might've been used to it in Java 8s Stream API.
(-1..1).map { y ->
// When you write extension functions this will be bound to the class being extended. So this here will point to the Position3D instance on which sameLevelNeighborsShuffled is called.
// When you write extension functions, this will be bound to the class being extended.
// So this here will point to the Position3D instance on which sameLevelNeighborsShuffled is called.
this.withRelativeX(x).withRelativeY(y)
}
// minus here will remove this position from the List and return a new List. shuffled will also return a new list which contains the same elements but shuffled.
// minus here will remove this position from the List and return a new List.
// shuffled will also return a new list which contains the same elements but shuffled.
}.minus(this).shuffled()
}

View file

@ -16,7 +16,8 @@ typealias GameMessage = Message<GameContext>
// Create an extension property (works the same way as an extension function) on AnyGameEntity.
var AnyGameEntity.position
// Define a getter for it which tries to find the EntityPosition attribute in our Entity and throws and exception if the Entity has no position.
// Define a getter for it which tries to find the
// EntityPosition attribute in our Entity and throws and exception if the Entity has no position.
get() = tryToFindAttribute(EntityPosition::class).position
// We also define a setter for it which sets the Property we defined before
set(value) {

View file

@ -25,7 +25,7 @@ class ConfigView (private val grid: TileGrid, theme: ColorTheme = GameConfig.TH
.addNewLine()
// and align it to center
.withAlignmentWithin(screen, ComponentAlignment.TOP_CENTER)
.build() // finally we build the component
.build() // finally, we build the component
//TODO: Options: world size, character tile (smiley, @, &), character customizations (class, looks, stats, start),
@ -45,7 +45,7 @@ class ConfigView (private val grid: TileGrid, theme: ColorTheme = GameConfig.TH
GameConfig.TILESET = CP437TilesetResources.anikki16x16()
}
//Once the back button is activated go back to startView
//Once the back button is activated, go back to startView
backButton.onActivated {
replaceWith(StartView(grid))
}

View file

@ -38,7 +38,7 @@ class LoseView (private val grid: TileGrid, theme: ColorTheme = GameConfig.THEME
replaceWith(PlayView(grid))
}
//On Quit BUtton activated, exit program
//On Quit BButton activated, exit program
exitButton.onActivated {
exitProcess(0)
}

View file

@ -8,7 +8,7 @@ import org.hexworks.zircon.api.component.ComponentAlignment
import org.hexworks.zircon.api.grid.TileGrid
import org.hexworks.zircon.api.view.base.BaseView
class PauseView(public val grid: TileGrid, theme: ColorTheme = GameConfig.THEME) : BaseView(grid, theme) {
class PauseView(private val grid: TileGrid, theme: ColorTheme = GameConfig.THEME) : BaseView(grid, theme) {
init {
val msg = "Pre-Game Configuration"
@ -24,7 +24,7 @@ class PauseView(public val grid: TileGrid, theme: ColorTheme = GameConfig.THEME)
.addNewLine()
// and align it to center
.withAlignmentWithin(screen, ComponentAlignment.TOP_CENTER)
.build() // finally we build the component
.build() // finally, we build the component
val backButton = Components.button()
.withAlignmentWithin(screen, ComponentAlignment.BOTTOM_CENTER)
@ -38,7 +38,7 @@ class PauseView(public val grid: TileGrid, theme: ColorTheme = GameConfig.THEME)
.withDecorations(ComponentDecorations.box(), ComponentDecorations.shadow())
.build()
//Once the back button is activated go back to startView
//Once the back button is activated, go back to startView
backButton.onActivated {
replaceWith(StartView(grid))
}

View file

@ -21,20 +21,20 @@ class PlayView (private val grid: TileGrid, private val game: Game = GameBuilder
init {
//Create Sidebar
val sidebar = Components.panel()
.withSize(Config().SIDEBAR_WIDTH, Config().WINDOW_HEIGHT - Config().LOG_AREA_HEIGHT)
.withPreferredSize(Config().sidebarWidth, Config().windowHeight - Config().logAreaHeight)
.withDecorations(box())
.build()
//Create area for logging
val logArea = Components.logArea()
.withDecorations(box(title = "Log"))
.withSize(Config().WINDOW_WIDTH, Config().LOG_AREA_HEIGHT)
.withPreferredSize(Config().windowWidth, Config().logAreaHeight)
.withAlignmentWithin(screen, ComponentAlignment.BOTTOM_RIGHT)
.build()
//Create Game view
val gameComponent = Components.panel()
.withSize(game.world.visibleSize.to2DSize())
.withPreferredSize(game.world.visibleSize.to2DSize())
.withComponentRenderer(
GameAreaComponentRenderer(
gameArea = game.world,

View file

@ -27,7 +27,7 @@ class StartView (private val grid: TileGrid, theme: ColorTheme = GameConfig.THEM
.addNewLine()
// and align it to center
.withAlignmentWithin(screen, ComponentAlignment.CENTER)
.build() // finally we build the component
.build() // finally, we build the component
val startButton = Components.button()
// we align the button to the bottom center of our header
@ -50,7 +50,9 @@ class StartView (private val grid: TileGrid, theme: ColorTheme = GameConfig.THEM
.withDecorations(box(), shadow())
.build()
//TODO: move this on to a configuration screen for world/player customization before PlayView, for now basic gameplay is in order though.
//TODO: move this on to a configuration screen for world/player customization before PlayView,
// for now basic gameplay is in order though.
//Once the start button is pressed, move on to the PlayView
startButton.onActivated {
replaceWith(PlayView(grid))

View file

@ -9,42 +9,41 @@ import org.hexworks.zircon.api.grid.TileGrid
import org.hexworks.zircon.api.view.base.BaseView
import kotlin.system.exitProcess
// For if winning.... obviously just a test.
class WinView (private val grid: TileGrid, theme: ColorTheme = GameConfig.THEME) : BaseView(grid, theme) {
// For if winning just a test.
class WinView(private val grid: TileGrid, theme: ColorTheme = GameConfig.THEME) : BaseView(grid, theme) {
init {
//Title
// Title
val header = Components.header()
.withText("You won!")
.withAlignmentWithin(screen, ComponentAlignment.CENTER)
.build()
//Create Reset Button
// Create Reset Button
val restartButton = Components.button()
.withAlignmentAround(header, ComponentAlignment.BOTTOM_LEFT)
.withText("Restart")
.withDecorations(box())
.build()
//Create Quit Button
// Create Quit Button
val exitButton = Components.button()
.withAlignmentAround(header, ComponentAlignment.BOTTOM_RIGHT)
.withText("Quit")
.withDecorations(box())
.build()
//On Reset Button activated, move back to PlayView
// On Reset Button activated, move back to PlayView
restartButton.onActivated {
replaceWith(PlayView(grid))
}
//On Quit Button activated, exit program
// On Quit Button activated, exit program
exitButton.onActivated {
exitProcess(0)
}
//Bake The Cake
// Bake The Cake
screen.addComponents(header, restartButton, exitButton)
}
}
}

View file

@ -7,7 +7,7 @@ import group.ouroboros.potrogue.extensions.GameEntity
* The TL;DR for DIP is this: By stating what we need (the World here) but not how we get it we let the outside world decide how to provide it for us.
* This is also called Wishful Thinking.
* This kind of dependency inversion lets the users of our program inject any kind of object that corresponds to the World contract.
* For example we can create an in-memory world, one which is stored in a database or one which is generated on the fly. Game wont care!
* For example, we can create an in-memory world, one which is stored in a database or one which is generated on the fly. Game wont care!
* This is in stark contrast to what we had before: an explicit instantiation of World by using the WorldBuilder.
*/

View file

@ -10,12 +10,12 @@ import org.hexworks.zircon.api.data.Position3D
import org.hexworks.zircon.api.data.Size3D
// Take the size of the World as a parameter
class GameBuilder (val worldSize: Size3D) {
class GameBuilder(val worldSize: Size3D) {
// We define the visible size which is our viewport of the world
private val visibleSize = Size3D.create(
xLength = Config().WINDOW_WIDTH - Config().SIDEBAR_WIDTH,
yLength = Config().WINDOW_HEIGHT - Config().LOG_AREA_HEIGHT,
xLength = Config().windowWidth - Config().sidebarWidth,
yLength = Config().windowHeight - Config().logAreaHeight,
zLength = 1
)
@ -25,7 +25,6 @@ class GameBuilder (val worldSize: Size3D) {
.build(visibleSize = visibleSize)
fun buildGame(): Game {
prepareWorld()
val player = addPlayer()
@ -48,7 +47,7 @@ class GameBuilder (val worldSize: Size3D) {
// We immediately add the player to the World which takes an offset and a size as a parameter
player,
// offset determines the position where the search for empty positions will start. Here we specify that the top level will be searched starting at (0, 0)
offset = Position3D.create(0, 0, Config().DUNGEON_LEVELS - 1),
offset = Position3D.create(0, 0, Config().dungeonLevels - 1),
size = world.visibleSize.copy(zLength = 0)
) // And we also determine that we should search only the throughout the viewport. This ensures that the player will be visible on the screen when we start the game
return player
@ -60,4 +59,4 @@ class GameBuilder (val worldSize: Size3D) {
worldSize = WORLD_SIZE
).buildGame()
}
}
}

View file

@ -16,4 +16,4 @@ data class GameContext(
// The object representing the player. This is optional, but because we use the player in a lot of places it makes sense to add it here
val player: GameEntity<Player>
) : Context
) : Context

View file

@ -36,7 +36,8 @@ class World (
init {
startingBlocks.forEach { (pos, block) ->
// a World takes a Map of GameBlocks, so we need to add them to the GameArea. Where these blocks come from? Well see soon enough wen we implement the WorldBuilder!
// A World takes a Map of GameBlocks, so we need to add them to the GameArea.
// Where these blocks come from? Well see soon enough wen we implement the WorldBuilder!
setBlockAt(pos, block)
block.entities.forEach { entity ->
// Also added the Entities in the starting blocks to our engine