maps/python/items/roll-o-matic.py

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()