176 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
| # Script for the lockable door and key.
 | ||
| # Ideas courtesy Yann Chachkoff.
 | ||
| #
 | ||
| # Copyright 2012 Nicolas Weeger
 | ||
| # Released as GPL
 | ||
| #
 | ||
| # Lockable doors have 2 components:
 | ||
| # - doors, which have the 'lockable 1' attribute
 | ||
| # - locking keys, which have a apply handler pointing to this script
 | ||
| #
 | ||
| # Rules are as follow:
 | ||
| # - the 'slaying' field is used to match doors and keys
 | ||
| # - locking keys start blank (empty slaying)
 | ||
| # - applying a blank key to an unlocked door locks the door and assigns the door to the key,
 | ||
| #  the key gets a '(used)' appended to its name
 | ||
| # - applying a valid key to a locked/unlocked door unlocks/locks
 | ||
| # - locked doors must be alive, unlocked mustn't be
 | ||
| # - the 'other_arch' field is used to link locked and unlocked variants
 | ||
| # - the following fields are changed when un/locking takes place: name, move_block,
 | ||
| #  move_allow, face. This allows customization of the doors (prevent walk but not fly, for instance)
 | ||
| #
 | ||
| # Right now, the following archetypes use this system:
 | ||
| # lockable_vdoor, lockable_vdoor_locked, lockable_hdoor, lockable_hdoor_locked, locking_key
 | ||
| #
 | ||
| # Feel free to use those as a base.
 | ||
| #
 | ||
| # Note: using a DOOR (type 23) instead of a LOCKED_DOOR (type 20) doesn't seem to
 | ||
| # work, since any key can remove the door - not the desired behaviour.
 | ||
| #
 | ||
| # If the lockable door has the correct 'trigger' hook (case for the previous archetypes),
 | ||
| # then the door can be picked, depending on the player's lockpicking level.
 | ||
| # Experience is awarded for the first successful pick only. Failures decrease the
 | ||
| # chance to succeed, and increase the chance of leaving traces.
 | ||
| 
 | ||
| import Crossfire
 | ||
| import random
 | ||
| 
 | ||
| def get_door(me, direction):
 | ||
|     '''Find the first item in the specified direction'''
 | ||
|     map = me.Map
 | ||
|     x = me.X
 | ||
|     y = me.Y
 | ||
| 
 | ||
|     if direction==2:
 | ||
|         ob = map.ObjectAt(x+1, y-1)
 | ||
|     elif direction==3:
 | ||
|         ob = map.ObjectAt(x+1, y)
 | ||
|     elif direction==4:
 | ||
|         ob = map.ObjectAt(x+1, y+1)
 | ||
|     elif direction==5:
 | ||
|         ob = map.ObjectAt(x, y+1)
 | ||
|     elif direction==6:
 | ||
|         ob = map.ObjectAt(x-1, y+1)
 | ||
|     elif direction==7:
 | ||
|         ob = map.ObjectAt(x-1, y)
 | ||
|     elif direction==8:
 | ||
|         ob = map.ObjectAt(x-1, y-1)
 | ||
|     else:
 | ||
|         ob = map.ObjectAt(x, y-1)
 | ||
|     return ob
 | ||
| 
 | ||
| def give_properties(who, lock):
 | ||
|   '''Give properties from either the archetype or the other archetype.'''
 | ||
|   if lock == who.Archetype.Clone.Alive:
 | ||
|     arch = who.Archetype
 | ||
|   else:
 | ||
|     arch = who.OtherArchetype
 | ||
|   who.MoveType = arch.Clone.MoveType
 | ||
|   who.MoveBlock = arch.Clone.MoveBlock
 | ||
|   who.MoveAllow = arch.Clone.MoveAllow
 | ||
|   who.Alive = arch.Clone.Alive
 | ||
|   who.Name = arch.Clone.Name
 | ||
|   who.Face = arch.Clone.Face
 | ||
| 
 | ||
| def check_picked(door, who):
 | ||
|   '''Check if the lock was picked, and inform the player if this is the case.'''
 | ||
|   if door.ReadKey('door_picked') == '1':
 | ||
|     who.Write('You notice some suspicious traces on the lock.')
 | ||
|     door.WriteKey('door_picked', '')
 | ||
| 
 | ||
| def handle_key():
 | ||
|   '''Handle applying a locking key.'''
 | ||
|   Crossfire.SetReturnValue(1)
 | ||
| 
 | ||
|   key = Crossfire.WhoAmI()
 | ||
|   who = Crossfire.WhoIsActivator()
 | ||
| 
 | ||
|   door = get_door(who, who.Facing)
 | ||
| 
 | ||
|   while door != None:
 | ||
|     if door.ReadKey('lockable') == '1':
 | ||
|       break
 | ||
|     door = door.Above
 | ||
| 
 | ||
|   if door == None:
 | ||
|     who.Write('There is no lock here')
 | ||
|   else:
 | ||
|     if door.Alive:
 | ||
|       # door is locked, check if key matches
 | ||
|       if door.Slaying != key.Slaying:
 | ||
|         who.Write("You can't use this %s on this %s"%(key.Name, door.Name))
 | ||
|       else:
 | ||
|         who.Write('You unlock the %s'%(door.Name))
 | ||
|         give_properties(door, 0)
 | ||
|         check_picked(door, who)
 | ||
|     else:
 | ||
|       # door is unlocked, a key can lock if blank or matching
 | ||
|       if key.Slaying == '' or key.Slaying == None or key.Slaying == door.Slaying:
 | ||
|         if key.Slaying == '' or key.Slaying == None:
 | ||
|           key.Slaying = door.Slaying
 | ||
|           key.Name = key.Name + " (used)"
 | ||
|         who.Write('You lock the %s'%(door.Name))
 | ||
|         give_properties(door, 1)
 | ||
|         check_picked(door, who)
 | ||
|       else:
 | ||
|         who.Write("You can't use this %s on this %s"%(key.Name, door.Name))
 | ||
| 
 | ||
| def get_attempts(door, who):
 | ||
|   '''Get how many attempts to pick a lock a player did'''
 | ||
|   s = door.ReadKey('attempts_' + who.Name)
 | ||
|   if s == '':
 | ||
|     return 0
 | ||
|   return int(s)
 | ||
| 
 | ||
| def get_success_chance(door, who, level):
 | ||
|   '''Return the chance of successfully picking the lock for the player'''
 | ||
|   if door.ReadKey('was_picked_' + who.Name) != '':
 | ||
|     return 100
 | ||
|   attempts = min(get_attempts(door, who), 5)
 | ||
|   diff = level - door.Level - attempts
 | ||
|   #who.Write('chance: %d'%(max(25, min(90, 50 + diff * 5))))
 | ||
|   return max(25, min(90, 50 + diff * 5))
 | ||
| 
 | ||
| def get_exp(door, who):
 | ||
|   '''Return the experience for lockpicking the door'''
 | ||
|   if door.ReadKey('was_picked_' + who.Name) != '':
 | ||
|     return 0
 | ||
|   attempts = get_attempts(door, who)
 | ||
|   #who.Write('exp: %d'%(round((door.Exp * (100. - min(100., attempts * 20.))) / 100.)))
 | ||
|   return round((door.Exp * (100. - min(100., attempts * 20.))) / 100.)
 | ||
| 
 | ||
| def handle_lockpick():
 | ||
|   '''Handle lockpicking a door.'''
 | ||
|   Crossfire.SetReturnValue(1)
 | ||
| 
 | ||
|   door = Crossfire.WhoAmI()
 | ||
|   who = Crossfire.WhoIsActivator()
 | ||
| 
 | ||
|   if who == None:
 | ||
|     return
 | ||
| 
 | ||
|   if door.Alive == 0:
 | ||
|     who.Write("This %s is unlocked."%(door.Name))
 | ||
|     return
 | ||
| 
 | ||
|   chance = get_success_chance(door, who, Crossfire.WhoIsOther().Level)
 | ||
|   # chance to leave traces on the lock
 | ||
|   if random.randint(0, 100) < 100 - chance:
 | ||
|     door.WriteKey('door_picked', '1', 1)
 | ||
| 
 | ||
|   # attempt to unlock
 | ||
|   if random.randint(0, 100) < chance:
 | ||
|     who.Write('You successfully pick the lock.')
 | ||
|     give_properties(door, 0)
 | ||
|     who.AddExp(get_exp(door, who), Crossfire.WhoIsOther().Name)
 | ||
|     door.WriteKey('was_picked_' + who.Name, '1', 1)
 | ||
|   else:
 | ||
|     who.Write('You fail to pick the lock.')
 | ||
|     door.WriteKey('attempts_' + who.Name, str(get_attempts(door, who) + 1), 1)
 | ||
| 
 | ||
| event = Crossfire.WhatIsEvent()
 | ||
| if event.Subtype == Crossfire.EventType.APPLY:
 | ||
|   handle_key()
 | ||
| elif event.Subtype == Crossfire.EventType.TRIGGER:
 | ||
|   handle_lockpick()
 |