Source

hexbattle / hexbattle.py

Full commit
#!/usr/bin/env python
# encoding: utf-8

"""The hexbattle game scene.

Plan:

- Get mouse position - done
- Add hexgrid. hex over which the mouse is gets highlighted. A highlighted hex can simply mean to overlay the grid with a brigther grid element. Just insert it via self.visible.insert(self.visible.index(self.hexgrid)+1), then it will be over the grix, but below everything else. Or split visible into layers. Better still: Make the layers properties, which automatically update self.visible when changed. - partly done, but not yet with hex-graphics (only blobs)
- Add a charakter on the hexgrid. - done
- Beam the Charakter (click, then click new position). - done
- Add a command interface: When a char gets dropped, show hexes around him. - done
- Let two charakters fight (click, then click other char). Who dies disappears  (→ to “died” list) - done, without the died list.
- Add several charakters and cycle through them in each round. All player controlled. - done

- Add basic AI.
    * Guess maximum effect per round up to the units next attack (single step, no complex tactics - done
    * → all attack the weakest point). - done
- Add real game: Scene selects the chars via initiative. User only controls one team. - done
- Add status info for an attack: hit, wound, critical, died.
- Add status info to characters (number of wounds and critical wounds). 
- Add movement on the grid with forbidden fields (different for final position and intermediate: Final not on anyone, intermediate not adjadent to an enemy).
- Highlight all allied units, differently for own group, enemy groups and neutral (None).
- Add hit info: overlay with life time (after which it disappears).

Ideas:

- Simple sparse hex collision ckecking:
    * hexmap = {}
    * hexmap[(thing.x, thing.y): thing]
    * def is_hex_free(x, y):
        return hexmap.has_key((x, y)). 

- Don’t move the unit itself, but a transparent copy of it and then let the unit walk (visualizes correctly, that the movement with the mouse is only decision making and not actual movement. See wesnoth :) ). 

"""

### A "Scene method not implemented" Exception class. 

class MethodNotImplemented(Exception):
    """A warning to display if any necessary scripting function isn't implemented."""
    def __init__(self, func, implementation = None):
        self.func = func
        self.implementation = implementation

    def __str__(self):
        if self.implementation is None:
            return "The method " + str(self.func) + " must be implemented."
        else:
            return "The method " + str(self.func) + " must be implemented." + "\nThe simplest way is to just add the following lines to your class:" + self.implementation

### A base Scene class to inherit from (API definition)

### Structure

__structure__ = """Structure plan:

* Interaction
→ UiState
→ presentation (also needed for the sound later on, so uses its own class)
→ input (on_draw functions and such)

* Logic
→ model (scene.update should partly move into this: model.step)
→ command (callable functions, see results → possible commands). Never used directly (only via the possible actions)
→ results (hexmap, units, actor, possible commands)

Loosely coupled: 
- UiState sends commands to the logic and reads results. 
- Presentation just shows the UiState. 
- Input changes the UiState. It needs the state as attribute.
- command changes the _model and returns the results - including new commands to call.
- results are recreated after every command. Available as logic.results as well as returned by any command (to avoid having to issue empty commands).

"""

__plan__ = """
Step by step:

1. First all logic from UiState into the model. Model gets all state which is independent of the UI. Any necessary interaction in UiState calls model methods. √
2. Refactor in such a way that the UiState and (even more importantly) the input do not need to call the model directly anymore.
2.1. As a start, let the UiState manage dumb sprites for all chars which are active. Then I can remove a lot of UiState dependent functionality from the chars.
2.2. return all actions on every command: command(...) → commands (instance of Commands).
3. Make the UiState adhere the list of possible commands.
4. Turn the model into a pure state machine.
"""

#### Imports ####

from core import core, run
from pyglet.clock import schedule_interval

from hexbattle_gui import Interaction
from hexbattle_logic import Logic

#### Basic Game-Scene API ####        

def start(dt=0):
    """start the scene"""
    logic = Logic()
    interaction = Interaction(logic)
    @core.win.event
    def on_draw():
        interaction.presentation.draw()
    schedule_interval(interaction.state.update, 0.02)
    schedule_interval(interaction.state.update_interactive_elements, 0.01)
    core.win.push_handlers(interaction.input)
    
if __name__ == "__main__": 
    # Call this Scene via game.py
    # pass the commandline args
    from sys import argv
    if "--test" in argv: 
        from doctest import testmod
        testmod()
    run(__file__)