mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Fix dungeon behavior
This commit is contained in:
parent
6a4b14fb83
commit
a471f0fd86
5 changed files with 314 additions and 74 deletions
|
|
@ -20,16 +20,12 @@ You can also build/rebuild individiaul #CODE blocks in the `batchcode/interactiv
|
|||
# this is loaded at the top of every #CODE block
|
||||
|
||||
from evennia import DefaultExit, create_object, search_object
|
||||
from evennia.contrib.tutorials import evadventure
|
||||
from evennia.contrib.tutorials.evadventure import npcs
|
||||
from evennia.contrib.tutorials.evadventure.combat_turnbased import EvAdventureCombatHandler
|
||||
from evennia.contrib.tutorials.evadventure.objects import (
|
||||
EvAdventureConsumable,
|
||||
EvAdventureObject,
|
||||
EvAdventureObjectFiller,
|
||||
EvAdventureRunestone,
|
||||
EvAdventureWeapon,
|
||||
from evennia.contrib.tutorials.evadventure.dungeon import (
|
||||
EvAdventureDungeonStartRoom,
|
||||
EvAdventureDungeonStartRoomExit,
|
||||
)
|
||||
from evennia.contrib.tutorials.evadventure.objects import EvAdventureWeapon
|
||||
from evennia.contrib.tutorials.evadventure.rooms import EvAdventurePvPRoom, EvAdventureRoom
|
||||
|
||||
# CODE
|
||||
|
|
@ -66,7 +62,7 @@ create_object(
|
|||
# with a static enemy
|
||||
|
||||
combat_room = create_object(EvAdventurePvPRoom, key="Combat Arena", aliases=("evtechdemo#01",))
|
||||
# link to/back to hub
|
||||
# link to/back to/from hub
|
||||
hub_room = search_object("evtechdemo#00")[0]
|
||||
create_object(
|
||||
DefaultExit, key="combat test", aliases=("combat",), location=hub_room, destination=combat_room
|
||||
|
|
@ -84,3 +80,64 @@ combat_room_enemy = create_object(
|
|||
)
|
||||
weapon_stick = create_object(EvAdventureWeapon, key="stick", attributes=(("damage_roll", "1d2"),))
|
||||
combat_room_enemy.weapon = weapon_stick
|
||||
|
||||
|
||||
# CODE
|
||||
|
||||
# A dungeon start room for testing the dynamic dungeon generation.
|
||||
|
||||
dungeon_start_room = create_object(
|
||||
EvAdventureDungeonStartRoom,
|
||||
key="Dungeon start room",
|
||||
aliases=("evtechdemo#02",),
|
||||
attributes=(("desc", "A central room, with dark exits leading to mysterious fates."),),
|
||||
)
|
||||
# link to/back to/from hub
|
||||
hub_room = search_object("evtechdemo#00")[0]
|
||||
create_object(
|
||||
DefaultExit,
|
||||
key="dungeon test",
|
||||
aliases=("dungeon",),
|
||||
location=hub_room,
|
||||
destination=dungeon_start_room,
|
||||
)
|
||||
create_object(
|
||||
DefaultExit,
|
||||
key="Back to Hub",
|
||||
aliases=("back", "hub"),
|
||||
location=dungeon_start_room,
|
||||
destination=hub_room,
|
||||
)
|
||||
|
||||
# add special exits out of the dungeon start room.
|
||||
# These must have one of the 8 cardinal directions
|
||||
# we point these exits back to the same location, which
|
||||
# is what the system will use to trigger generating a new room
|
||||
create_object(
|
||||
EvAdventureDungeonStartRoomExit,
|
||||
key="north",
|
||||
aliases=("n",),
|
||||
location=dungeon_start_room,
|
||||
destination=dungeon_start_room,
|
||||
)
|
||||
create_object(
|
||||
EvAdventureDungeonStartRoomExit,
|
||||
key="east",
|
||||
aliases=("e",),
|
||||
location=dungeon_start_room,
|
||||
destination=dungeon_start_room,
|
||||
)
|
||||
create_object(
|
||||
EvAdventureDungeonStartRoomExit,
|
||||
key="south",
|
||||
aliases=("s",),
|
||||
location=dungeon_start_room,
|
||||
destination=dungeon_start_room,
|
||||
)
|
||||
create_object(
|
||||
EvAdventureDungeonStartRoomExit,
|
||||
key="west",
|
||||
aliases=("w",),
|
||||
location=dungeon_start_room,
|
||||
destination=dungeon_start_room,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,14 @@ decided to go in that direction. Each room is tagged with the specific 'instance
|
|||
id of that particular branch of dungon. When no characters remain in the branch,
|
||||
the branch is deleted.
|
||||
|
||||
Each room in the dungeon starts with a Tag `not_clear`; while this is set, all exits out
|
||||
of the room (not the one they came from) is blocked. When whatever problem the room
|
||||
offers has been solved (such as a puzzle or a battle), the tag is removed and the player(s)
|
||||
can choose which exit to leave through.
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from math import sqrt
|
||||
from random import randint, random, shuffle
|
||||
|
||||
|
|
@ -25,12 +30,21 @@ from evennia.typeclasses.attributes import AttributeProperty
|
|||
from evennia.utils import create, search
|
||||
from evennia.utils.utils import inherits_from
|
||||
|
||||
from .rooms import EvAdventureDungeonRoom
|
||||
from .rooms import EvAdventureRoom
|
||||
|
||||
# aliases for cardinal directions
|
||||
_AVAILABLE_DIRECTIONS = [
|
||||
"north",
|
||||
"east",
|
||||
"south",
|
||||
"west",
|
||||
# commented out to make the dungeon simpler to navigate
|
||||
# "northeast", "southeast", "southwest", "northwest",
|
||||
]
|
||||
|
||||
_EXIT_ALIASES = {
|
||||
"north": ("n",),
|
||||
"east": ("w",),
|
||||
"east": ("e",),
|
||||
"south": ("s",),
|
||||
"west": ("w",),
|
||||
"northeast": ("ne",),
|
||||
|
|
@ -64,10 +78,53 @@ _EXIT_GRID_SHIFT = {
|
|||
|
||||
|
||||
# --------------------------------------------------
|
||||
# Dungeon orchestrator and rooms
|
||||
# Dungeon orchestrator and room / exits
|
||||
# --------------------------------------------------
|
||||
|
||||
|
||||
class EvAdventureDungeonRoom(EvAdventureRoom):
|
||||
"""
|
||||
Dangerous dungeon room.
|
||||
|
||||
"""
|
||||
|
||||
allow_combat = True
|
||||
allow_death = True
|
||||
|
||||
# dungeon generation attributes; set when room is created
|
||||
back_exit = AttributeProperty(None, autocreate=False)
|
||||
dungeon_orchestrator = AttributeProperty(None, autocreate=False)
|
||||
xy_coords = AttributeProperty(None, autocreate=False)
|
||||
|
||||
@property
|
||||
def is_room_clear(self):
|
||||
return not bool(self.tags.get("not_clear", category="dungeon_room"))
|
||||
|
||||
def clear_room(self):
|
||||
self.tags.remove("not_clear", category="dungeon_room")
|
||||
|
||||
def at_object_creation(self):
|
||||
"""
|
||||
Set the `not_clear` tag on the room. This is removed when the room is
|
||||
'cleared', whatever that means for each room.
|
||||
|
||||
We put this here rather than in the room-creation code so we can override
|
||||
easier (for example we may want an empty room which auto-clears).
|
||||
|
||||
"""
|
||||
self.tags.add("not_clear", category="dungeon_room")
|
||||
|
||||
def get_display_footer(self, looker, **kwargs):
|
||||
"""
|
||||
Show if the room is 'cleared' or not as part of its description.
|
||||
|
||||
"""
|
||||
if self.is_room_clear:
|
||||
return ""
|
||||
else:
|
||||
return "|rThe path forwards is blocked!|n"
|
||||
|
||||
|
||||
class EvAdventureDungeonExit(DefaultExit):
|
||||
"""
|
||||
Dungeon exit. This will not create the target room until it's traversed.
|
||||
|
|
@ -80,7 +137,7 @@ class EvAdventureDungeonExit(DefaultExit):
|
|||
We want to block progressing forward unless the room is clear.
|
||||
|
||||
"""
|
||||
self.locks.add("traverse:not tag(not_clear, dungeon_room)")
|
||||
self.locks.add("traverse:not objloctag(not_clear, dungeon_room)")
|
||||
|
||||
def at_traverse(self, traversing_object, target_location, **kwargs):
|
||||
"""
|
||||
|
|
@ -92,8 +149,60 @@ class EvAdventureDungeonExit(DefaultExit):
|
|||
self.destination = target_location = self.location.db.dungeon_orchestrator.new_room(
|
||||
self
|
||||
)
|
||||
if self.id in self.location.dungeon_orchestrator.unvisited_exits:
|
||||
self.location.dungeon_orchestrator.unvisited_exits.remove(self.id)
|
||||
|
||||
super().at_traverse(traversing_object, target_location, **kwargs)
|
||||
|
||||
def at_failed_traverse(self, traversing_object, **kwargs):
|
||||
"""
|
||||
Called when failing to traverse.
|
||||
|
||||
"""
|
||||
traversing_object.msg("You can't get through this way yet!")
|
||||
|
||||
|
||||
def room_generator(dungeon_orchestrator, depth, coords):
|
||||
"""
|
||||
Plugin room generator
|
||||
|
||||
This default one returns the same empty room.
|
||||
|
||||
Args:
|
||||
dungeon_orchestrator (EvAdventureDungeonOrchestrator): The current orchestrator.
|
||||
depth (int): The 'depth' of the dungeon (radial distance from start room) this
|
||||
new room will be placed at.
|
||||
coords (tuple): The `(x,y)` coords that the new room will be created at.
|
||||
|
||||
"""
|
||||
room_typeclass = EvAdventureDungeonRoom
|
||||
|
||||
# simple map of depth to name and desc of room
|
||||
name_depth_map = {
|
||||
1: ("Water-logged passage", "This earth-walled passage is dripping of water."),
|
||||
2: ("Passage with roots", "Roots are pushing through the earth walls."),
|
||||
3: ("Hardened clay passage", "The walls of this passage is of hardened clay."),
|
||||
4: ("Clay with stones", "This passage has clay with pieces of stone embedded."),
|
||||
5: ("Stone passage", "Walls are crumbling stone, with roots passing through it."),
|
||||
6: ("Stone hallway", "Walls are cut from rough stone."),
|
||||
7: ("Stone rooms", "A stone room, built from crude and heavy blocks."),
|
||||
8: ("Granite hall", "The walls are of well-fitted granite blocks."),
|
||||
9: ("Marble passages", "The walls are blank and shiny marble."),
|
||||
10: ("Furnished rooms", "The marble walls have tapestries and furnishings."),
|
||||
}
|
||||
key, desc = name_depth_map.get(depth, ("Dark rooms", "There is very dark here."))
|
||||
|
||||
new_room = create.create_object(
|
||||
room_typeclass,
|
||||
key=key,
|
||||
attributes=(
|
||||
("desc", desc),
|
||||
("xy_coords", coords),
|
||||
("dungeon_orchestrator", dungeon_orchestrator),
|
||||
),
|
||||
)
|
||||
return new_room
|
||||
|
||||
|
||||
class EvAdventureDungeonOrchestrator(DefaultScript):
|
||||
"""
|
||||
|
|
@ -104,15 +213,22 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
|
|||
"""
|
||||
|
||||
# this determines how branching the dungeon will be
|
||||
max_unexplored_exits = 5
|
||||
max_new_exits_per_room = 3
|
||||
max_unexplored_exits = 2
|
||||
max_new_exits_per_room = 2
|
||||
|
||||
rooms = AttributeProperty(list())
|
||||
unvisited_exits = AttributeProperty(list())
|
||||
highest_depth = AttributeProperty(0)
|
||||
|
||||
last_updated = AttributeProperty(datetime.utcnow())
|
||||
|
||||
# the room-generator function; copied from the same-name value on the start-room when the
|
||||
# orchestrator is first created
|
||||
room_generator = AttributeProperty(None, autocreate=False)
|
||||
|
||||
# (x,y): room coordinates used up by orchestrator
|
||||
xy_grid = AttributeProperty(dict())
|
||||
start_room = AttributeProperty(None, autocreate=False)
|
||||
|
||||
def register_exit_traversed(self, exit):
|
||||
"""
|
||||
|
|
@ -136,27 +252,29 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
|
|||
)
|
||||
self.unvisited_exits.append(out_exit.id)
|
||||
|
||||
def _generate_dungeon_room(self, depth, coords):
|
||||
# TODO - determine what type of room to create here based on location and depth
|
||||
room_typeclass = EvAdventureDungeonRoom
|
||||
new_room = create.create_object(
|
||||
room_typeclass,
|
||||
key="Dungeon room",
|
||||
attributes=(
|
||||
("xy_coords", coords, "dungeon_xygrid"),
|
||||
("dungeon_orchestrator", self),
|
||||
),
|
||||
)
|
||||
return new_room
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
Clean up the entire dungeon along with the orchestrator.
|
||||
|
||||
"""
|
||||
# first secure all characters in this branch back to the start room
|
||||
characters = search.search_object_by_tag(self.key, category="dungeon_character")
|
||||
start_room = self.start_room
|
||||
for character in characters:
|
||||
start_room.msg_contents(
|
||||
"Suddenly someone stumbles out of a dark exit, covered in dust!"
|
||||
)
|
||||
character.location = start_room
|
||||
character.msg(
|
||||
"|rAfter a long time of silence, the room suddenly rumbles and then collapses! "
|
||||
"All turns dark ...|n\n\nThen you realize you are back where you started."
|
||||
)
|
||||
character.tags.remove(self.key, category="dungeon_character")
|
||||
# next delete all rooms in the dungeon (this will also delete exits)
|
||||
rooms = search.search_object_by_tag(self.key, category="dungeon_room")
|
||||
for room in rooms:
|
||||
room.delete()
|
||||
# finally delete the orchestrator itself
|
||||
super().delete()
|
||||
|
||||
def new_room(self, from_exit):
|
||||
|
|
@ -167,11 +285,12 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
|
|||
from_exit (Exit): The exit leading to this new room.
|
||||
|
||||
"""
|
||||
self.last_updated = datetime.utcnow()
|
||||
# figure out coordinate of old room and figure out what coord the
|
||||
# new one would get
|
||||
source_location = from_exit.location
|
||||
x, y = source_location.attributes.get("xy_coord", category="dungeon_xygrid", default=(0, 0))
|
||||
dx, dy = _EXIT_GRID_SHIFT.get(from_exit.key, (1, 0))
|
||||
x, y = source_location.attributes.get("xy_coords", default=(0, 0))
|
||||
dx, dy = _EXIT_GRID_SHIFT.get(from_exit.key, (0, 1))
|
||||
new_x, new_y = (x + dx, y + dy)
|
||||
|
||||
# the dungeon's depth acts as a measure of the current difficulty level. This is the radial
|
||||
|
|
@ -179,7 +298,7 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
|
|||
# depth achieved.
|
||||
depth = int(sqrt(new_x**2 + new_y**2))
|
||||
|
||||
new_room = self._generate_dungeon_room(depth, (new_x, new_y))
|
||||
new_room = self.room_generator(self, depth, (new_x, new_y))
|
||||
|
||||
self.xy_grid[(new_x, new_y)] = new_room
|
||||
|
||||
|
|
@ -210,7 +329,7 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
|
|||
if n_exits > 1:
|
||||
n_exits = randint(1, n_exits)
|
||||
available_directions = [
|
||||
direction for direction in _EXIT_ALIASES if direction != back_exit_key
|
||||
direction for direction in _AVAILABLE_DIRECTIONS if direction != back_exit_key
|
||||
]
|
||||
# randomize order of exits
|
||||
shuffle(available_directions)
|
||||
|
|
@ -239,7 +358,7 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
|
|||
# --------------------------------------------------
|
||||
|
||||
|
||||
class EvAdventureStartRoomExit(DefaultExit):
|
||||
class EvAdventureDungeonStartRoomExit(DefaultExit):
|
||||
"""
|
||||
Traversing this exit will either lead to an existing dungeon branch or create
|
||||
a new one.
|
||||
|
|
@ -264,11 +383,18 @@ class EvAdventureStartRoomExit(DefaultExit):
|
|||
"""
|
||||
if target_location == self.location:
|
||||
# make a global orchestrator script for this dungeon branch
|
||||
self.location.room_generator
|
||||
dungeon_orchestrator = create.create_script(
|
||||
EvAdventureDungeonOrchestrator,
|
||||
key=f"dungeon_orchestrator_{self.key}_{datetime.utcnow()}",
|
||||
attributes=(
|
||||
("start_room", self.location),
|
||||
("room_generator", self.location.room_generator),
|
||||
),
|
||||
)
|
||||
self.destination = target_location = dungeon_orchestrator.new_room(self)
|
||||
# make sure to tag character when entering so we can find them again later
|
||||
traversing_object.tags.add(dungeon_orchestrator.key, category="dungeon_character")
|
||||
|
||||
super().at_traverse(traversing_object, target_location, **kwargs)
|
||||
|
||||
|
|
@ -280,7 +406,7 @@ class EvAdventureStartRoomResetter(DefaultScript):
|
|||
"""
|
||||
|
||||
def at_script_creation(self):
|
||||
self.key = "evadventure_startroom_resetter"
|
||||
self.key = "evadventure_dungeon_startroom_resetter"
|
||||
|
||||
def at_repeat(self):
|
||||
"""
|
||||
|
|
@ -289,21 +415,63 @@ class EvAdventureStartRoomResetter(DefaultScript):
|
|||
"""
|
||||
room = self.obj
|
||||
for exi in room.exits:
|
||||
if inherits_from(exi, EvAdventureStartRoomExit) and random() < 0.5:
|
||||
if inherits_from(exi, EvAdventureDungeonStartRoomExit) and random() < 0.5:
|
||||
exi.reset_exit()
|
||||
|
||||
|
||||
class EvAdventureDungeonRoomStart(EvAdventureDungeonRoom):
|
||||
class EvAdventureDungeonBranchDeleter(DefaultScript):
|
||||
"""
|
||||
Exits leading out of the start room, (except one leading outside) will lead to a different
|
||||
dungeon-branch, and after a certain time, the given exit will instead spawn a new branch. This
|
||||
room is responsible for cycling these exits regularly.
|
||||
Cleanup script. After some time a dungeon branch will 'collapse', forcing all players in it
|
||||
back to the start room.
|
||||
|
||||
"""
|
||||
|
||||
# set at creation time when the start room is created
|
||||
branch_max_life = AttributeProperty(0, autocreate=False)
|
||||
|
||||
def at_script_creation(self):
|
||||
self.key = "evadventure_dungeon_branch_deleter"
|
||||
|
||||
def at_repeat(self):
|
||||
"""
|
||||
Go through all dungeon-orchestrators and find which ones are too old.
|
||||
|
||||
"""
|
||||
max_dt = timedelta(seconds=self.branch_max_life)
|
||||
max_allowed_date = datetime.utcnow() - max_dt
|
||||
|
||||
for orchestrator in EvAdventureDungeonOrchestrator.objects.all():
|
||||
if orchestrator.last_updated < max_allowed_date:
|
||||
# orchestrator is too old; tell it to clean up and delete itself
|
||||
orchestrator.delete()
|
||||
|
||||
|
||||
class EvAdventureDungeonStartRoom(EvAdventureDungeonRoom):
|
||||
"""
|
||||
The start room is the only permanent part of the dungeon. Exits leading from this room (except
|
||||
one leading back outside) each create/links to a separate dungeon branch/instance.
|
||||
|
||||
- A script will reset each exit every 5 mins; after that time, entering the exit will spawn
|
||||
a new branch-instance instead of leading to the one before.
|
||||
- Another script will check age of branch instance every hour; once an instance has been
|
||||
inactive for a week, it will 'collapse', forcing everyone inside back to the start room.
|
||||
|
||||
The actual exits should be created in the build script.
|
||||
|
||||
"""
|
||||
|
||||
recycle_time = 5 * 60 # seconds
|
||||
recycle_time = 60 * 5 # 5 mins
|
||||
branch_check_time = 60 * 60 # one hour
|
||||
branch_max_life = 60 * 60 * 24 * 7 # 1 week
|
||||
|
||||
# allow for a custom room_generator function
|
||||
room_generator = AttributeProperty(room_generator, autocreate=False)
|
||||
|
||||
def get_display_footer(self, looker, **kwargs):
|
||||
return (
|
||||
"|yYou sense that if you want to team up, "
|
||||
"you must all pick the same path from here ... or you'll quickly get separated.|n"
|
||||
)
|
||||
|
||||
def at_object_creation(self):
|
||||
# want to set the script interval on creation time, so we use create_script with obj=self
|
||||
|
|
@ -311,3 +479,17 @@ class EvAdventureDungeonRoomStart(EvAdventureDungeonRoom):
|
|||
create.create_script(
|
||||
EvAdventureStartRoomResetter, obj=self, interval=self.recycle_time, autostart=True
|
||||
)
|
||||
create.create_script(
|
||||
EvAdventureDungeonBranchDeleter,
|
||||
obj=self,
|
||||
interval=self.branch_check_time,
|
||||
autostart=True,
|
||||
attributes=(("branch_max_life", self.branch_max_life),),
|
||||
)
|
||||
|
||||
def at_object_receive(self, obj, source_location, **kwargs):
|
||||
"""
|
||||
Make sure to clean the dungeon branch-tag from characters when leaving a dungeon branch.
|
||||
|
||||
"""
|
||||
obj.tags.remove(category="dungeon_character")
|
||||
|
|
|
|||
|
|
@ -28,38 +28,9 @@ class EvAdventurePvPRoom(DefaultRoom):
|
|||
allow_combat = True
|
||||
allow_pvp = True
|
||||
|
||||
|
||||
class EvAdventureDungeonRoom(EvAdventureRoom):
|
||||
"""
|
||||
Dangerous dungeon room.
|
||||
|
||||
"""
|
||||
|
||||
allow_combat = True
|
||||
allow_death = True
|
||||
|
||||
# dungeon generation attributes; set when room is created
|
||||
back_exit = AttributeProperty(None, autocreate=False)
|
||||
dungeon_orchestrator = AttributeProperty(None, autocreate=False)
|
||||
xy_coords = AttributeProperty(None, autocreate=False)
|
||||
|
||||
def at_object_creation(self):
|
||||
"""
|
||||
Set the `not_clear` tag on the room. This is removed when the room is
|
||||
'cleared', whatever that means for each room.
|
||||
|
||||
We put this here rather than in the room-creation code so we can override
|
||||
easier (for example we may want an empty room which auto-clears).
|
||||
|
||||
"""
|
||||
self.tags.add("not_clear")
|
||||
|
||||
def get_display_footer(self, looker, **kwargs):
|
||||
"""
|
||||
Show if the room is 'cleared' or not as part of its description.
|
||||
|
||||
"""
|
||||
if self.tags.get("not_clear", "dungeon_room"):
|
||||
# this tag is cleared when the room is resolved, whatever that means.
|
||||
return "|rThe path forwards is blocked!|n"
|
||||
return ""
|
||||
return "|yNon-lethal PvP combat is allowed here!|n"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class TestDungeon(EvAdventureMixin, BaseEvenniaTest):
|
|||
|
||||
"""
|
||||
super().setUp()
|
||||
droomclass = dungeon.EvAdventureDungeonRoomStart
|
||||
droomclass = dungeon.EvAdventureDungeonStartRoom
|
||||
droomclass.recycle_time = 0 # disable the tick
|
||||
|
||||
self.start_room = create_object(droomclass, key="bottom of well")
|
||||
|
|
@ -36,14 +36,14 @@ class TestDungeon(EvAdventureMixin, BaseEvenniaTest):
|
|||
self.start_room.scripts.get("evadventure_startroom_resetter")[0].interval, -1
|
||||
)
|
||||
self.start_north = create_object(
|
||||
dungeon.EvAdventureStartRoomExit,
|
||||
dungeon.EvAdventureDungeonStartRoomExit,
|
||||
key="north",
|
||||
location=self.start_room,
|
||||
destination=self.start_room,
|
||||
)
|
||||
self.start_north
|
||||
self.start_south = create_object(
|
||||
dungeon.EvAdventureStartRoomExit,
|
||||
dungeon.EvAdventureDungeonStartRoomExit,
|
||||
key="south",
|
||||
location=self.start_room,
|
||||
destination=self.start_room,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ a certain object type.
|
|||
|
||||
|
||||
from ast import literal_eval
|
||||
|
||||
from django.conf import settings
|
||||
from evennia.utils import utils
|
||||
|
||||
|
|
@ -466,6 +467,7 @@ def tag(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
category.
|
||||
If accessing_obj has the ".obj" property (such as is the case for
|
||||
a command), then accessing_obj.obj is used instead.
|
||||
|
||||
"""
|
||||
if hasattr(accessing_obj, "obj"):
|
||||
accessing_obj = accessing_obj.obj
|
||||
|
|
@ -474,6 +476,34 @@ def tag(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
return bool(accessing_obj.tags.get(tagkey, category=category))
|
||||
|
||||
|
||||
def objtag(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
objtag(tagkey)
|
||||
objtag(tagkey, category):
|
||||
|
||||
Only true if `accessed_obj` has the given tag and optional category.
|
||||
|
||||
"""
|
||||
return tag(accessed_obj, None, *args, **kwargs)
|
||||
|
||||
|
||||
def objloctag(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
objloctag(tagkey)
|
||||
objloctag(tagkey, category):
|
||||
|
||||
Only true if `accessed_obj.location` has the given tag and optional category.
|
||||
If obj has no location, this lockfunc fails.
|
||||
|
||||
"""
|
||||
try:
|
||||
return tag(accessed_obj.location, None, *args, **kwargs)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
|
||||
def is_ooc(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue