demoth / jake2

Quake 2 java port
GNU General Public License v2.0
59 stars 9 forks source link

Component for all non monster entities #93

Open demoth opened 1 year ago

demoth commented 1 year ago

Quake2 (and by extension - jake2) suffers from the Blob antipattern: the edict (or SubgameEntity in case of jake2) contains dozens of fields, which are used in many often unrelated scenarios. This leads to high coupling of different game related functions. In addition to it, some fields are used for completely different purposes, for example:

One way to solve it is to incorporate an entity-component-system approach. At the moment, I find it difficult to implement purely. Also, there core design of the engine is not very friendly to it (yet). However, extracting relevant properties into structures (components) should greatly improve readability of the game logic.

A typical spawn function should look like

func_stroyent(self, game) {
    // validate editor provided values
    if (self.health <= 0)
        game.freeEntity(self)

    // initialize required component(s)
    self.addComponent(StroyentComponent(self.health, self.style)) // anything provided by the map entities is fine to use here, to create a component

    // common code
    self.think = stroyent_think
    game.linkEntity(self)
}

so in the think or other adapters (block, use, etc..), the required component can be used:

stroyent_think(self, game) {
    // get the compoenent from the entity
    val stroyent = self.components[STROYENT]
    // do some custom logic
    target.health += stroyent.amount
    stroyent.amount--
}

Some properties should stay in the main entity class - mostly those properties that are accessed by the common game code. The good indicator that a property should be extracted to a separate component is that the property is only used from the spawn, think, block, use and other adapters.

Candidates for the extraction are: