diff --git a/README.md b/README.md index 282942c..5cbd121 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,46 @@ -# Gooey's Defence -This module serves as the main module for the Gooey's Defence Gameplay Template. -It currently is undergoing active development and thus is not at a public release stage. +
+ +
-## How To Play (at the moment) -Currently this modules lacks a few features that make it publicly playable. However it does have many feature currently implemented. As such this shall outline all the current features and how to use them. +_This is a module for [Terasology]. +It adds the "Gooey's Defence" gameplay to the game, which is a tower-defense style gameplay experience. +The goal is to defend the center of the arena against the malicious Gooeys spawning in waves in the outer areas of the arena and traversing it. +The player can buy tower components to build towers with various effects to keep the Gooeys from reaching the center._ -#### Activation -Firstly, please start a new world after updating this module. During development I shall make no guarantees that the module will be backwards compatible. -Once your new world has loaded you will need to activate it. That is currently done by simply ~~interacting with any shrine block via `E`~~ entering the game now. +

👉 Documentation 👈

-If the world loaded correctly you should see translucent blue lines crossing the field from the entrances to the shrine. These are the paths the enemies will take in order to reach the spawn. As you alter the world, the paths will also change. If no path is found then the blue line will disappear and your game will probably crash at the moment. -Likewise be careful using the debugger, if the pathfinding times out (and it does count time paused in the debugger) then your game will crash. -Probably. +## Contributing +We welcome contributions to our modules, be it bug fixes or feature contributions. +Check out the [Contributor Guide][contributor-guide] on the main project wiki to learn more. -#### Enemies -In order to spawn in some enemies just interact with the world again. This can be done by interacting with a shrine block with `E` key or by using an item you are holding with `right click`. ~~This does include placing blocks, so do with that as you will~~. +To check out this module (and all its dependencies) to your Terasology workspace run (in the workspace root): -There are three enemy types at the moment, `BasicEnemy`, `FastEnemy` and `StrongEnemy`. However in order to spawn in different types you will need to edit the `EnemyManager#spawnEnemy()` method. Simply change the prefab referenced to one of the other types and recompile. +``` +groovyw module recurse GooeyDefence +``` -When an enemy reaches the shrine, it will be destroyed and make the shrine flash red briefly. The game will not end for the release, but the game will end for the master branch. The restart button is there to help, so don't worry about having your testing constantly interrupted. +To build a module JAR for just this module run (in the workspace root): -#### Towers -The last bit of information you need at the moment is the towers. These are the multiblock structures that will attack the enemies and damage them, with the enemies eventually dying if they reach zero health. +``` +gradlew :module:GooeyDefence:jar +``` -A tower is made up of at least three blocks. -The first block type is **Cores**. These provide power to the other blocks, so make sure that you have enough cores to provide power to the tower. +To run all tests and static code checks for this module run (in the workspace root): -The second block type is **Targeters**. These are the blocks that actually attack the enemies. There are a number of different Targeter blocks, all with different strengths and weaknesses. They are listed below. -Crucially, Targeters don't need to actually see the enemy to shoot it, with their shots phasing through blocks. You can also stack multiple different types of Targeters onto a single tower. For now... +``` +gradlew :module:GooeyDefence:check +``` -Lastly you have **Effectors**. Although Targeters attack the enemies, their shots won't actually do anything. Effectors provide an effect to the enemies that are targeted. -The effects range from damage, to crowd control to both. Like the Targeters, multiple types can be stacked onto a single tower to apply multiple different effects. This does mean that you can technically freeze and ignite an enemy at the same time. Which is why multiple effect stacking will probably be tweaked in future. Abuse it before you lose it. +### Documentation via gh-pages -**Plain** blocks are the odd type out. They provide no function to the tower, but they allow the blocks to connect without having to directly touch. They are useful if you want to connect some of the above blocks but don't actually want to have to use a more expensive functional block to do so. +The documentation of this module is built with [docsify]. +It is served via [gh-pages]. +To preview the site you can either use the `docsify` [CLI tool](https://github.com/docsifyjs/docsify-cli) or just run a static server on the `docs` folder. + + +[Terasology]: https://github.com/MovingBlocks/Terasology +[gh-pages]: https://pages.github.com/ +[docsify]: https://docsify.js.org/#/ +[contributor-guide]: https://github.com/MovingBlocks/Terasology/wiki/Contributor-Quick-Start -#### Upgrading -If you want to upgrade the effectiveness of the tower's blocks you can. By interacting with a tower via `E` you can bring up the tower screen. This has a list of all the targeters and effectors in the tower. By selecting one you can view the details of that block and upgrade it. A block can be upgraded in multiple different ways. You can always upgrade the range and attack speed but some other towers have other upgrades you can buy. -If you break an upgraded block you will lose all your upgrades and have to re apply them. This is only a minor inconvenience at the moment, but when upgrades start to cost money will become more serious. The costs will also not be refunded. You break it, you pay for it. \ No newline at end of file diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/_assets/styles.css b/docs/_assets/styles.css new file mode 100644 index 0000000..c3d4329 --- /dev/null +++ b/docs/_assets/styles.css @@ -0,0 +1,17 @@ +figure img { + display: block; + width: auto; + margin-left: auto; + margin-right: auto; +} + +figure { + margin: 1em 0em; + padding: 1em 1em; +} + +figcaption { + margin-top: 1em; + font-style: italic; + text-align: center; +} \ No newline at end of file diff --git a/docs/_media/banner.png b/docs/_media/banner.png new file mode 100644 index 0000000..4689e27 Binary files /dev/null and b/docs/_media/banner.png differ diff --git a/docs/_sidebar.md b/docs/_sidebar.md new file mode 100644 index 0000000..c1ddd5a --- /dev/null +++ b/docs/_sidebar.md @@ -0,0 +1,16 @@ +* [Home](README.md) +* [The Arena](arena.md) +* [The Gooeys](gooeys.md) +* [The Towers](towers.md) +* Components + * [TowerEffector](tower-effector-component.md) + * [TowerTargeter](tower-targeter-component.md) +* Systems + * [EffectorSystem](effector-system.md) + * [TargeterSystem](targeter-system.md) +* UI + * [ActivateScreen](activate-screen.md) + * [TowerScreen](tower-screen.md) + * [Parsers](parsers.md) +* API + * [API (SNAPSHOT)](http://jenkins.terasology.io/teraorg/job/Terasology/job/Modules/job/G/job/GooeyDefence/job/develop/javadoc/overview-summary.html) diff --git a/docs/activate-screen.md b/docs/activate-screen.md new file mode 100644 index 0000000..0d7bcde --- /dev/null +++ b/docs/activate-screen.md @@ -0,0 +1,8 @@ +# `ActivateScreen` + +The `ActivateScreen` is a user interface element that is displayed at game start-up after the loading screen is closed. +It consists a welcome title, some informational text, and a button labeled "Begin". +Closing the screen via the "Begin" button or pressing `Esc` will activate the field and cause the game to start or resume. + +The informational text displayed varies depending on whether the world is newly created or loaded from a save. +While this is not implemented currently, the idea is to show statistics, flavour text, and possibly allow for configuring the game. diff --git a/docs/arena.md b/docs/arena.md new file mode 100644 index 0000000..218bc01 --- /dev/null +++ b/docs/arena.md @@ -0,0 +1,13 @@ +# The Arena + +The world generator creates an arena with the following properties: +* base height: 0 +* circular defence field with four entries and a shrine in the center +* random block pattern on the floor of the defence field + +## Attack Paths + +If the world loaded correctly, there should be translucent blue lines crossing the field from the entrances to the shrine. +These are the paths the gppeys will take in order to reach the shrine. +As the player alters the world, these paths will change. +If no path is found, the blue lines will disappear and the gameplay will not work as expected. \ No newline at end of file diff --git a/docs/block-upgrades-component.md b/docs/block-upgrades-component.md new file mode 100644 index 0000000..9bdd041 --- /dev/null +++ b/docs/block-upgrades-component.md @@ -0,0 +1,30 @@ +# `BlockUpgrades` Component + +The `BlockUpgrades` component can be used to define new component upgrades in component prefabs. +It relates to a tower component using the `componentName` field and lists a number of upgrades modifying properties of the related tower component. + +For example, the following prefab snippet shows a `FireEffector` and an upgrade to its `damage` property, increasing it by two: + +``` + "FireEffector": { + "drain": 5, + "damage": 2, + "fireDuration": 2000 + }, + "BlockUpgrades": { + "componentName": "FireEffector", + "upgrades": [ + { + "upgradeName": "Damage", + "stages": [ + { + "cost": 5, + "values": { + "damage": 2 + } + }, + ... +``` + +The values defined in each upgrade section are limited to `int`, `float`, `short`, `long`, `double`, and `byte` types. +If the component has a parser, fields being upgraded do not need to be defined within that parser as they are independent. \ No newline at end of file diff --git a/docs/effector-system.md b/docs/effector-system.md new file mode 100644 index 0000000..734aa24 --- /dev/null +++ b/docs/effector-system.md @@ -0,0 +1,28 @@ +# `EffectorSystem` + +Every tower effector requires a respective system to apply the effector's effect to a target. +In order to do so, a system can inject managers, listen and react to events sent by other systems, and send out events itself. + +## `ApplyEffectEvent` + +`ApplyEffectEvent` is the trigger event every effector system should listen for. +This event is sent against the effector, allowing systems to filter for their associated `EffectorComponent`. +An effector system by default is not expected to react to `ApplyEffectEvent`s sent against other effectors. + +Every `ApplyEffectEvent` only affects a single target. +If an effector's effect applies to multiple targets, multiple `ApplyEffectEvent`s will be sent. +The combination of multiple effectors can further result in multiple `ApplyEffectEvent`s bein sent for the same target. + +## `RemoveEffectEvent` + +If an effector's effect duration is `EffectDuration.LASTING`, then a second event called `RemoveEffectEvent` will be sent at some point in time. +This event is expected to remove the effect from a target. + +Like `ApplyEffectEvent`, `RemoveEffectEvent` is sent against an effector and affects a single target. + +## Effect Strength + +An effector's effect can be strengthened or weakened. +This depends on the target initiating the attack as different targeter types have different multipliers to balance long ranges and powerful selection strategies. +In both, the `ApplyEffectEvent` and the `RemoveEffectEvent` the effector system is expected to weaken or strengthen its effect based on the attack initiating targeter's multiplier. +It is guaranteed that the multiplier is equal for both, the `ApplyEffectEvent` and `RemoveEffectEvent`. diff --git a/docs/gooeys.md b/docs/gooeys.md new file mode 100644 index 0000000..f766ebd --- /dev/null +++ b/docs/gooeys.md @@ -0,0 +1,14 @@ +# The Gooeys + +Gooeys are the malicious intruders in this gameplay. +They spawn in the outer areas of the gameplay arena and try to traverse it to reach the shrine in the arena center. + +Currently, Gooeys do not spawn automatically. +A wave of Gooeys can be triggered by interacting with the shrine in the center of the arena (`E`) or by using a held item (right-click). +In the future, gooey waves are expected to change with respect to number, strength, and speed of the Gooeys the longer the player survives. + +When a Gooey reaches the shrine, it will be destroyed and make the shrine flash red briefly. +As a result, the game will end and show a restart button allowing a player to retry defending the shrine. + +The main properties of Gooeys are movement speed and health. +Currently, there are the following Gooey types, varying in these properties: `BasicEnemy`, `FastEnemy`, and `StrongEnemy`. diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..ecea5b4 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,46 @@ + + + + + + + + Gooey's Defence + + + + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/parsers.md b/docs/parsers.md new file mode 100644 index 0000000..f096f0e --- /dev/null +++ b/docs/parsers.md @@ -0,0 +1,29 @@ +# Parsers + +A parser is any class that extends the BaseParser abstract class. +It contains information about the component it applies to, the fields that should be displayed and methods to convert those field values into human-readable forms. + +Firstly, a parser is only applied onto the class that is returned by the `getComponentClass()` method. +As not all fields on a component should be displayed in UI, the `getFields()` method returns a map between a field name, and the display name. +If a field is not present as a key in this map, then it is not displayed +The fields are also displayed in alphabetical order, according to the field name, not the display name. +This is to allow field names, values and upgrade values to all line up. + +Lastly, and this is where the reflection magic happens, the parser should contain methods to convert field values to readable values. +These methods should have a specific structure: +* The return type should be string +* The name of the method must be the same as the name of the field - capitalisation matters +* The first parameter should be of type boolean - it will indicate if the value is actually an upgrade values +* The second parameter should have the same type as the field - this will be passed the value to convert + +If a method matching this is not found, then either the `handleField()` or `handleUpgrade()` methods are called. +These are backup methods that take a string field name and an object value. +By default, `handleField()` simply calls `String.valueOf()` on the value passed, and `handleUpgrade()` calls `handleField()` and prepends a '+' to the result. + +As of present the value of a field being converted can be an Enum, String, or any of the six primitive numbers. +Other values may be supported in the future. +Alternatively, an object default might be added. + +Of note here is that both component field values and upgrade values are passed through the parser. +The boolean flag, or differing handle methods allow for different conversion of upgrades vs field values. + diff --git a/docs/targeter-system.md b/docs/targeter-system.md new file mode 100644 index 0000000..81f7360 --- /dev/null +++ b/docs/targeter-system.md @@ -0,0 +1,22 @@ +# `TargeterSystem` + +Every tower targeter requires a respective system to select targets and initiate the attack on them. +In order to do so, a system can inject managers, listen and react to events sent by other systems, and send out events itself. + +## `SelectEnemiesEvent` + +`SelectEnemiesEvent` is the target selection trigger event every targeter system should listen for. +This event is sent against the targeter, allowing systems to filter for their associated `TargeterComponent`. +A targeter system by default is not expected to react to `SelectEnemiesEvent`s sent against other targeters. + +## Helper Methods + +The `BaseTargeterSystem` provides a number of optional-to-use helper methods to simplify implementing targeter systems and avoid code duplication. + +| Method | Functionality | +|-----------------|----------------------------------------------------------------------------------------| +| getSingleTarget | selecting a single target from a given input list based on a given selection strategy | +| canUseTarget | basic check to see if a target exists and is within range | +| getTarget | selecting a single target based on range, selection method and location of a tower | + +Further, `EnemyManager` provides a method to get all targets within a radius of a given position. \ No newline at end of file diff --git a/docs/tower-effector-component.md b/docs/tower-effector-component.md new file mode 100644 index 0000000..095a118 --- /dev/null +++ b/docs/tower-effector-component.md @@ -0,0 +1,32 @@ +# `TowerEffector` Component + +The `TowerEffector` component is the base class for all tower effectors. +New effectors can be created by extending the `TowerEffector` component and adding it to a block extending `GooeyDefence:PlainBlock`. +Additionally, a system is required including the implementation of the logic applying the effector's effect to a target. + +New effectors can also extend other existing effector components to leverage fields defined in those and avoid code duplication. +For instance, `FireEffector` and `PoisonEffector` both reuse the `DamageEffector` by extending the class. +Thus, they can apply damage to a target without duplicating the logic implemented in `DamageEffector`. + +Every new effector needs to implement the `getEffectCount()` and `getEffectDuration()` methods explained below. +In case a new effector extends another effector, it may need to override these methods. + +## `getEffectCount()` + +The `getEffectCount()` method is used to collect information on how many times an effector's effect should be applied to a target. +The following options are provided by the `EffectCount` enum: + +| Option | Effect Application | +|--------------|---------------------------------------------------------------| +| `PER_SHOT` | Every single time the tower attacks, this effector is applied | +| `CONTINUOUS` | The effect is only applied when the enemy is first attacked | + +## `getEffectDuration()` +The `getEffectDuration()` method is used to get information on how long an effector's effect applied on a target is expected to last. +The following options are provided by the `EffectDuration` enum. + +| Option | Effect Duration | +|-------------|----------------------------------------------------------------------| +| `INSTANT` | The effect has no duration | +| `LASTING` | The effect has a duration for as long as the enemy is being attacked | +| `PERMANENT` | The effect has a duration, but it's not determined how long | diff --git a/docs/tower-screen.md b/docs/tower-screen.md new file mode 100644 index 0000000..239570c --- /dev/null +++ b/docs/tower-screen.md @@ -0,0 +1,23 @@ +# `TowerScreen` + +The `TowerScreen` is a user interface element that lists all functional components of a tower and provides details about them and the general tower stats. +The UI is split into three main segments: the block lists, the block info and the tower info. + +## Block Lists + +The Block Lists are three `UIList`s that contain all tower components, separated into the three component types: Core, Effector, and Targeter. +The Cores are listed in the top right corner, while the Targeters are shown in the top left, and the Effectors in the bottom left. +These `UIList`s are populated based on the tower entity's internal list. +The name of each component is derived from the `DisplayNameComponent` or, if not present, the prefab name. + +## Block Info + +The Block Info is displayed in the center of the screen. +It shows the component details, including the name, the description and component-specific settings for a selected tower component. +Currently, the only customizable setting is the target selection strategy for Targeters. +Additionally, the part of the screen lists all fields related to a selected tower component as well as a buttons for upgrading it. + +## Tower Info + +The Tower Info contains general information about the tower itself. +Currently, this only includes the amount of power produced by the Cores and an accumulation of the power drain introduced by all Effectors and Targeters. diff --git a/docs/tower-targeter-component.md b/docs/tower-targeter-component.md new file mode 100644 index 0000000..af683bd --- /dev/null +++ b/docs/tower-targeter-component.md @@ -0,0 +1,38 @@ +# `TowerTargeter` Component + +The `TowerTargeter` component is the base class for all tower targeters. +New targeters can be created by extending the `TowerTargeter` component and adding it to a block extending `GooeyDefence:PlainBlock`. +Additionally, a system is required including the implementation of the logic selecting targets and initiating the attack on them. + +## Mandatory fields + +The following fields are mandatory for every targeters to implement: + +| Field | Description | +|-----------------|-----------------------------------------------------| +| drain | The amount of power the targeter will consume | +| range | The range of the targeter in blocks | +| attackSpeed | How often the targeter will attack, in milliseconds | +| selectionMethod | The method used to select a target | +| lastTarget | The enemy targeted last attack | +| affectedEnemies | All targets affected last attack | + +## `getMultiplier()` + +The `getMultiplier()` method is called by effectors when applying their effects to weaken or strengthen it based on the targeter that initiated the attack including applying the effect. +Implementing this method is mandatory for every targeter. +It's expected to return a float value to the caller. + +## Target Selection Strategy + +The target selection strategy of a tower indicates how the targeter chooses targets to initiate attack on. +The strategy is a mandatory property of every targeter, stored in the `selectionMethod` field. +A targeter can only apply the selection strategy on targets within its range. +The following target selection strategies are available for use: + +| Strategy | Description | +|----------|---------------------------------| +| FIRST | The enemy closest to shrine | +| WEAK | The enemy with the least health | +| STRONG | The enemy with the most health | +| RANDOM | A random enemy within range | diff --git a/docs/towers.md b/docs/towers.md new file mode 100644 index 0000000..076b5e3 --- /dev/null +++ b/docs/towers.md @@ -0,0 +1,73 @@ +# The Towers + +Towers are the multi-block structures that attack the enemies either by damaging them or affecting their stats. +A tower is made up of at least three blocks: +* a core block +* an effector block +* a targeter block + +## Cores + +The Cores of a tower define its power level. +A tower needs power to run effectors and targeters that will allow the tower to target gooeys and affect these targets. +Upgrades to a tower core increase the amount of power it can provide. + +## Effectors + +Effectors define how a tower will affect targets. +They drain the tower's power to realize the tower's effect on targeted enemies. + +Currently, there are the following effectors: + +| Effector | Effect | Notes | +|----------------|--------------|---------------------------------------| +| DamageEffector | Plain Damage | direct damage only | +| FireEffector | Burn Damage | damage over time, chance of spreading | +| IceEffector | Slow | | +| PoisonEffector | Poison | damage both direct and over time | +| StunEffector | Stun | | +| VisualEffector | Enlarge | increases enemy visibility | + +Multiple types of effectors can be stacked onto a single tower to apply multiple different effects. +This does mean that, technically, an enemy can be slowed and burnt at the same time. + +Most effectors can be upgraded via the upgrade system. + +## Targeters + +Targeters define which enemies are targeted by a tower and as a result will be affected by the tower's effect. +They drain the tower's power to detect possible targets and initiate the attack on them. +Targeters don't need to actually see a target to attack it as their attacks phase through blocks. + +A targeter has a maximum range, an attack speed, and a selection strategy for targets. +Currently, there are the following targeters: + +| Targeter | Selection Strategy | +|-----------------|-----------------------------------------------------------------------------------------------| +| AoeTargeter | All targets in range | +| ChainTargeter | Single base target, attack chaining to additional targets depending on chain range and length | +| MissileTargeter | Single, far away base target, small splash radius | +| SingleTargeter | Single target within range | +| SniperTargeter | Far away target, high effect multiplier | +| SplashTargeter | Single base target, small splash radius | + +Same as for effectors, each different targeter type has strengths and weaknesses compared to other targeters. +The multipliers are used to keep the balance across the different targeter types, for instance to reduce an effector's effect with more powerful targeters, such as the `AoeTargeter`. +If, for instance `AoeTargeter` and `SingleTargeter` had the same multiplier, the former would damage multiple targets for the same amount as the later would damage only a single target for. +This would result in the `AoeTargeter` being objectively a better choice, resulting in an imbalance across the different targeters. + +Multiple types of targeters can be stacked onto a single tower to leverage multiple different selection strategies. + +## Connectors + +Connectors are plain blocks that do not provide any function to the tower other than connecting functional blocks. +Functional blocks can also be placed adjacent to each other, but with connectors they don't have to, allowing for a cost-effective yet more strategic placement while still leveraging the other components of a tower. + +## Upgrades + +The effectiveness of a tower's components can be improved by upgrading them using the "Tower Sceen" (`E`). +The "Tower Screen" lists all functional components of a tower. +Selecting any of those displays details of the respective component and means to upgrade it. + +Available tower component upgrades depend on the component type. For instance, for examples targeter upgrades include increasing range and attack speed. +Breaking an upgraded tower component block will reset all applied upgrades. \ No newline at end of file diff --git a/module.txt b/module.txt index e5022b2..79a8155 100644 --- a/module.txt +++ b/module.txt @@ -3,7 +3,7 @@ "version": "1.1.1-SNAPSHOT", "author": "The Terasology Foundation", "displayName": "Gooey's Defence", - "description": "In this gameplay module, the player needs to defend the center against incoming waves using \"towers\".", + "description": "Tower-defense style gameplay in which the player needs to defend the center of the gameplay arena against incoming waves of Gooeys by building \"towers\" that attack them.", "dependencies": [ { "id": "CoreRendering",