196 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
| # Script for the roll-o-matic.
 | |
| # Idea courtesy Alex Schultz.
 | |
| #
 | |
| # Copyright 2007 Nicolas Weeger
 | |
| # Released as GPL
 | |
| #
 | |
| # This script makes its item move following tiles, in direction specified by a player
 | |
| 
 | |
| """
 | |
| Using this script:
 | |
| 
 | |
| 1. Create a transport object in the map editor. Ensure that its type is actually TRANSPORT.
 | |
| 
 | |
| 2. Add Python events that trigger this script. Transports require at least two events:
 | |
| 
 | |
|    - event_time, which periodically calls this script to move the transport along
 | |
| 
 | |
|    - event_apply and/or event_say, to start and stop the transport
 | |
| 
 | |
|    In practice, each event has fields:
 | |
| 
 | |
|     title Python
 | |
|     slaying /python/items/roll-o-matic.py
 | |
| 
 | |
|    Optionally, add:
 | |
| 
 | |
|     name ship_mode
 | |
| 
 | |
|    Which makes roll-o-matic work for ships.
 | |
| """
 | |
| 
 | |
| import Crossfire
 | |
| 
 | |
| key_direction = 'rom_dir'
 | |
| key_follow = 'rom_follow'
 | |
| 
 | |
| dir_x = [ 0, 0, 1, 1, 1, 0, -1, -1, -1 ]
 | |
| dir_y = [ 0, -1, -1, 0, 1, 1, 1, 0, -1 ]
 | |
| 
 | |
| ship_mode = False
 | |
| 
 | |
| def is_floor(ob):
 | |
|     if ship_mode:
 | |
|         return ob.ArchName in {'sea_route', 'sea_route_2', 'sea_route_3', 'sea_route_4'}
 | |
|     else:
 | |
|         return ob.Floor == 1
 | |
| 
 | |
| def find_floor(x, y):
 | |
|     """Find all objects satisfying is_floor() at the given location."""
 | |
|     obj = me.Map.ObjectAt(x, y)
 | |
|     while obj != None:
 | |
|         if is_floor(obj):
 | |
|             yield obj
 | |
|         obj = obj.Above
 | |
| 
 | |
| def find_player():
 | |
|     obj = me.Map.ObjectAt(me.X, me.Y)
 | |
|     while obj != None:
 | |
|         if obj.Type == Crossfire.Type.PLAYER:
 | |
|             return obj
 | |
|         obj = obj.Above
 | |
|     return None
 | |
| 
 | |
| def has_floor(x, y, name):
 | |
|     """Check whether one floor at the given location matches the given 'name'."""
 | |
|     for ob in find_floor(x, y):
 | |
|         if ob.Name == name:
 | |
|             return True
 | |
|     return False
 | |
| 
 | |
| def abs_dir(d):
 | |
|     while d < 1:
 | |
|         d = d + 8
 | |
|     while d > 8:
 | |
|         d = d - 8
 | |
|     return d
 | |
| 
 | |
| def stop():
 | |
|     me.WriteKey(key_direction, '', 1)
 | |
|     me.WriteKey(key_follow, '', 1)
 | |
|     me.Map.Print('The %s stops moving.'%me.Name)
 | |
| 
 | |
| def try_move(check_directions, want_dir):
 | |
|     floor = me.ReadKey(key_follow)
 | |
|     x = me.X
 | |
|     y = me.Y
 | |
|     done = False
 | |
|     for check in check_directions:
 | |
|         d = abs_dir(want_dir + check)
 | |
|         if has_floor(x + dir_x[d], y + dir_y[d], floor):
 | |
|             # Next time, move in the direction we last moved in.
 | |
|             # This heuristic helps follow the roads better.
 | |
|             me.WriteKey(key_direction, str(d))
 | |
|             if me.Move(d) == 0:
 | |
|                 continue
 | |
| 
 | |
|             if pl != None:
 | |
|                 pl.Move(d)
 | |
|             done = True
 | |
|             break
 | |
| 
 | |
|     if not done:
 | |
|         stop()
 | |
| 
 | |
| def do_shipwreck(me):
 | |
|     stop()
 | |
| 
 | |
| def handle_move():
 | |
|     want_dir = me.ReadKey(key_direction)
 | |
|     floor = me.ReadKey(key_follow)
 | |
|     if want_dir == '' or floor == '':
 | |
|         return
 | |
| #   me.Map.Print('roll')
 | |
|     pl = find_player()
 | |
|     want_dir = int(want_dir)
 | |
|     if ship_mode:
 | |
|         # For ship routes, check all candidate directions except the opposite-direction one.
 | |
|         check_directions = [0, 1, -1, 2, -2, 3, -3]
 | |
| 
 | |
|         curr_tile = me.Map.ObjectAt(me.X, me.Y)
 | |
|         while curr_tile != None:
 | |
|             if curr_tile.Name == 'shipwreck':
 | |
|                 do_shipwreck(me)
 | |
|                 return
 | |
|             curr_tile = curr_tile.Above
 | |
|     else:
 | |
|         # For roads, only check the routes that make progress in the right direction.
 | |
|         check_directions = [0, 1, -1]
 | |
|     try_move(check_directions, want_dir)
 | |
| 
 | |
| def start_move(want_dir):
 | |
|     floors = list(find_floor(me.X, me.Y))
 | |
|     if len(floors) < 1:
 | |
|         return
 | |
|     floor = floors[0].Name
 | |
| 
 | |
|     if me.ReadKey(key_direction) == '':
 | |
|         me.Map.Print('The %s starts moving!' % me.Name)
 | |
| 
 | |
|     me.WriteKey(key_direction, str(want_dir), 1)
 | |
|     me.WriteKey(key_follow, floor, 1)
 | |
| 
 | |
| def handle_say():
 | |
|     msg = Crossfire.WhatIsMessage()
 | |
|     if msg == 'stop':
 | |
|         if me.ReadKey(key_direction) != '':
 | |
|             stop()
 | |
|         return
 | |
| 
 | |
|     want_dir = -1
 | |
| 
 | |
|     for d in Crossfire.DirectionName.keys():
 | |
|         if msg == Crossfire.DirectionName[d].lower():
 | |
|             want_dir = d
 | |
|             break
 | |
| 
 | |
|     if want_dir == -1:
 | |
|         return
 | |
| 
 | |
|     start_move(want_dir)
 | |
| 
 | |
| def handle_apply():
 | |
|     if pl.Transport != None:
 | |
|         # Stop if already moving.
 | |
|         stop()
 | |
|     else:
 | |
|         # When applied, we don't know initial direction. Try to find one.
 | |
|         start_move(0)
 | |
|         try_move([0, 1, 2, 3, 4, 5, 6, 7], 0)
 | |
|     Crossfire.SetReturnValue(0)
 | |
| 
 | |
| def do_handle():
 | |
|     if me.Map == None:
 | |
|         return
 | |
| 
 | |
|     if "ship_mode" in params:
 | |
|         global ship_mode
 | |
|         ship_mode = True
 | |
| 
 | |
|     Crossfire.SetReturnValue(1)
 | |
| 
 | |
|     if evt.Subtype == Crossfire.EventType.SAY:
 | |
|         handle_say()
 | |
|     elif evt.Subtype == Crossfire.EventType.APPLY:
 | |
|         handle_apply()
 | |
|     elif evt.Subtype == Crossfire.EventType.TIME:
 | |
|         handle_move()
 | |
| 
 | |
| 
 | |
| evt = Crossfire.WhatIsEvent()
 | |
| me = Crossfire.WhoAmI()
 | |
| pl = Crossfire.WhoIsActivator()
 | |
| params = Crossfire.ScriptParameters()
 | |
| 
 | |
| do_handle()
 |