More dungeon code

This commit is contained in:
Griatch 2022-07-24 13:01:01 +02:00
parent 3d27034102
commit 5b43a4bc1f
4 changed files with 64 additions and 24 deletions

View file

@ -75,7 +75,12 @@ class EvAdventureDungeonExit(DefaultExit):
"""
dungeon_orchestrator = AttributeProperty(None, autocreate=False)
def at_object_creation(self):
"""
We want to block progressing forward unless the room is clear.
"""
self.locks.add("traverse:not tag(not_clear, dungeon_room)")
def at_traverse(self, traversing_object, target_location, **kwargs):
"""
@ -84,7 +89,9 @@ class EvAdventureDungeonExit(DefaultExit):
"""
if target_location == self.location:
self.destination = target_location = self.dungeon_orchestrator.new_room(self)
self.destination = target_location = self.location.db.dungeon_orchestrator.new_room(
self
)
super().at_traverse(traversing_object, target_location, **kwargs)
@ -129,14 +136,16 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
)
self.unvisited_exits.append(out_exit.id)
def _generate_room(self, depth, coords):
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",
tags=((self.key, "dungeon_room"),),
attributes=(("xy_coord", coords, "dungeon_xygrid"),),
attributes=(
("xy_coords", coords, "dungeon_xygrid"),
("dungeon_orchestrator", self),
),
)
return new_room
@ -170,7 +179,7 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
# depth achieved.
depth = int(sqrt(new_x**2 + new_y**2))
new_room = self._generate_room(depth, (new_x, new_y))
new_room = self._generate_dungeon_room(depth, (new_x, new_y))
self.xy_grid[(new_x, new_y)] = new_room
@ -182,7 +191,14 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
aliases=_EXIT_ALIASES.get(back_exit_key, ()),
location=new_room,
destination=from_exit.location,
attributes=(("desc", "A dark passage."),),
attributes=(
(
"desc",
"A dark passage.",
),
),
# we default to allowing back-tracking (also used for fleeing)
locks=("traverse: true()",),
)
# figure out what other exits should be here, if any
@ -205,8 +221,8 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
direction = available_directions.pop(0)
dx, dy = _EXIT_GRID_SHIFT[direction]
target_coord = (new_x + dx, new_y + dy)
if target_coord not in self.xy_grid:
# no room there - make an exit to it
if target_coord not in self.xy_grid and target_coord != (0, 0):
# no room there (and not back to start room) - make an exit to it
self.create_out_exit(new_room, direction)
# we create this to avoid other rooms linking here, but don't create the
# room yet
@ -215,6 +231,8 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
self.highest_depth = max(self.highest_depth, depth)
return new_room
# --------------------------------------------------
# Start room
@ -232,16 +250,11 @@ class EvAdventureStartRoomExit(DefaultExit):
"""
# we store the orchestrator like this since we don't want to actually manipulate it,
# but only use the reference to know when to create a new room
dungeon_orchestrator = AttributeProperty(None, autocreate=False)
def reset_exit(self):
"""
Flush the exit, so next traversal creates a new dungeon branch.
"""
self.dungeon_orchestrator = None
self.destination = self.location
def at_traverse(self, traversing_object, target_location, **kwargs):
@ -249,12 +262,13 @@ class EvAdventureStartRoomExit(DefaultExit):
When traversing create a new orchestrator if one is not already assigned.
"""
if target_location == self.location or self.dungeon_orchestrator is None:
self.dungeon_orchestrator = create.create_script(
if target_location == self.location:
# make a global orchestrator script for this dungeon branch
dungeon_orchestrator = create.create_script(
EvAdventureDungeonOrchestrator,
key=f"dungeon_orchestrator_{self.key}_{datetime.utcnow()}",
)
self.destination = target_location = self.dungeon_orchestrator.new_room(self)
self.destination = target_location = dungeon_orchestrator.new_room(self)
super().at_traverse(traversing_object, target_location, **kwargs)

View file

@ -5,7 +5,7 @@ EvAdventure rooms.
"""
from evennia import DefaultRoom
from evennia import AttributeProperty, DefaultRoom, TagProperty
class EvAdventureRoom(DefaultRoom):
@ -37,3 +37,29 @@ class EvAdventureDungeonRoom(EvAdventureRoom):
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 ""

View file

@ -56,7 +56,7 @@ class TestDungeon(EvAdventureMixin, BaseEvenniaTest):
if exi.key == direction:
# by setting target to old-location we trigger the
# special behavior of this Exit type
exi.at_traverse(self.character, old_location)
exi.at_traverse(self.character, exi.destination)
break
return self.character.location
@ -74,7 +74,7 @@ class TestDungeon(EvAdventureMixin, BaseEvenniaTest):
self.assertTrue(inherits_from(new_room_north, EvAdventureDungeonRoom))
# check if Orchestrator was created
orchestrator = self.start_north.scripts.get(dungeon.EvAdventureDungeonOrchestrator)
orchestrator = new_room_north.db.dungeon_orchestrator
self.assertTrue(bool(orchestrator))
self.assertTrue(orchestrator.key.startswith("dungeon_orchestrator_north_"))

View file

@ -14,15 +14,15 @@ the database object. Like everything else, they can be accessed
transparently through the decorating TypeClass.
"""
from collections import defaultdict
from django.conf import settings
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import validate_comma_separated_integer_list
from evennia.typeclasses.models import TypedObject
from django.db import models
from evennia.objects.manager import ObjectDBManager
from evennia.typeclasses.models import TypedObject
from evennia.utils import logger
from evennia.utils.utils import make_iter, dbref, lazy_property
from evennia.utils.utils import dbref, lazy_property, make_iter
class ContentsHandler: