Make turnbased combat work
This commit is contained in:
parent
ea7a3c83d7
commit
f70fd64478
7 changed files with 139 additions and 60 deletions
|
|
@ -2,13 +2,9 @@
|
||||||
|
|
||||||
*Note: This is considered an advanced topic.*
|
*Note: This is considered an advanced topic.*
|
||||||
|
|
||||||
Evennia offers many convenient ways to store object data, such as via Attributes or Scripts. This is
|
Evennia offers many convenient ways to store object data, such as via Attributes or Scripts. This is sufficient for most use cases. But if you aim to build a large stand-alone system, trying to squeeze your storage requirements into those may be more complex than you bargain for. Examples may be to store guild data for guild members to be able to change, tracking the flow of money across a game-wide economic system or implement other custom game systems that requires the storage of custom data in a quickly accessible way.
|
||||||
sufficient for most use cases. But if you aim to build a large stand-alone system, trying to squeeze
|
|
||||||
your storage requirements into those may be more complex than you bargain for. Examples may be to
|
Whereas [Tags](../Components/Tags.md) or [Scripts](../Components/Scripts.md) can handle many situations, sometimes things may be easier to handle by adding your own _database model_.
|
||||||
store guild data for guild members to be able to change, tracking the flow of money across a game-
|
|
||||||
wide economic system or implement other custom game systems that requires the storage of custom data
|
|
||||||
in a quickly accessible way. Whereas [Tags](../Components/Tags.md) or [Scripts](../Components/Scripts.md) can handle many situations,
|
|
||||||
sometimes things may be easier to handle by adding your own database model.
|
|
||||||
|
|
||||||
## Overview of database tables
|
## Overview of database tables
|
||||||
|
|
||||||
|
|
@ -22,9 +18,7 @@ retrieving text stored in tables. A table may look like this
|
||||||
2 | Rock | evennia.DefaultObject | None ...
|
2 | Rock | evennia.DefaultObject | None ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Each line is considerably longer in your database. Each column is referred to as a "field" and every
|
Each line is considerably longer in your database. Each column is referred to as a "field" and every row is a separate object. You can check this out for yourself. If you use the default sqlite3 database, go to your game folder and run
|
||||||
row is a separate object. You can check this out for yourself. If you use the default sqlite3
|
|
||||||
database, go to your game folder and run
|
|
||||||
|
|
||||||
evennia dbshell
|
evennia dbshell
|
||||||
|
|
||||||
|
|
@ -42,34 +36,19 @@ You will drop into the database shell. While there, try:
|
||||||
|
|
||||||
sqlite> .exit
|
sqlite> .exit
|
||||||
|
|
||||||
Evennia uses [Django](https://docs.djangoproject.com), which abstracts away the database SQL
|
Evennia uses [Django](https://docs.djangoproject.com), which abstracts away the database SQL manipulation and allows you to search and manipulate your database entirely in Python. Each database table is in Django represented by a class commonly called a *model* since it describes the look of the table. In Evennia, Objects, Scripts, Channels etc are examples of Django models that we then extend and build on.
|
||||||
manipulation and allows you to search and manipulate your database entirely in Python. Each database
|
|
||||||
table is in Django represented by a class commonly called a *model* since it describes the look of
|
|
||||||
the table. In Evennia, Objects, Scripts, Channels etc are examples of Django models that we then
|
|
||||||
extend and build on.
|
|
||||||
|
|
||||||
## Adding a new database table
|
## Adding a new database table
|
||||||
|
|
||||||
Here is how you add your own database table/models:
|
Here is how you add your own database table/models:
|
||||||
|
|
||||||
1. In Django lingo, we will create a new "application" - a subsystem under the main Evennia program.
|
1. In Django lingo, we will create a new "application" - a subsystem under the main Evennia program. For this example we'll call it "myapp". Run the following (you need to have a working Evennia running before you do this, so make sure you have run the steps in [Setup Quickstart](Getting- Started) first):
|
||||||
For this example we'll call it "myapp". Run the following (you need to have a working Evennia
|
|
||||||
running before you do this, so make sure you have run the steps in [Setup Quickstart](Getting-
|
|
||||||
Started) first):
|
|
||||||
|
|
||||||
cd mygame/world
|
cd mygame/world
|
||||||
evennia startapp myapp
|
evennia startapp myapp
|
||||||
|
|
||||||
1. A new folder `myapp` is created. "myapp" will also be the name (the "app label") from now on. We
|
1. A new folder `myapp` is created. "myapp" will also be the name (the "app label") from now on. We chose to put it in the `world/` subfolder here, but you could put it in the root of your `mygame` if that makes more sense. 1. The `myapp` folder contains a few empty default files. What we are interested in for now is `models.py`. In `models.py` you define your model(s). Each model will be a table in the database. See the next section and don't continue until you have added the models you want.
|
||||||
chose to put it in the `world/` subfolder here, but you could put it in the root of your `mygame` if
|
1. You now need to tell Evennia that the models of your app should be a part of your database scheme. Add this line to your `mygame/server/conf/settings.py`file (make sure to use the path where you put `myapp` and don't forget the comma at the end of the tuple):
|
||||||
that makes more sense.
|
|
||||||
1. The `myapp` folder contains a few empty default files. What we are
|
|
||||||
interested in for now is `models.py`. In `models.py` you define your model(s). Each model will be a
|
|
||||||
table in the database. See the next section and don't continue until you have added the models you
|
|
||||||
want.
|
|
||||||
1. You now need to tell Evennia that the models of your app should be a part of your database
|
|
||||||
scheme. Add this line to your `mygame/server/conf/settings.py`file (make sure to use the path where
|
|
||||||
you put `myapp` and don't forget the comma at the end of the tuple):
|
|
||||||
|
|
||||||
```
|
```
|
||||||
INSTALLED_APPS = INSTALLED_APPS + ("world.myapp", )
|
INSTALLED_APPS = INSTALLED_APPS + ("world.myapp", )
|
||||||
|
|
@ -78,19 +57,16 @@ you put `myapp` and don't forget the comma at the end of the tuple):
|
||||||
1. From `mygame/`, run
|
1. From `mygame/`, run
|
||||||
|
|
||||||
evennia makemigrations myapp
|
evennia makemigrations myapp
|
||||||
evennia migrate
|
evennia migrate myapp
|
||||||
|
|
||||||
This will add your new database table to the database. If you have put your game under version
|
This will add your new database table to the database. If you have put your game under version control (if not, [you should](../Coding/Version-Control.md)), don't forget to `git add myapp/*` to add all items
|
||||||
control (if not, [you should](../Coding/Version-Control.md)), don't forget to `git add myapp/*` to add all items
|
|
||||||
to version control.
|
to version control.
|
||||||
|
|
||||||
## Defining your models
|
## Defining your models
|
||||||
|
|
||||||
A Django *model* is the Python representation of a database table. It can be handled like any other Python class. It defines *fields* on itself, objects of a special type. These become the "columns" of the database table. Finally, you create new instances of the model to add new rows to the database.
|
A Django *model* is the Python representation of a database table. It can be handled like any other Python class. It defines *fields* on itself, objects of a special type. These become the "columns" of the database table. Finally, you create new instances of the model to add new rows to the database.
|
||||||
|
|
||||||
We won't describe all aspects of Django models here, for that we refer to the vast [Django
|
We won't describe all aspects of Django models here, for that we refer to the vast [Django documentation](https://docs.djangoproject.com/en/4.1/topics/db/models/) on the subject. Here is a (very) brief example:
|
||||||
documentation](https://docs.djangoproject.com/en/4.1/topics/db/models/) on the subject. Here is a
|
|
||||||
(very) brief example:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
@ -182,7 +158,7 @@ Evennia's normal models don't need to explicitly save, since they are based on `
|
||||||
|
|
||||||
## Using the `SharedMemoryModel` parent
|
## Using the `SharedMemoryModel` parent
|
||||||
|
|
||||||
Evennia doesn't base most of its models on the raw `django.db.models` but on the Evennia base model `evennia.utils.idmapper.models.SharedMemoryModel`. There are two main reasons for this:
|
Evennia doesn't base most of its models on the raw `django.db.models.Model` but on the Evennia base model `evennia.utils.idmapper.models.SharedMemoryModel`. There are two main reasons for this:
|
||||||
|
|
||||||
1. Ease of updating fields without having to explicitly call `save()`
|
1. Ease of updating fields without having to explicitly call `save()`
|
||||||
2. On-object memory persistence and database caching
|
2. On-object memory persistence and database caching
|
||||||
|
|
@ -202,7 +178,7 @@ To explain the second and more important point, consider the following example u
|
||||||
shield.cracked = True # where cracked is not a database field
|
shield.cracked = True # where cracked is not a database field
|
||||||
```
|
```
|
||||||
|
|
||||||
And then later:
|
And then in another function you do
|
||||||
|
|
||||||
```python
|
```python
|
||||||
shield = MyDataStore.objects.get(db_key="SmallShield")
|
shield = MyDataStore.objects.get(db_key="SmallShield")
|
||||||
|
|
@ -249,4 +225,4 @@ To search your new custom database table you need to use its database *manager*
|
||||||
self.caller.msg(match.db_text)
|
self.caller.msg(match.db_text)
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [Django query documentation](https://docs.djangoproject.com/en/4.1/topics/db/queries/) for a lot more information about querying the database.
|
See the [Beginner Tutorial lesson on Django querying](Beginner-Tutorial-Django-queries) for a lot more information about querying the database.
|
||||||
|
|
@ -71,7 +71,6 @@ from traceback import format_exc
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from evennia.commands.cmdset import CmdSet
|
from evennia.commands.cmdset import CmdSet
|
||||||
from evennia.server.models import ServerConfig
|
from evennia.server.models import ServerConfig
|
||||||
from evennia.utils import logger, utils
|
from evennia.utils import logger, utils
|
||||||
|
|
@ -166,7 +165,6 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
]
|
]
|
||||||
errstring = ""
|
errstring = ""
|
||||||
for python_path in python_paths:
|
for python_path in python_paths:
|
||||||
|
|
||||||
if "." in path:
|
if "." in path:
|
||||||
modpath, classname = python_path.rsplit(".", 1)
|
modpath, classname = python_path.rsplit(".", 1)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -200,10 +200,6 @@ class CombatActionStunt(CombatAction):
|
||||||
f"to gain {'advantage' if self.advantage else 'disadvantage'} "
|
f"to gain {'advantage' if self.advantage else 'disadvantage'} "
|
||||||
f"against $You({target.key})!"
|
f"against $You({target.key})!"
|
||||||
)
|
)
|
||||||
self.msg(
|
|
||||||
"|yHaving succeeded, you hold back to plan your next move.|n [hold]",
|
|
||||||
broadcast=False,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.")
|
self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.")
|
||||||
|
|
||||||
|
|
@ -258,6 +254,7 @@ class CombatActionWield(CombatAction):
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.combatant.equipment.move(self.item)
|
self.combatant.equipment.move(self.item)
|
||||||
|
self.msg(f"$You() $conj(wield) $You({self.item.key}).")
|
||||||
|
|
||||||
|
|
||||||
# main combathandler
|
# main combathandler
|
||||||
|
|
@ -311,6 +308,7 @@ class EvAdventureCombatBaseHandler(DefaultScript):
|
||||||
key=combathandler_key,
|
key=combathandler_key,
|
||||||
obj=obj,
|
obj=obj,
|
||||||
persistent=persistent,
|
persistent=persistent,
|
||||||
|
autostart=False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
obj.ndb.combathandler = combathandler
|
obj.ndb.combathandler = combathandler
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,18 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
|
||||||
self.stop()
|
self.stop()
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
||||||
|
def get_combat_summary(self, combatant):
|
||||||
|
"""Add your next queued action to summary"""
|
||||||
|
summary = super().get_combat_summary(combatant)
|
||||||
|
next_action = self.get_next_action_dict(combatant) or {"key": "hold"}
|
||||||
|
next_repeat = self.time_until_next_repeat()
|
||||||
|
|
||||||
|
summary = (
|
||||||
|
f"{summary}\n Your queued action: [|b{next_action['key']}|n] (|b{next_repeat}s|n until"
|
||||||
|
" next round,\n or until all combatants have chosen their next action)."
|
||||||
|
)
|
||||||
|
return summary
|
||||||
|
|
||||||
def get_sides(self, combatant):
|
def get_sides(self, combatant):
|
||||||
"""
|
"""
|
||||||
Get a listing of the two 'sides' of this combat, from the perspective of the provided
|
Get a listing of the two 'sides' of this combat, from the perspective of the provided
|
||||||
|
|
@ -265,19 +277,18 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
|
||||||
self.combatants[combatant] = action_dict
|
self.combatants[combatant] = action_dict
|
||||||
|
|
||||||
# track who inserted actions this turn (non-persistent)
|
# track who inserted actions this turn (non-persistent)
|
||||||
did_action = set(self.ndb.did_action or ())
|
did_action = set(self.ndb.did_action or set())
|
||||||
did_action.add(combatant)
|
did_action.add(combatant)
|
||||||
if len(did_action) >= len(self.combatants):
|
if len(did_action) >= len(self.combatants):
|
||||||
# everyone has inserted an action. Start next turn without waiting!
|
# everyone has inserted an action. Start next turn without waiting!
|
||||||
self.force_repeat()
|
self.force_repeat()
|
||||||
|
|
||||||
def get_next_action_dict(self, combatant, rotate_queue=True):
|
def get_next_action_dict(self, combatant):
|
||||||
"""
|
"""
|
||||||
Give the action_dict for the next action that will be executed.
|
Give the action_dict for the next action that will be executed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
combatant (EvAdventureCharacter, EvAdventureNPC): The combatant to get the action for.
|
combatant (EvAdventureCharacter, EvAdventureNPC): The combatant to get the action for.
|
||||||
rotate_queue (bool, optional): Rotate the queue after getting the action dict.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: The next action-dict in the queue.
|
dict: The next action-dict in the queue.
|
||||||
|
|
@ -355,6 +366,8 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
|
||||||
for combatant in combatants:
|
for combatant in combatants:
|
||||||
self.execute_next_action(combatant)
|
self.execute_next_action(combatant)
|
||||||
|
|
||||||
|
self.ndb.did_action = set()
|
||||||
|
|
||||||
# check if anyone is defeated
|
# check if anyone is defeated
|
||||||
for combatant in list(self.combatants.keys()):
|
for combatant in list(self.combatants.keys()):
|
||||||
if combatant.hp <= 0:
|
if combatant.hp <= 0:
|
||||||
|
|
@ -364,6 +377,8 @@ class EvAdventureTurnbasedCombatHandler(EvAdventureCombatBaseHandler):
|
||||||
self.combatants.pop(combatant)
|
self.combatants.pop(combatant)
|
||||||
self.defeated_combatants.append(combatant)
|
self.defeated_combatants.append(combatant)
|
||||||
self.msg("|r$You() $conj(fall) to the ground, defeated.|n", combatant=combatant)
|
self.msg("|r$You() $conj(fall) to the ground, defeated.|n", combatant=combatant)
|
||||||
|
else:
|
||||||
|
self.combatants[combatant] = self.fallback_action_dict
|
||||||
|
|
||||||
# check if anyone managed to flee
|
# check if anyone managed to flee
|
||||||
flee_timeout = self.flee_timeout
|
flee_timeout = self.flee_timeout
|
||||||
|
|
@ -402,10 +417,14 @@ def _get_combathandler(caller, turn_timeout=30, flee_time=3, combathandler_key="
|
||||||
caller.location,
|
caller.location,
|
||||||
interval=turn_timeout,
|
interval=turn_timeout,
|
||||||
attributes=[("flee_time", flee_time)],
|
attributes=[("flee_time", flee_time)],
|
||||||
combathandler_key=combathandler_key,
|
key=combathandler_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _rerun_current_node(caller, raw_string, **kwargs):
|
||||||
|
return None, kwargs
|
||||||
|
|
||||||
|
|
||||||
def _queue_action(caller, raw_string, **kwargs):
|
def _queue_action(caller, raw_string, **kwargs):
|
||||||
"""
|
"""
|
||||||
Goto-function that queue the action with the CombatHandler. This always returns
|
Goto-function that queue the action with the CombatHandler. This always returns
|
||||||
|
|
@ -462,6 +481,10 @@ def _get_default_wizard_options(caller, **kwargs):
|
||||||
return [
|
return [
|
||||||
{"key": ("back", "b"), "goto": (_step_wizard, {**kwargs, **{"step": "back"}})},
|
{"key": ("back", "b"), "goto": (_step_wizard, {**kwargs, **{"step": "back"}})},
|
||||||
{"key": ("abort", "a"), "goto": (_step_wizard, {**kwargs, **{"step": "abort"}})},
|
{"key": ("abort", "a"), "goto": (_step_wizard, {**kwargs, **{"step": "abort"}})},
|
||||||
|
{
|
||||||
|
"key": "_default",
|
||||||
|
"goto": (_rerun_current_node, kwargs),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -473,6 +496,7 @@ def node_choose_enemy_target(caller, raw_string, **kwargs):
|
||||||
action_dict = kwargs["action_dict"]
|
action_dict = kwargs["action_dict"]
|
||||||
|
|
||||||
combathandler = _get_combathandler(caller)
|
combathandler = _get_combathandler(caller)
|
||||||
|
|
||||||
_, enemies = combathandler.get_sides(caller)
|
_, enemies = combathandler.get_sides(caller)
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
|
|
@ -489,6 +513,30 @@ def node_choose_enemy_target(caller, raw_string, **kwargs):
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_choose_enemy_recipient(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Choose an enemy as a 'recipient' for an action.
|
||||||
|
"""
|
||||||
|
text = "Choose an enemy as a recipient."
|
||||||
|
action_dict = kwargs["action_dict"]
|
||||||
|
|
||||||
|
combathandler = _get_combathandler(caller)
|
||||||
|
_, enemies = combathandler.get_sides(caller)
|
||||||
|
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
"desc": target.get_display_name(caller),
|
||||||
|
"goto": (
|
||||||
|
_step_wizard,
|
||||||
|
{**kwargs, **{"action_dict": {**action_dict, **{"recipient": target}}}},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for target in enemies
|
||||||
|
]
|
||||||
|
options.extend(_get_default_wizard_options(caller, **kwargs))
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def node_choose_allied_target(caller, raw_string, **kwargs):
|
def node_choose_allied_target(caller, raw_string, **kwargs):
|
||||||
"""
|
"""
|
||||||
Choose an enemy as a target for an action
|
Choose an enemy as a target for an action
|
||||||
|
|
@ -507,11 +555,49 @@ def node_choose_allied_target(caller, raw_string, **kwargs):
|
||||||
_step_wizard,
|
_step_wizard,
|
||||||
{
|
{
|
||||||
**kwargs,
|
**kwargs,
|
||||||
**{
|
**{"action_dict": {**action_dict, **{"target": caller}}},
|
||||||
"action_dict": {
|
|
||||||
**{**action_dict, **{"target": caller, "recipient": caller}}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
options.extend(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"desc": target.get_display_name(caller),
|
||||||
|
"goto": (
|
||||||
|
_step_wizard,
|
||||||
|
{
|
||||||
|
**kwargs,
|
||||||
|
**{"action_dict": {**action_dict, **{"target": target}}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for target in allies
|
||||||
|
]
|
||||||
|
)
|
||||||
|
options.extend(_get_default_wizard_options(caller, **kwargs))
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_choose_allied_recipient(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Choose an allied recipient for an action
|
||||||
|
"""
|
||||||
|
text = "Choose an ally as a recipient."
|
||||||
|
action_dict = kwargs["action_dict"]
|
||||||
|
|
||||||
|
combathandler = _get_combathandler(caller)
|
||||||
|
allies, _ = combathandler.get_sides(caller)
|
||||||
|
|
||||||
|
# can choose yourself
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
"desc": "Yourself",
|
||||||
|
"goto": (
|
||||||
|
_step_wizard,
|
||||||
|
{
|
||||||
|
**kwargs,
|
||||||
|
**{"action_dict": {**action_dict, **{"recipient": caller}}},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -527,7 +613,7 @@ def node_choose_allied_target(caller, raw_string, **kwargs):
|
||||||
**{
|
**{
|
||||||
"action_dict": {
|
"action_dict": {
|
||||||
**action_dict,
|
**action_dict,
|
||||||
**{"target": target, "recipient": target},
|
**{"recipient": target},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -585,7 +671,10 @@ def node_choose_use_item(caller, raw_string, **kwargs):
|
||||||
options = [
|
options = [
|
||||||
{
|
{
|
||||||
"desc": item.get_display_name(caller),
|
"desc": item.get_display_name(caller),
|
||||||
"goto": (_step_wizard, {**kwargs, **{**action_dict, **{"item": item}}}),
|
"goto": (
|
||||||
|
_step_wizard,
|
||||||
|
{**kwargs, **{"action_dict": {**action_dict, **{"item": item}}}},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
for item in caller.equipment.get_usable_objects_from_backpack()
|
for item in caller.equipment.get_usable_objects_from_backpack()
|
||||||
]
|
]
|
||||||
|
|
@ -607,7 +696,10 @@ def node_choose_wield_item(caller, raw_string, **kwargs):
|
||||||
options = [
|
options = [
|
||||||
{
|
{
|
||||||
"desc": item.get_display_name(caller),
|
"desc": item.get_display_name(caller),
|
||||||
"goto": (_step_wizard, {**kwargs, **{**action_dict, **{"item": item}}}),
|
"goto": (
|
||||||
|
_step_wizard,
|
||||||
|
{**kwargs, **{"action_dict": {**action_dict, **{"item": item}}}},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
for item in caller.equipment.get_wieldable_objects_from_backpack()
|
for item in caller.equipment.get_wieldable_objects_from_backpack()
|
||||||
]
|
]
|
||||||
|
|
@ -623,6 +715,8 @@ def node_combat(caller, raw_string, **kwargs):
|
||||||
|
|
||||||
combathandler = _get_combathandler(caller)
|
combathandler = _get_combathandler(caller)
|
||||||
|
|
||||||
|
caller.msg(f"combathandler.combatants: {combathandler.combatants}")
|
||||||
|
|
||||||
text = combathandler.get_combat_summary(caller)
|
text = combathandler.get_combat_summary(caller)
|
||||||
options = [
|
options = [
|
||||||
{
|
{
|
||||||
|
|
@ -642,8 +736,8 @@ def node_combat(caller, raw_string, **kwargs):
|
||||||
{
|
{
|
||||||
"steps": [
|
"steps": [
|
||||||
"node_choose_ability",
|
"node_choose_ability",
|
||||||
"node_choose_allied_target",
|
|
||||||
"node_choose_enemy_target",
|
"node_choose_enemy_target",
|
||||||
|
"node_choose_allied_recipient",
|
||||||
],
|
],
|
||||||
"action_dict": {"key": "stunt", "advantage": True},
|
"action_dict": {"key": "stunt", "advantage": True},
|
||||||
},
|
},
|
||||||
|
|
@ -656,7 +750,7 @@ def node_combat(caller, raw_string, **kwargs):
|
||||||
{
|
{
|
||||||
"steps": [
|
"steps": [
|
||||||
"node_choose_ability",
|
"node_choose_ability",
|
||||||
"node_choose_enemy_target",
|
"node_choose_enemy_recipient",
|
||||||
"node_choose_allied_target",
|
"node_choose_allied_target",
|
||||||
],
|
],
|
||||||
"action_dict": {"key": "stunt", "advantage": False},
|
"action_dict": {"key": "stunt", "advantage": False},
|
||||||
|
|
@ -701,6 +795,10 @@ def node_combat(caller, raw_string, **kwargs):
|
||||||
"desc": "hold, doing nothing",
|
"desc": "hold, doing nothing",
|
||||||
"goto": (_queue_action, {"action_dict": {"key": "hold"}}),
|
"goto": (_queue_action, {"action_dict": {"key": "hold"}}),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "_default",
|
||||||
|
"goto": "node_combat",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
@ -763,6 +861,8 @@ class CmdTurnAttack(Command):
|
||||||
{
|
{
|
||||||
"node_choose_enemy_target": node_choose_enemy_target,
|
"node_choose_enemy_target": node_choose_enemy_target,
|
||||||
"node_choose_allied_target": node_choose_allied_target,
|
"node_choose_allied_target": node_choose_allied_target,
|
||||||
|
"node_choose_enemy_recipient": node_choose_enemy_recipient,
|
||||||
|
"node_choose_allied_recipient": node_choose_allied_recipient,
|
||||||
"node_choose_ability": node_choose_ability,
|
"node_choose_ability": node_choose_ability,
|
||||||
"node_choose_use_item": node_choose_use_item,
|
"node_choose_use_item": node_choose_use_item,
|
||||||
"node_choose_wield_item": node_choose_wield_item,
|
"node_choose_wield_item": node_choose_wield_item,
|
||||||
|
|
@ -770,6 +870,7 @@ class CmdTurnAttack(Command):
|
||||||
},
|
},
|
||||||
startnode="node_combat",
|
startnode="node_combat",
|
||||||
combathandler=combathandler,
|
combathandler=combathandler,
|
||||||
|
auto_look=False,
|
||||||
# cmdset_mergetype="Union",
|
# cmdset_mergetype="Union",
|
||||||
persistent=True,
|
persistent=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ class EquipmentHandler:
|
||||||
WieldLocation.BACKPACK: [],
|
WieldLocation.BACKPACK: [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
self.slots[WieldLocation.BACKPACK] = [
|
||||||
|
obj for obj in self.slots[WieldLocation.BACKPACK] if obj and obj.id
|
||||||
|
]
|
||||||
|
|
||||||
def _save(self):
|
def _save(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -358,6 +361,7 @@ class EquipmentHandler:
|
||||||
obj
|
obj
|
||||||
for obj in self.slots[WieldLocation.BACKPACK]
|
for obj in self.slots[WieldLocation.BACKPACK]
|
||||||
if obj
|
if obj
|
||||||
|
and obj.id
|
||||||
and obj.inventory_use_slot
|
and obj.inventory_use_slot
|
||||||
in (WieldLocation.WEAPON_HAND, WieldLocation.TWO_HANDS, WieldLocation.SHIELD_HAND)
|
in (WieldLocation.WEAPON_HAND, WieldLocation.TWO_HANDS, WieldLocation.SHIELD_HAND)
|
||||||
]
|
]
|
||||||
|
|
@ -376,7 +380,7 @@ class EquipmentHandler:
|
||||||
return [
|
return [
|
||||||
obj
|
obj
|
||||||
for obj in self.slots[WieldLocation.BACKPACK]
|
for obj in self.slots[WieldLocation.BACKPACK]
|
||||||
if obj and obj.inventory_use_slot in (WieldLocation.BODY, WieldLocation.HEAD)
|
if obj and obj.id and obj.inventory_use_slot in (WieldLocation.BODY, WieldLocation.HEAD)
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_usable_objects_from_backpack(self):
|
def get_usable_objects_from_backpack(self):
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ class ExtendedLoopingCall(LoopingCall):
|
||||||
if self.running and self.interval > 0:
|
if self.running and self.interval > 0:
|
||||||
total_runtime = self.clock.seconds() - self.starttime
|
total_runtime = self.clock.seconds() - self.starttime
|
||||||
interval = self.start_delay or self.interval
|
interval = self.start_delay or self.interval
|
||||||
|
print("next_call_time:", total_runtime, interval, self.clock.seconds(), self.starttime)
|
||||||
return max(0, interval - (total_runtime % self.interval))
|
return max(0, interval - (total_runtime % self.interval))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -889,6 +889,9 @@ class EvMenu:
|
||||||
if not nodename:
|
if not nodename:
|
||||||
# no nodename return. Re-run current node
|
# no nodename return. Re-run current node
|
||||||
nodename = self.nodename
|
nodename = self.nodename
|
||||||
|
elif nodename_or_callable is None:
|
||||||
|
# repeat current node
|
||||||
|
nodename = self.nodename
|
||||||
else:
|
else:
|
||||||
# the nodename given directly
|
# the nodename given directly
|
||||||
nodename = nodename_or_callable
|
nodename = nodename_or_callable
|
||||||
|
|
@ -934,7 +937,6 @@ class EvMenu:
|
||||||
if options:
|
if options:
|
||||||
options = [options] if isinstance(options, dict) else options
|
options = [options] if isinstance(options, dict) else options
|
||||||
for inum, dic in enumerate(options):
|
for inum, dic in enumerate(options):
|
||||||
|
|
||||||
# homogenize the options dict
|
# homogenize the options dict
|
||||||
keys = make_iter(dic.get("key"))
|
keys = make_iter(dic.get("key"))
|
||||||
desc = dic.get("desc", dic.get("text", None))
|
desc = dic.get("desc", dic.get("text", None))
|
||||||
|
|
@ -1326,7 +1328,6 @@ def list_node(option_generator, select=None, pagesize=10):
|
||||||
return None, kwargs
|
return None, kwargs
|
||||||
|
|
||||||
def _list_node(caller, raw_string, **kwargs):
|
def _list_node(caller, raw_string, **kwargs):
|
||||||
|
|
||||||
option_list = (
|
option_list = (
|
||||||
option_generator(caller) if callable(option_generator) else option_generator
|
option_generator(caller) if callable(option_generator) else option_generator
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue