Refactor of ExtendedRoom
This commit is contained in:
parent
6066f33a9b
commit
615b98c171
6 changed files with 1271 additions and 494 deletions
|
|
@ -94,6 +94,7 @@ EvEditor = None
|
||||||
EvMore = None
|
EvMore = None
|
||||||
ANSIString = None
|
ANSIString = None
|
||||||
signals = None
|
signals = None
|
||||||
|
FuncParser = None
|
||||||
|
|
||||||
# Handlers
|
# Handlers
|
||||||
SESSION_HANDLER = None
|
SESSION_HANDLER = None
|
||||||
|
|
@ -157,7 +158,7 @@ def _init(portal_mode=False):
|
||||||
global TASK_HANDLER
|
global TASK_HANDLER
|
||||||
global GLOBAL_SCRIPTS, OPTION_CLASSES
|
global GLOBAL_SCRIPTS, OPTION_CLASSES
|
||||||
global EvMenu, EvTable, EvForm, EvMore, EvEditor
|
global EvMenu, EvTable, EvForm, EvMore, EvEditor
|
||||||
global ANSIString
|
global ANSIString, FuncParser
|
||||||
global AttributeProperty, TagProperty, TagCategoryProperty
|
global AttributeProperty, TagProperty, TagCategoryProperty
|
||||||
|
|
||||||
# Parent typeclasses
|
# Parent typeclasses
|
||||||
|
|
@ -203,6 +204,7 @@ def _init(portal_mode=False):
|
||||||
from .utils.evmenu import EvMenu
|
from .utils.evmenu import EvMenu
|
||||||
from .utils.evmore import EvMore
|
from .utils.evmore import EvMore
|
||||||
from .utils.evtable import EvTable
|
from .utils.evtable import EvTable
|
||||||
|
from .utils.funcparser import FuncParser
|
||||||
|
|
||||||
# search functions
|
# search functions
|
||||||
from .utils.search import (
|
from .utils.search import (
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
# Extended Room
|
# Extended Room
|
||||||
|
|
||||||
Contribution - Griatch 2012, vincent-lg 2019
|
Contribution - Griatch 2012, vincent-lg 2019, Griatch 2023
|
||||||
|
|
||||||
This extends the normal `Room` typeclass to allow its description to change
|
This extends the normal `Room` typeclass to allow its description to change with
|
||||||
with time-of-day and/or season. It also adds 'details' for the player to look at
|
time-of-day and/or season as well as any other state (like flooded or dark).
|
||||||
in the room (without having to create a new in-game object for each). The room is
|
Embedding `$state(burning, This place is on fire!)` in the description will
|
||||||
supported by new `look` and `desc` commands.
|
allow for changing the description based on room state. The room also supports
|
||||||
|
`details` for the player to look at in the room (without having to create a new
|
||||||
|
in-game object for each), as well as support for random echoes. The room
|
||||||
|
comes with a set of alternate commands for `look` and `@desc`, as well as new
|
||||||
|
commands `detail`, `roomstate` and `time`.
|
||||||
|
|
||||||
## Installation/testing:
|
## Installation
|
||||||
|
|
||||||
Adding the `ExtendedRoomCmdset` to the default character cmdset will add all
|
Add the `ExtendedRoomCmdset` to the default character cmdset will add all
|
||||||
new commands for use.
|
new commands for use.
|
||||||
|
|
||||||
In more detail, in `mygame/commands/default_cmdsets.py`:
|
In more detail, in `mygame/commands/default_cmdsets.py`:
|
||||||
|
|
@ -30,52 +34,157 @@ class CharacterCmdset(default_cmds.CharacterCmdSet):
|
||||||
Then reload to make the new commands available. Note that they only work
|
Then reload to make the new commands available. Note that they only work
|
||||||
on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right
|
on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right
|
||||||
typeclass or use the `typeclass` command to swap existing rooms. Note that since
|
typeclass or use the `typeclass` command to swap existing rooms. Note that since
|
||||||
this contrib overrides the `look` command, you will need to add the
|
this contrib overrides the `look` and `@desc` commands, you will need to add the
|
||||||
`extended_room.ExtendedRoomCmdSet` to the default character cmdset *after*
|
`extended_room.ExtendedRoomCmdSet` to the default character cmdset *after*
|
||||||
super().at_cmdset_creation(), or it will be overridden by the default look.
|
`super().at_cmdset_creation()`, or they will be overridden by the default look.
|
||||||
|
|
||||||
|
To dig a new extended room:
|
||||||
|
|
||||||
|
dig myroom:evennia.contrib.grid.extended_room.ExtendedRoom = north,south
|
||||||
|
|
||||||
|
To make all new rooms ExtendedRooms without having to specify it, make your
|
||||||
|
`Room` typeclass inherit from the `ExtendedRoom` and then reload:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# in mygame/typeclasses/rooms.py
|
||||||
|
|
||||||
|
from evennia.contrib.grid.extended_room import ExtendedRoom
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
class Room(ObjectParent, ExtendedRoom):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Time-changing description slots
|
### State-dependent description slots
|
||||||
|
|
||||||
This allows to change the full description text the room shows
|
By default, the normal `room.db.desc` description is used. You can however
|
||||||
depending on larger time variations. Four seasons (spring, summer,
|
add new state-ful descriptions with `room.add_desc(description,
|
||||||
autumn and winter) are used by default. The season is calculated
|
room_state=roomstate)` or with the in-game command
|
||||||
on-demand (no Script or timer needed) and updates the full text block.
|
|
||||||
|
|
||||||
There is also a general description which is used as fallback if
|
```
|
||||||
one or more of the seasonal descriptions are not set when their
|
@desc/roomstate [<description>]
|
||||||
time comes.
|
```
|
||||||
|
|
||||||
An updated `desc` command allows for setting seasonal descriptions.
|
For example
|
||||||
|
|
||||||
The room uses the `evennia.utils.gametime.GameTime` global script. This is
|
```
|
||||||
started by default, but if you have deactivated it, you need to
|
@desc/dark This room is pitch black.`.
|
||||||
supply your own time keeping mechanism.
|
|
||||||
|
|
||||||
### In-description changing tags
|
```
|
||||||
|
|
||||||
Within each seasonal (or general) description text, you can also embed
|
|
||||||
time-of-day dependent sections. Text inside such a tag will only show
|
These will be stored in Attributes `desc_<roomstate>`. To set the default,
|
||||||
during that particular time of day. The tags looks like `<timeslot> ...
|
fallback description, just use `@desc <description>`.
|
||||||
</timeslot>`. By default there are four timeslots per day - morning,
|
To activate a state on the room, use `room.add/remove_state(*roomstate)` or the in-game
|
||||||
afternoon, evening and night.
|
command
|
||||||
|
```
|
||||||
|
roomstate <state> (use it again to toggle the state off)
|
||||||
|
```
|
||||||
|
For example
|
||||||
|
```
|
||||||
|
roomstate dark
|
||||||
|
```
|
||||||
|
There is one in-built, time-based state `season`. By default these are 'spring',
|
||||||
|
'summer', 'autumn' and 'winter'. The `room.get_season()` method returns the
|
||||||
|
current season based on the in-game time. By default they change with a 12-month
|
||||||
|
in-game time schedule. You can control them with
|
||||||
|
```
|
||||||
|
ExtendedRoom.months_per_year # default 12
|
||||||
|
ExtendedRoom.seasons_per year # a dict of {"season": (start, end), ...} where
|
||||||
|
# start/end are given in fractions of the whole year
|
||||||
|
```
|
||||||
|
To set a seasonal description, just set it as normal, with `room.add_desc` or
|
||||||
|
in-game with
|
||||||
|
|
||||||
|
```
|
||||||
|
@desc/winter This room is filled with snow.
|
||||||
|
@desc/autumn Red and yellow leaves cover the ground.
|
||||||
|
```
|
||||||
|
|
||||||
|
Normally the season changes with the in-game time, you can also 'force' a given
|
||||||
|
season by setting its state
|
||||||
|
```
|
||||||
|
roomstate winter
|
||||||
|
```
|
||||||
|
If you set the season manually like this, it won't change automatically again
|
||||||
|
until you unset it.
|
||||||
|
|
||||||
|
You can get the stateful description from the room with `room.get_stateful_desc()`.
|
||||||
|
|
||||||
|
### Changing parts of description based on state
|
||||||
|
|
||||||
|
All descriptions can have embedded `$state(roomstate, description)`
|
||||||
|
[FuncParser tags](FuncParser) embedded in them. Here is an example:
|
||||||
|
|
||||||
|
```py
|
||||||
|
room.add_desc("This a nice beach. "
|
||||||
|
"$state(empty, It is completely empty)"
|
||||||
|
"$state(full, It is full of people).", room_state="summer")
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a summer-description with special embedded strings. If you set the room
|
||||||
|
with
|
||||||
|
|
||||||
|
> room.add_room_state("summer", "empty")
|
||||||
|
> room.get_stateful_desc()
|
||||||
|
|
||||||
|
This is a nice beach. It is completely empty
|
||||||
|
|
||||||
|
> room.remove_room_state("empty")
|
||||||
|
> room.add_room_state("full")
|
||||||
|
> room.get_stateful_desc()
|
||||||
|
|
||||||
|
This is a nice beach. It is full of people.
|
||||||
|
|
||||||
|
There are four time-of-day states that are meant to be used with these tags. The
|
||||||
|
room tracks and changes these automatically. By default they are 'morning',
|
||||||
|
'afternoon', 'evening' and 'night'. You can get the current time-slot with
|
||||||
|
`room.get_time_of_day`. You can control them with
|
||||||
|
|
||||||
|
```
|
||||||
|
ExtendedRoom.hours_per_day # default 24
|
||||||
|
ExtendedRoom.times_of_day # dict of {season: (start, end), ...} where
|
||||||
|
# the start/end are given as fractions of the day
|
||||||
|
```
|
||||||
|
|
||||||
|
You use these inside descriptions as normal:
|
||||||
|
|
||||||
|
"A glade. $(morning, The morning sun shines down through the branches)."
|
||||||
|
|
||||||
### Details
|
### Details
|
||||||
|
|
||||||
The Extended Room can be "detailed" with special keywords. This makes
|
_Details_ are "virtual" targets to look at in a room, without having to create a
|
||||||
use of a special `Look` command. Details are "virtual" targets to look
|
new database instance for every thing. It's good to add more information to a
|
||||||
at, without there having to be a database object created for it. The
|
location. The details are stored as strings in a dictionary.
|
||||||
Details are simply stored in a dictionary on the room and if the look
|
|
||||||
command cannot find an object match for a `look <target>` command it
|
detail window = There is a window leading out.
|
||||||
will also look through the available details at the current location
|
detail rock = The rock has a text written on it: 'Do not dare lift me'.
|
||||||
if applicable. The `detail` command is used to change details.
|
|
||||||
|
When you are in the room you can then do `look window` or `look rock` and get
|
||||||
|
the matching detail-description. This requires the new custom `look` command.
|
||||||
|
|
||||||
|
### Random echoes
|
||||||
|
|
||||||
|
The `ExtendedRoom` supports random echoes. Just set them as an Attribute list
|
||||||
|
`room_messages`:
|
||||||
|
|
||||||
|
```
|
||||||
|
room.room_message_rate = 120 # in seconds. 0 to disable
|
||||||
|
room.db.room_messages = ["A car passes by.", "You hear the sound of car horns."]
|
||||||
|
room.start_repeat_broadcast_messages() # also a server reload works
|
||||||
|
```
|
||||||
|
|
||||||
|
These will start randomly echoing to the room every 120s.
|
||||||
|
|
||||||
|
|
||||||
### Extra commands
|
### Extra commands
|
||||||
|
|
||||||
- `CmdExtendedRoomLook` - look command supporting room details
|
- `CmdExtendedRoomLook` (`look`) - look command supporting room details
|
||||||
- `CmdExtendedRoomDesc` - desc command allowing to add seasonal descs,
|
- `CmdExtendedRoomDesc` (`@desc`) - desc command allowing to add stateful descs,
|
||||||
- `CmdExtendedRoomDetail` - command allowing to manipulate details in this room
|
- `CmdExtendeRoomState` (`roomstate`) - toggle room states
|
||||||
as well as listing them
|
- `CmdExtendedRoomDetail` (`detail`) - list and manipulate room details
|
||||||
- `CmdExtendedRoomGameTime` - A simple `time` command, displaying the current
|
- `CmdExtendedRoomGameTime` (`time`) - Shows the current time and season in the room.
|
||||||
time and season.
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Extended Room - Griatch 2012, vincent-lg 2019
|
Extended Room - Griatch 2012, vincent-lg 2019, Griatch 2023
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -7,5 +7,6 @@ from .extended_room import CmdExtendedRoomDesc # noqa
|
||||||
from .extended_room import CmdExtendedRoomDetail # noqa
|
from .extended_room import CmdExtendedRoomDetail # noqa
|
||||||
from .extended_room import CmdExtendedRoomGameTime # noqa
|
from .extended_room import CmdExtendedRoomGameTime # noqa
|
||||||
from .extended_room import CmdExtendedRoomLook # noqa
|
from .extended_room import CmdExtendedRoomLook # noqa
|
||||||
|
from .extended_room import CmdExtendedRoomState # noqa
|
||||||
from .extended_room import ExtendedRoom # noqa
|
from .extended_room import ExtendedRoom # noqa
|
||||||
from .extended_room import ExtendedRoomCmdSet # noqa
|
from .extended_room import ExtendedRoomCmdSet # noqa
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -6,118 +6,392 @@ Testing of ExtendedRoom contrib
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from evennia import create_object
|
||||||
|
from evennia.utils.test_resources import BaseEvenniaCommandTest, EvenniaTestCase
|
||||||
from mock import Mock, patch
|
from mock import Mock, patch
|
||||||
|
from parameterized import parameterized
|
||||||
from evennia.commands.default.tests import BaseEvenniaCommandTest
|
|
||||||
from evennia.objects.objects import DefaultRoom
|
|
||||||
|
|
||||||
from . import extended_room
|
from . import extended_room
|
||||||
|
|
||||||
|
|
||||||
class ForceUTCDatetime(datetime.datetime):
|
def _get_timestamp(season, time_of_day):
|
||||||
|
"""
|
||||||
|
Utility to get a timestamp for a given season and time of day.
|
||||||
|
|
||||||
"""Force UTC datetime."""
|
"""
|
||||||
|
# grab a month / time given a season and time of day
|
||||||
@classmethod
|
seasons = {"spring": 3, "summer": 6, "autumn": 9, "winter": 12}
|
||||||
def fromtimestamp(cls, timestamp):
|
times_of_day = {"morning": 6, "afternoon": 12, "evening": 18, "night": 0}
|
||||||
"""Force fromtimestamp to run with naive datetimes."""
|
# return a datetime object for the 1st of the month at the given hour
|
||||||
return datetime.datetime.utcfromtimestamp(timestamp)
|
return datetime.datetime(2064, seasons[season], 1, times_of_day[time_of_day]).timestamp()
|
||||||
|
|
||||||
|
|
||||||
@patch("evennia.contrib.grid.extended_room.extended_room.datetime.datetime", ForceUTCDatetime)
|
class TestExtendedRoom(EvenniaTestCase):
|
||||||
# mock gametime to return April 9, 2064, at 21:06 (spring evening)
|
"""
|
||||||
@patch("evennia.utils.gametime.gametime", new=Mock(return_value=2975000766))
|
Test Extended Room typeclass.
|
||||||
class TestExtendedRoom(BaseEvenniaCommandTest):
|
|
||||||
room_typeclass = extended_room.ExtendedRoom
|
"""
|
||||||
DETAIL_DESC = "A test detail."
|
|
||||||
SPRING_DESC = "A spring description."
|
base_room_desc = "Base room description."
|
||||||
OLD_DESC = "Old description."
|
|
||||||
settings.TIME_ZONE = "UTC"
|
def setUp(self):
|
||||||
|
self.room = create_object(extended_room.ExtendedRoom, key="Test Room")
|
||||||
|
self.room.desc = self.base_room_desc
|
||||||
|
|
||||||
|
def test_room_description(self):
|
||||||
|
"""
|
||||||
|
Test that the vanilla room description is returned as expected.
|
||||||
|
"""
|
||||||
|
room_desc = self.room.get_display_desc(None)
|
||||||
|
self.assertEqual(room_desc, self.base_room_desc)
|
||||||
|
|
||||||
|
@parameterized.expand(
|
||||||
|
[
|
||||||
|
("spring", "Spring room description."),
|
||||||
|
("summer", "Summer room description."),
|
||||||
|
("autumn", "Autumn room description."),
|
||||||
|
("winter", "Winter room description."),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@patch("evennia.utils.gametime.gametime")
|
||||||
|
def test_seasonal_room_descriptions(self, season, desc, mock_gametime):
|
||||||
|
"""
|
||||||
|
Test that the room description changes with the season.
|
||||||
|
"""
|
||||||
|
mock_gametime.return_value = _get_timestamp(season, "morning")
|
||||||
|
self.room.add_desc(desc, room_state=season)
|
||||||
|
|
||||||
|
room_desc = self.room.get_display_desc(None)
|
||||||
|
self.assertEqual(room_desc, desc)
|
||||||
|
|
||||||
|
@parameterized.expand(
|
||||||
|
[
|
||||||
|
("morning", "Morning room description."),
|
||||||
|
("afternoon", "Afternoon room description."),
|
||||||
|
("evening", "Evening room description."),
|
||||||
|
("night", "Night room description."),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@patch("evennia.utils.gametime.gametime")
|
||||||
|
def test_get_time_of_day_tags(self, time_of_day, desc, mock_gametime):
|
||||||
|
"""
|
||||||
|
Test room with $
|
||||||
|
"""
|
||||||
|
mock_gametime.return_value = _get_timestamp("spring", time_of_day)
|
||||||
|
room_time_of_day = self.room.get_time_of_day()
|
||||||
|
self.assertEqual(room_time_of_day, time_of_day)
|
||||||
|
|
||||||
|
self.room.add_desc(
|
||||||
|
"$state(morning, Morning room description.)"
|
||||||
|
"$state(afternoon, Afternoon room description.)"
|
||||||
|
"$state(evening, Evening room description.)"
|
||||||
|
"$state(night, Night room description.)"
|
||||||
|
" What a great day!"
|
||||||
|
)
|
||||||
|
room_desc = self.room.get_display_desc(None)
|
||||||
|
self.assertEqual(room_desc, f"{desc} What a great day!")
|
||||||
|
|
||||||
|
def test_room_states(self):
|
||||||
|
"""
|
||||||
|
Test rooms with custom game states.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.room.add_desc(
|
||||||
|
"$state(under_construction, This room is under construction.)"
|
||||||
|
" $state(under_repair, This room is under repair.)"
|
||||||
|
)
|
||||||
|
self.room.add_room_state("under_construction")
|
||||||
|
self.assertEqual(self.room.room_states, ["under_construction"])
|
||||||
|
self.assertEqual(self.room.get_display_desc(None), "This room is under construction. ")
|
||||||
|
|
||||||
|
self.room.add_room_state("under_repair")
|
||||||
|
self.assertEqual(self.room.room_states, ["under_construction", "under_repair"])
|
||||||
|
self.assertEqual(
|
||||||
|
self.room.get_display_desc(None),
|
||||||
|
"This room is under construction. This room is under repair.",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.room.remove_room_state("under_construction")
|
||||||
|
self.assertEqual(
|
||||||
|
self.room.get_display_desc(None),
|
||||||
|
" This room is under repair.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_alternative_descs(self):
|
||||||
|
"""
|
||||||
|
Test rooms with alternate descriptions.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.room.add_desc("The room is burning!", room_state="burning")
|
||||||
|
self.room.add_desc("The room is flooding!", room_state="flooding")
|
||||||
|
self.assertEqual(self.room.get_display_desc(None), self.base_room_desc)
|
||||||
|
|
||||||
|
self.room.add_room_state("burning")
|
||||||
|
self.assertEqual(self.room.get_display_desc(None), "The room is burning!")
|
||||||
|
|
||||||
|
self.room.add_room_state("flooding")
|
||||||
|
self.room.remove_room_state("burning")
|
||||||
|
self.assertEqual(self.room.get_display_desc(None), "The room is flooding!")
|
||||||
|
|
||||||
|
self.room.clear_room_state()
|
||||||
|
self.assertEqual(self.room.get_display_desc(None), self.base_room_desc)
|
||||||
|
|
||||||
|
def test_details(self):
|
||||||
|
"""
|
||||||
|
Test room details.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.room.add_detail("test", "Test detail.")
|
||||||
|
self.room.add_detail("test2", "Test detail 2.")
|
||||||
|
self.room.add_detail("window", "Window detail.")
|
||||||
|
self.room.add_detail("window pane", "Window Pane detail.")
|
||||||
|
|
||||||
|
self.assertEqual(self.room.get_detail("test"), "Test detail.")
|
||||||
|
self.assertEqual(self.room.get_detail("test2"), "Test detail 2.")
|
||||||
|
self.assertEqual(self.room.get_detail("window"), "Window detail.")
|
||||||
|
self.assertEqual(self.room.get_detail("window pane"), "Window Pane detail.")
|
||||||
|
self.assertEqual(self.room.get_detail("win"), "Window detail.")
|
||||||
|
self.assertEqual(self.room.get_detail("window p"), "Window Pane detail.")
|
||||||
|
|
||||||
|
self.room.remove_detail("test")
|
||||||
|
self.assertEqual(self.room.get_detail("test"), "Test detail 2.") # finding nearest
|
||||||
|
self.room.remove_detail("test2")
|
||||||
|
self.assertEqual(self.room.get_detail("test"), None) # all test* gone
|
||||||
|
|
||||||
|
|
||||||
|
class TestExtendedRoomCommands(BaseEvenniaCommandTest):
|
||||||
|
"""
|
||||||
|
Test the ExtendedRoom commands.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
base_room_desc = "Base room description."
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.room1.ndb.last_timeslot = "afternoon"
|
self.room1.swap_typeclass("evennia.contrib.grid.extended_room.ExtendedRoom")
|
||||||
self.room1.ndb.last_season = "winter"
|
self.room1.desc = self.base_room_desc
|
||||||
self.room1.db.details = {"testdetail": self.DETAIL_DESC}
|
|
||||||
self.room1.db.spring_desc = self.SPRING_DESC
|
|
||||||
self.room1.db.desc = self.OLD_DESC
|
|
||||||
|
|
||||||
def test_return_appearance(self):
|
@patch("evennia.utils.gametime.gametime")
|
||||||
# get the appearance of a non-extended room for contrast purposes
|
def test_cmd_desc(self, mock_gametime):
|
||||||
old_desc = DefaultRoom.return_appearance(self.room1, self.char1)
|
"""Test new desc command"""
|
||||||
# the new appearance should be the old one, but with the desc switched
|
|
||||||
self.assertEqual(
|
mock_gametime.return_value = _get_timestamp("autumn", "afternoon")
|
||||||
old_desc.replace(self.OLD_DESC, self.SPRING_DESC),
|
|
||||||
self.room1.return_appearance(self.char1),
|
# view base desc
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"",
|
||||||
|
f"""
|
||||||
|
Room Room(#{self.room1.id}) Season: autumn. Time: afternoon. States: None
|
||||||
|
|
||||||
|
Room state (default) (active):
|
||||||
|
Base room description.
|
||||||
|
""".strip(),
|
||||||
)
|
)
|
||||||
self.assertEqual("spring", self.room1.ndb.last_season)
|
|
||||||
self.assertEqual("evening", self.room1.ndb.last_timeslot)
|
|
||||||
|
|
||||||
def test_return_detail(self):
|
# add spring desc
|
||||||
self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail"))
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"/spring Spring description.",
|
||||||
|
"The spring-description was set on Room",
|
||||||
|
)
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"/burning Burning description.",
|
||||||
|
"The burning-description was set on Room",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"",
|
||||||
|
"""
|
||||||
|
Room Room(#1) Season: autumn. Time: afternoon. States: None
|
||||||
|
|
||||||
|
Room state spring:
|
||||||
|
Spring description.
|
||||||
|
|
||||||
|
Room state burning:
|
||||||
|
Burning description.
|
||||||
|
|
||||||
|
Room state (default) (active):
|
||||||
|
Base room description.
|
||||||
|
""".strip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# remove a desc
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"/del/burning/spring",
|
||||||
|
(
|
||||||
|
"The burning-description was deleted, if it existed.|The spring-description was"
|
||||||
|
" deleted, if it existed"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# add autumn, which should be active
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"/autumn Autumn description.",
|
||||||
|
"The autumn-description was set on Room",
|
||||||
|
)
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
|
"",
|
||||||
|
"""
|
||||||
|
Room Room(#1) Season: autumn. Time: afternoon. States: None
|
||||||
|
|
||||||
|
Room state autumn (active):
|
||||||
|
Autumn description.
|
||||||
|
|
||||||
|
Room state (default):
|
||||||
|
Base room description.
|
||||||
|
""".strip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cmd_detail(self):
|
||||||
|
"""Test adding details"""
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDetail(),
|
||||||
|
"test=Test detail.",
|
||||||
|
"Set detail 'test': 'Test detail.'",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDetail(),
|
||||||
|
"",
|
||||||
|
"""
|
||||||
|
Details on Room:
|
||||||
|
test: Test detail.
|
||||||
|
""".strip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# remove a detail
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDetail(),
|
||||||
|
"/del test",
|
||||||
|
"Deleted detail 'test', if it existed.",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomDetail(),
|
||||||
|
"",
|
||||||
|
f"""
|
||||||
|
The room Room(#{self.room1.id}) doesn't have any details.
|
||||||
|
""".strip(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("evennia.utils.gametime.gametime")
|
||||||
|
def test_cmd_roomstate(self, mock_gametime):
|
||||||
|
"""
|
||||||
|
Test the roomstate command
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mock_gametime.return_value = _get_timestamp("autumn", "afternoon")
|
||||||
|
|
||||||
|
# show existing room states (season/time doesn't count)
|
||||||
|
|
||||||
|
self.assertEqual(self.room1.room_states, [])
|
||||||
|
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"",
|
||||||
|
f"Room states (not counting automatic time/season) on Room(#{self.room1.id}):\n None",
|
||||||
|
)
|
||||||
|
|
||||||
|
# add room states
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"burning",
|
||||||
|
"Added room state 'burning' to this room.",
|
||||||
|
)
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"windy",
|
||||||
|
"Added room state 'windy' to this room.",
|
||||||
|
)
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"",
|
||||||
|
(
|
||||||
|
f"Room states (not counting automatic time/season) on Room(#{self.room1.id}):\n "
|
||||||
|
"'burning' and 'windy'"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# toggle windy
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"windy",
|
||||||
|
"Cleared room state 'windy' from this room.",
|
||||||
|
)
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"",
|
||||||
|
(
|
||||||
|
f"Room states (not counting automatic time/season) on Room(#{self.room1.id}):\n "
|
||||||
|
"'burning'"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# add a autumn state and make sure we override it
|
||||||
|
self.room1.add_desc("Autumn description.", room_state="autumn")
|
||||||
|
self.room1.add_desc("Spring description.", room_state="spring")
|
||||||
|
|
||||||
|
self.assertEqual(self.room1.get_stateful_desc(), "Autumn description.")
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomState(),
|
||||||
|
"spring",
|
||||||
|
"Added room state 'spring' to this room.",
|
||||||
|
)
|
||||||
|
self.assertEqual(self.room1.get_stateful_desc(), "Spring description.")
|
||||||
|
|
||||||
|
@patch("evennia.utils.gametime.gametime")
|
||||||
|
def test_cmd_roomtime(self, mock_gametime):
|
||||||
|
"""
|
||||||
|
Test the time command
|
||||||
|
"""
|
||||||
|
|
||||||
|
mock_gametime.return_value = _get_timestamp("autumn", "afternoon")
|
||||||
|
|
||||||
|
self.call(
|
||||||
|
extended_room.CmdExtendedRoomGameTime(), "", "It's a autumn day, in the afternoon."
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("evennia.utils.gametime.gametime")
|
||||||
|
def test_cmd_look(self, mock_gametime):
|
||||||
|
"""
|
||||||
|
Test the look command.
|
||||||
|
"""
|
||||||
|
mock_gametime.return_value = _get_timestamp("autumn", "afternoon")
|
||||||
|
|
||||||
|
autumn_desc = (
|
||||||
|
"This is a nice autumnal forest."
|
||||||
|
"$state(morning,|_The morning sun is just rising)"
|
||||||
|
"$state(afternoon,|_The afternoon sun is shining through the trees)"
|
||||||
|
"$state(burning,|_and this place is on fire!)"
|
||||||
|
"$state(afternoon, .)"
|
||||||
|
"$state(flooded, and it's raining heavily!)"
|
||||||
|
)
|
||||||
|
self.room1.add_desc(autumn_desc, room_state="autumn")
|
||||||
|
|
||||||
def test_cmdextendedlook(self):
|
|
||||||
rid = self.room1.id
|
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomLook(),
|
extended_room.CmdExtendedRoomLook(),
|
||||||
"here",
|
"",
|
||||||
"Room(#{})\n{}".format(rid, self.SPRING_DESC),
|
f"Room(#{self.room1.id})\nThis is a nice autumnal forest.",
|
||||||
)
|
)
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomLook(),
|
extended_room.CmdExtendedRoomLook(),
|
||||||
"testdetail",
|
"",
|
||||||
"You look closely at {}.\n|{}".format("testdetail", self.DETAIL_DESC)
|
(
|
||||||
|
f"Room(#{self.room1.id})\nThis is a nice autumnal forest. The afternoon sun is"
|
||||||
|
" shining through the trees."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
self.room1.add_room_state("burning")
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomLook(), "nonexistent", "Could not find 'nonexistent'."
|
extended_room.CmdExtendedRoomLook(),
|
||||||
|
"",
|
||||||
|
(
|
||||||
|
f"Room(#{self.room1.id})\nThis is a nice autumnal forest. The afternoon sun is"
|
||||||
|
" shining through the trees and this place is on fire!"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cmdextendedlook_second_person(self):
|
|
||||||
# char2 is already in the same room.
|
|
||||||
# replace char2.msg with a Mock; this disables it and will catch what it is called with
|
|
||||||
self.char2.msg = Mock()
|
|
||||||
|
|
||||||
self.call(
|
|
||||||
extended_room.CmdExtendedRoomLook(),
|
|
||||||
"testdetail"
|
|
||||||
)
|
|
||||||
|
|
||||||
# check what char2 saw.
|
|
||||||
self.char2.msg.assert_called_with(text=('Char looks closely at testdetail.\n', {}), from_obj=self.char1)
|
|
||||||
|
|
||||||
def test_cmdsetdetail(self):
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "", "Details on Room")
|
|
||||||
self.call(
|
|
||||||
extended_room.CmdExtendedRoomDetail(),
|
|
||||||
"thingie = newdetail with spaces",
|
|
||||||
"Detail set 'thingie': 'newdetail with spaces'",
|
|
||||||
)
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "thingie", "Detail 'thingie' on Room:\n")
|
|
||||||
self.call(
|
|
||||||
extended_room.CmdExtendedRoomDetail(),
|
|
||||||
"/del thingie",
|
|
||||||
"Detail thingie deleted, if it existed.",
|
|
||||||
cmdstring="detail",
|
|
||||||
)
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "thingie", "Detail 'thingie' not found.")
|
|
||||||
|
|
||||||
# Test with aliases
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "", "Details on Room")
|
|
||||||
self.call(
|
|
||||||
extended_room.CmdExtendedRoomDetail(),
|
|
||||||
"thingie;other;stuff = newdetail with spaces",
|
|
||||||
"Detail set 'thingie;other;stuff': 'newdetail with spaces'",
|
|
||||||
)
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "thingie", "Detail 'thingie' on Room:\n")
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "other", "Detail 'other' on Room:\n")
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "stuff", "Detail 'stuff' on Room:\n")
|
|
||||||
self.call(
|
|
||||||
extended_room.CmdExtendedRoomDetail(),
|
|
||||||
"/del other;stuff",
|
|
||||||
"Detail other;stuff deleted, if it existed.",
|
|
||||||
)
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "other", "Detail 'other' not found.")
|
|
||||||
self.call(extended_room.CmdExtendedRoomDetail(), "stuff", "Detail 'stuff' not found.")
|
|
||||||
|
|
||||||
def test_cmdgametime(self):
|
|
||||||
self.call(extended_room.CmdExtendedRoomGameTime(), "", "It's a spring day, in the evening.")
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ This is the v1.0 develop version (for ref in doc building).
|
||||||
import time
|
import time
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
import evennia
|
||||||
import inflect
|
import inflect
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
import evennia
|
|
||||||
from evennia.commands import cmdset
|
from evennia.commands import cmdset
|
||||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||||
from evennia.objects.manager import ObjectManager
|
from evennia.objects.manager import ObjectManager
|
||||||
|
|
@ -72,7 +72,9 @@ class ObjectSessionHandler:
|
||||||
)
|
)
|
||||||
if any(sessid for sessid in self._sessid_cache if sessid not in evennia.SESSION_HANDLER):
|
if any(sessid for sessid in self._sessid_cache if sessid not in evennia.SESSION_HANDLER):
|
||||||
# cache is out of sync with sessionhandler! Only retain the ones in the handler.
|
# cache is out of sync with sessionhandler! Only retain the ones in the handler.
|
||||||
self._sessid_cache = [sessid for sessid in self._sessid_cache if sessid in evennia.SESSION_HANDLER]
|
self._sessid_cache = [
|
||||||
|
sessid for sessid in self._sessid_cache if sessid in evennia.SESSION_HANDLER
|
||||||
|
]
|
||||||
self.obj.db_sessid = ",".join(str(val) for val in self._sessid_cache)
|
self.obj.db_sessid = ",".join(str(val) for val in self._sessid_cache)
|
||||||
self.obj.save(update_fields=["db_sessid"])
|
self.obj.save(update_fields=["db_sessid"])
|
||||||
|
|
||||||
|
|
@ -100,7 +102,8 @@ class ObjectSessionHandler:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
sessions = [
|
sessions = [
|
||||||
evennia.SESSION_HANDLER[ssid] if ssid in evennia.SESSION_HANDLER else None for ssid in self._sessid_cache
|
evennia.SESSION_HANDLER[ssid] if ssid in evennia.SESSION_HANDLER else None
|
||||||
|
for ssid in self._sessid_cache
|
||||||
]
|
]
|
||||||
if None in sessions:
|
if None in sessions:
|
||||||
# this happens only if our cache has gone out of sync with the SessionHandler.
|
# this happens only if our cache has gone out of sync with the SessionHandler.
|
||||||
|
|
@ -761,7 +764,6 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
contents = [obj for obj in contents if obj not in exclude]
|
contents = [obj for obj in contents if obj not in exclude]
|
||||||
|
|
||||||
for receiver in contents:
|
for receiver in contents:
|
||||||
|
|
||||||
# actor-stance replacements
|
# actor-stance replacements
|
||||||
outmessage = _MSG_CONTENTS_PARSER.parse(
|
outmessage = _MSG_CONTENTS_PARSER.parse(
|
||||||
inmessage,
|
inmessage,
|
||||||
|
|
@ -1290,7 +1292,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
looker (Object): Object doing the looking.
|
looker (Object): Object doing the looking.
|
||||||
**kwargs: Arbitrary data for use when overriding.
|
**kwargs: Arbitrary data for use when overriding.
|
||||||
Returns:
|
Returns:
|
||||||
str: The desc display string..
|
str: The desc display string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.db.desc or "You see nothing special."
|
return self.db.desc or "You see nothing special."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue