Animated doors
You can watch such doors in action: http://www.duffy1.franken.de/doors.avi
There is a very well done resource for doors that did almost all I need out of the box: http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=11651
I just added two things (see attached doors.cc and doors.h):
- make the door selectable by simple add ItemObjectType to its type mask
- add a console function to report its current state as there was only a toggle function
The selecting only works if you apply the item select patch to rpgTSCtrl.cc as described in the section PickupItems.
In the door.cs included in the resource I added this wrapper function as I had some problems getting a proper return code using TGEEval directly from the object (the object returns an int and as long as it's not going through Torque script you won't get the int as a string but get the result of a previous TGEEval):
// wrapper function as TGEEval always return strings
function doorOpenState(%door) {
return %door.getOpenState();
}
The above adds what you need to show, animate and select a door. The logic i.e. the checks for a key, keeping track of a door's state and so on is done in the python code.
Beware: This is not final. There is quite some debug code in it and it adds doors when you click on it instead of fetching everything from the DB. I share this code to get feedback and suggestions but not really to give you a final version. This may change as I work on it. You've been warned.
Let's start with the class holding the state of a door: mud/world/door.py:
# door definition
from mud.common.persistent import Persistent
from sqlobject import *
from collections import defaultdict
class Door(Persistent):
doorId = IntCol()
locked = IntCol(default = 0)
lockable = IntCol(default = 0)
lockId = IntCol(default = 0)
open = IntCol(default = 0)
def _init(self,*args,**kw):
Persistent._init(self, *args, **kw)
(TODO: Mention it has to be added to Genesis.py and how)
The select function (again only with the pickup patch) will call PySelect when you click on a door. This function calls select in mud/simulation/simmind.py in the class SimMind. I changed it like that to make it notice a door:
def select(self,srcId,tgtId,charIndex,doubleClick,modifier_shift):
if self.selectCredit.get(srcId,None):
return
try:
src = self.simLookup[srcId]
except KeyError:
print "simmind.select: source not found"
return
try:
tgt = self.simLookup[tgtId]
except KeyError:
try:
tgt = self.simItemLookup[tgtId]
#print "simmind: select() : %s"%tgtId
self.perspective.callRemote('SimAvatar','select',srcId,tgtId,charIndex,doubleClick,modifier_shift)
return
except KeyError:
try:
clazz = TGEEval("%d.getClassName();"%tgtId)
if clazz == "Door":
#TGEEval("%d.toggle();"%tgtId)
doorIsOpen = int(TGEEval("doorOpenState(%d);"%tgtId))
#print "door state=%d"%doorIsOpen
self.perspective.callRemote('SimAvatar','selectDoor',srcId,tgtId,charIndex,doubleClick,modifier_shift,doorIsOpen)
except:
print_exc()
print "simmind.select: target ID %d not found"%tgtId
return
return
self.selectCredit[srcId] = True
d = self.perspective.callRemote('SimAvatar','select',srcId,tgtId,charIndex,doubleClick,modifier_shift)
d.addCallback(self.grantSelectCredit,srcId)
d.addErrback(self.grantSelectCredit,srcId)
def remote_toggleDoor(self,doorId):
try:
TGEEval("%d.toggle();"%doorId)
except:
print_exc()
If you don't need any checks server-side you can just uncomment the TGEEval with the toggle function. I did that to test the resource and the changes I did to the door.cc.
As I wanted to check for locked doors and key items I forward the request to the zone code through SimAvatar. mud/world/simavatar.py:
def perspective_selectDoor(self,srcId,tgtId,charIndex,doubleClick,modifier_shift,isOpen):
self.zone.selectDoor(self.simLookup[srcId],tgtId,charIndex,doubleClick,modifier_shift,isOpen)
def toggleDoor(self,door,sound,loc):
self.mind.callRemote("toggleDoor",door.doorId)
self.mind.callRemote("playSound",sound,loc)
In the ZoneInstance definition I added a lookup structure for doors:
self.doors = {}
(TODO: load the doors when the ZoneInstance is created)
mod/world/zone.py:
def selectDoor(self,srcSimObject,doorId,charindex,doubleClick,modifier_shift,isOpen):
mob = self.mobLookup[srcSimObject]
player = mob.player
if self.doors.has_key(doorId):
door = self.doors[doorId]
else:
door = Door(doorId=doorId)
# just for testing
if doorId == 6317:
door.locked = True
door.lockable = True
door.lockId = 6317
self.doors[doorId] = door
# door closed and locked: need a key to unlock
if not isOpen and door.locked and (modifier_shift or doubleClick):
key = player.getKeyFor(door.lockId)
if key:
door.locked = False
player.sendGameText(RPG_MSG_GAME_GREEN,r'Door is unlocked.\n')
else:
player.sendGameText(RPG_MSG_GAME_RED,r'You have no key for this door.\n')
return
# door is unlocked and should be locked: need a key
if door.lockable and (modifier_shift or doubleClick):
if isOpen:
self.simAvatar.toggleDoor(door)
key = player.getKeyFor(door.lockId)
if key:
door.locked = True
player.sendGameText(RPG_MSG_GAME_GREEN,r'Door is locked.\n')
else:
player.sendGameText(RPG_MSG_GAME_RED,r'You have no key for this door.\n')
return
if not door.locked:
loc = player.simObject.position
if isOpen:
sound = "doorClose.ogg"
else:
sound = "doorcreak.ogg"
self.simAvatar.toggleDoor(door, sound, loc)
else:
player.sendGameText(RPG_MSG_GAME_RED,r'Door is locked.\n')
The key checking code is in mud/world/player.py:
def getKeyFor(self, lockId):
found = None
for item in self.curChar.items:
if item.lockId == lockId:
found = item
break
return found
This needs an additional attribute in the item: lockId. This integer defines the ID of the lock this item can open and close. Currently I just use the door object ID for that.
(TODO: show how to add the lockId to ItemProto, ItemInstance and Item)
This is an example of a key item: put that somewhere in <game root>/genesis/item/
item = DBItemProto(name="Schluessel") item.itemType = ["COMMON","COMPONENT"] item.desc = "Ein kleiner Metallschluessel." item.model = "door/keysilver1.dts" item.bitmap = "STUFF/24" item.stackMax = 1 item.worthCopper = 30 item.lockId = 6317
The only important things to mention is the setting of the attribute lockId and make sure it's not stackable (stackMax = 1).
The door definition in the mission as well as its datablock is defined in the door object resource: an example:
new Door(Door1) {
canSaveDynamicFields = "1";
position = "-41.9096 -439.304 139.676";
rotation = "1 0 0 0";
scale = "1.5 1 1.25";
dnStartTime = "-1";
dnEndTime = "-1";
dataBlock = "Door1";
};
