Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: initial contributor documentation #18

Closed
wants to merge 1 commit into from

Conversation

oniatus
Copy link

@oniatus oniatus commented Jul 16, 2018

First draft of a contributor documentation with the goal to describe the high level concepts and give a first entry point for further module extensions or submodule development.

@@ -0,0 +1,95 @@
# Module Contributor Guide
<special git conventions for the module?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of present none other than exists for all Terasology modules.

- Defence field with four entries and a shrine in the center
- Field is filled with a random block pattern on the floor

<can the world generator be extended?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of present no. There is a stretch goal to allow this as well as a bunch of other features like having multiple defense fields, each with different layout types.


### Towers
Towers are block entities and contain a core, an effector and a targeter.
<How are different towers defined and how can new towers be added?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Towers themselves are not actually anything defined by the module.
They are all created from the block listed below by the player.

I have however considered adding in some preset tower designs, as well as allowing the user to 'save' a tower design to an item which then can then reuse elsewhere in the field. Does this seem like something that would be useful? It's not included in the main proposal but I could add it as a goal to work on after GSoC

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a preset be like a combination of different effectors/targeters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. I would use Structure Templates.

#### Effectors
Effectors represent the tower effect and cause a drain on the tower power.
Most effectors can be upgraded via the upgrade system.
New effectors can be created via prefabs and by extending the `TowerEffector` component.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a few more steps to it than that, in order to get the Effector functioning.

Firstly you'll need to make the new component. This needs to be a subclass of TowerEffector and thus must either extend that, or some other effector. The benefits of extending an existing effector is that it allows you to build on fields defined within that. For instance, FireEffector and PoisonEffector both extend DamageEffector, as they apply damage to the target and thus can reuse the latter.

An effector also needs to implement (or override if extending from another effector) the following methods

getEffectCount()
This method is used to indicate how many times the effect should be applied onto the target. The options are defined in the EffectCount enum and are as follows:

  • 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()
This method indicates how long the effect should last for. There are three options defined in the EffectDuration enum.

  • 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.

After that, the effector needs to be added to a block, this is the same as the method of adding a core block, and details are in that section.

Finally, a System needs to be created to apply the effect to the enemy. The system should listen for an ApplyEffectEvent. This event will be sent against the effector itself, to allow for the system to filter for the correct component. The event will always only be sent with a single enemy target, with multiple events being sent for each enemy targeted.
If the effector has EffectDuration.LASTING then a second event will be sent to remove the effect from enemies, RemoveEffectEvent. This is the same as the prior event and is sent against the effector with a single enemy target.
Of note, is that the event will contain a strength multiplier, this should be used to weaken or strengthen an effect, and is used as a balancing tool on a per-targeter basis. It is gauranteed that the value will be the same for both the ApplyEffectEvent and RemoveEffectEvent
The system may otherwise do whatever it needs to, in order to apply the effect. This includes listening for other events, injecting managers and other things common for a system.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may end up changing effectors to use the Alteration Effects module in future. If I do, then the mechanism for extending effectors will obviously also alter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we can document the current state and extend it later on :) The concept sounds solid.
One possible case which is harder with the enums could be an extension with special cases, e.g. effect count per second shot or effect durations based on special conditions. However this should be easy to change so we can keep it for now as is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. I did poke around in Alteration Effects and it seems like it doesn't quite have all the functionality I would want, so I'd have to extend it anyway. Thus for now I'll stick with the current system

Most effectors can be upgraded via the upgrade system.
New effectors can be created via prefabs and by extending the `TowerEffector` component.

#### Targeters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method for extending this is similar to the effector with a few key differences, which will be outlined here.

Firstly, the component must extend TowerTargeter. Likewise with effectors, the targeter can instead extend an existing targeter component. TowerTargeter also includes some fields by default which are as follows:

  • 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. Eg, 200 means it will attack every 0.2s, or 5 times a second
  • selectionMethod: The method used to select a target, more info below. This should not be set in the prefabs
  • lastTarget: The enemy targeted last attack, more info below. This should not be set in the prefabs
  • affectedEnemies: All enemies effected last attack, used internally. This should not be set in the prefabs

The component must also implement (or override if extending an existing targeter) the getMultiplier method. This returns a float which is passed to the effectors when they apply their effects. It's used as a balancing tool in order to strengthen or weaken effects. For instance, the Sniper Targeter has a high multiplier, but the Aoe Targeter has a low one.

The selectionMethod of a tower is an optional enum that indicates how the tower should choose a target from the enemies within range. It is not required to be used. There are four possible options:

  • 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

Following on from this a new block must be made. This is the same as effectors, prefab must extend GooeyDefence:PlainBlock etc.

Lastly, a system must be created in order to actually select the targets to attack. The system should listen for the SelectEnemiesEvent which will be sent against the targeter entity. A number of helper methods are provided in the BaseTargeterSystem, such as getSingleTarget which selects a single entity from a list, given the selection method to use; canUseTarget which does a basic check to see if the enemy is within range and exists; and getTarget which gets a single entity, based on the range, selection method and location of the tower.
EnemyManager also contains a method to get all enemies within a radius of a position. It is not required to use any of these methods however.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that I implemented components with private fields but I seem to remember that someone told me they are not supposed to have any methods. Maybe cervator or someone else closer to the gestalt library can clarify this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This particular concern is also in issue #8

<How are different towers defined and how can new towers be added?>

#### Core
The core defines the power level of a tower which can be affected with upgrades.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to extend a core, all that is needed is for the new component to extend TowerCore or an existing core component.

After that, the core needs to be added to a block, this follows the standard method of adding a Terasology Block. The only requirements here are that the block have an associated prefab, who's parent is GooeyDefence:PlainBlock. This will ensure the block can be added to the tower and be breakable. (Although obviously the prefab should also contain the new component created as well) Best practices also include adding a DisplayNameComponent and a BlockUpgradesComponent. Details for the latter are included in the extending content section.

<how to add extra features with new modules>

# Open Points
- World size and properties are defined hardcoded in DefenceField, can be moved to a configuration prefab
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good suggestion an I will attempt to do this. However, do you know of a way to retrieve a prefab from a static context when the module is loaded?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does seem like it would be perfect for doing that.
Issue #21 has been opened for this.


# Open Points
- World size and properties are defined hardcoded in DefenceField, can be moved to a configuration prefab
- Shrine template could be extracted to something else than hardcoded block positions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I also wish to do. I considered Structure Templates, but that would be adding the whole module just for the shrine, and I'm unsure if you can use it in world generation.
If I end up adding in template functionality for the towers, then I will also switch this across to use a Structure Template.

# Open Points
- World size and properties are defined hardcoded in DefenceField, can be moved to a configuration prefab
- Shrine template could be extracted to something else than hardcoded block positions
- Paths all entrances in PathfindingManager can be stored in something more readable than a List of Lists
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any data structures you suggest using in place of a 2d list?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A custom class which provides the methods you need (something like List<Vector3i> findPathForEntrance(int)?) would work :) you can use the nested list inside.

Copy link
Member

@syntaxi syntaxi Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you feel this would improve readability then I can easily change it across. Issue #22

- Shrine template could be extracted to something else than hardcoded block positions
- Paths all entrances in PathfindingManager can be stored in something more readable than a List of Lists
- EnemyWalkingPlugin.areAllBlocksPenetrable is a code duplication with a part of the WalkingPlugin from FlexiblePathfinding.
The method could be extracted in flexible pathfinding and then reused in GooeyDefence.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will take a look at the duplication and see what I can extract into a method.

@jdrueckert
Copy link
Member

Superseded by #65

@jdrueckert jdrueckert closed this Apr 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants