More dungeon code

This commit is contained in:
Griatch 2022-07-24 13:01:01 +02:00
parent 07ff42b77c
commit 6a4b14fb83
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): def at_traverse(self, traversing_object, target_location, **kwargs):
""" """
@ -84,7 +89,9 @@ class EvAdventureDungeonExit(DefaultExit):
""" """
if target_location == self.location: 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) super().at_traverse(traversing_object, target_location, **kwargs)
@ -129,14 +136,16 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
) )
self.unvisited_exits.append(out_exit.id) 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 # TODO - determine what type of room to create here based on location and depth
room_typeclass = EvAdventureDungeonRoom room_typeclass = EvAdventureDungeonRoom
new_room = create.create_object( new_room = create.create_object(
room_typeclass, room_typeclass,
key="Dungeon room", key="Dungeon room",
tags=((self.key, "dungeon_room"),), attributes=(
attributes=(("xy_coord", coords, "dungeon_xygrid"),), ("xy_coords", coords, "dungeon_xygrid"),
("dungeon_orchestrator", self),
),
) )
return new_room return new_room
@ -170,7 +179,7 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
# depth achieved. # depth achieved.
depth = int(sqrt(new_x**2 + new_y**2)) 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 self.xy_grid[(new_x, new_y)] = new_room
@ -182,7 +191,14 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
aliases=_EXIT_ALIASES.get(back_exit_key, ()), aliases=_EXIT_ALIASES.get(back_exit_key, ()),
location=new_room, location=new_room,
destination=from_exit.location, 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 # figure out what other exits should be here, if any
@ -205,8 +221,8 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
direction = available_directions.pop(0) direction = available_directions.pop(0)
dx, dy = _EXIT_GRID_SHIFT[direction] dx, dy = _EXIT_GRID_SHIFT[direction]
target_coord = (new_x + dx, new_y + dy) target_coord = (new_x + dx, new_y + dy)
if target_coord not in self.xy_grid: if target_coord not in self.xy_grid and target_coord != (0, 0):
# no room there - make an exit to it # no room there (and not back to start room) - make an exit to it
self.create_out_exit(new_room, direction) self.create_out_exit(new_room, direction)
# we create this to avoid other rooms linking here, but don't create the # we create this to avoid other rooms linking here, but don't create the
# room yet # room yet
@ -215,6 +231,8 @@ class EvAdventureDungeonOrchestrator(DefaultScript):
self.highest_depth = max(self.highest_depth, depth) self.highest_depth = max(self.highest_depth, depth)
return new_room
# -------------------------------------------------- # --------------------------------------------------
# Start 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): def reset_exit(self):
""" """
Flush the exit, so next traversal creates a new dungeon branch. Flush the exit, so next traversal creates a new dungeon branch.
""" """
self.dungeon_orchestrator = None
self.destination = self.location self.destination = self.location
def at_traverse(self, traversing_object, target_location, **kwargs): 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. When traversing create a new orchestrator if one is not already assigned.
""" """
if target_location == self.location or self.dungeon_orchestrator is None: if target_location == self.location:
self.dungeon_orchestrator = create.create_script( # make a global orchestrator script for this dungeon branch
dungeon_orchestrator = create.create_script(
EvAdventureDungeonOrchestrator, EvAdventureDungeonOrchestrator,
key=f"dungeon_orchestrator_{self.key}_{datetime.utcnow()}", 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) 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): class EvAdventureRoom(DefaultRoom):
@ -37,3 +37,29 @@ class EvAdventureDungeonRoom(EvAdventureRoom):
allow_combat = True allow_combat = True
allow_death = 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: if exi.key == direction:
# by setting target to old-location we trigger the # by setting target to old-location we trigger the
# special behavior of this Exit type # special behavior of this Exit type
exi.at_traverse(self.character, old_location) exi.at_traverse(self.character, exi.destination)
break break
return self.character.location return self.character.location
@ -74,7 +74,7 @@ class TestDungeon(EvAdventureMixin, BaseEvenniaTest):
self.assertTrue(inherits_from(new_room_north, EvAdventureDungeonRoom)) self.assertTrue(inherits_from(new_room_north, EvAdventureDungeonRoom))
# check if Orchestrator was created # 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(bool(orchestrator))
self.assertTrue(orchestrator.key.startswith("dungeon_orchestrator_north_")) 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. transparently through the decorating TypeClass.
""" """
from collections import defaultdict from collections import defaultdict
from django.conf import settings from django.conf import settings
from django.db import models
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import validate_comma_separated_integer_list from django.core.validators import validate_comma_separated_integer_list
from django.db import models
from evennia.typeclasses.models import TypedObject
from evennia.objects.manager import ObjectDBManager from evennia.objects.manager import ObjectDBManager
from evennia.typeclasses.models import TypedObject
from evennia.utils import logger 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: class ContentsHandler: