maps/python/monsters/dragon.py

115 lines
4.3 KiB
Python

"""
dragon.py -- talking dragon that flies you places
Kevin Zheng 2024
This replaces the old dragon hangers, where every location that wanted a dragon
hanger needed to copy/paste a template map.
Usage: Put an event_say and event_apply handler that calls this script on a
dragon_exit.
"""
import math
import re
import Crossfire
world_map_path_matcher = r"/world/world_(\d\d\d)_(\d\d\d)";
activator = Crossfire.WhoIsActivator()
event = Crossfire.WhatIsEvent()
whoami = Crossfire.WhoAmI()
price_per_worldmap_tile = 5*50 # price per world map tile traveled, in money units (silver)
max_fare = 150*50 # maximum fare per trip, in money units (silver)
# All the fun places we can go! Tuple of map path and X, Y coordinate.
destinations = {
'Port Joseph': ('/world/world_101_114', 16, 39),
'Red Town': ('/pup_land/rainbow/station', 7, 3),
'Wolfsburg': ('/world/world_128_109', 35, 13),
'Brest': ('/world/world_107_123', 32, 30),
'Navar': ('/world/world_121_116', 37, 46),
'Darcap': ('/world/world_116_102', 29, 37),
'Stoneville': ('/world/world_103_127', 5, 15),
'Scorn': ('/world/world_105_115', 5, 37),
'Lake Country': ('/world/world_109_126', 16, 20),
'Santo Dominion': ('/world/world_102_108', 17, 12),
# 'Nurnberg': ('/pup_land/nurnberg/city', 25, 15), # needs a passport check
}
def search_destination(name):
if name in destinations:
return destinations[name]
else:
return None
def world_map_coord(path):
"""Try to extract the coordinates from a world map path."""
groups = re.match(world_map_path_matcher, path)
if groups is not None:
coords = groups.group(1, 2)
cx, cy = int(coords[0]), int(coords[1])
return cx, cy
return None
def fare(dest):
curr_coord = world_map_coord(whoami.Map.Path)
dest_coord = world_map_coord(dest[0])
if curr_coord is None or dest_coord is None:
return max_fare
else:
return min(max_fare, dist_fare(curr_coord, dest_coord))
def dist_fare(start, end):
dist = math.hypot(end[0] - start[0], end[1] - start[1])
return math.ceil(dist * price_per_worldmap_tile)
# State for each player. Dict (player_name: str, state) where state is
# (destination: str, price: int).
state = Crossfire.GetPrivateDictionary()
def handle_say():
msg = Crossfire.WhatIsMessage()
text = msg.split()
if text[0] == "what":
whoami.Say("Dragon Express can whisk you to one of %d locations for a small fee. Travel faster today!" % len(destinations))
return
elif text[0] == "where":
whoami.Say("We have %d exciting destinations: %s. Where would you like to go?" % (len(destinations), ", ".join(destinations.keys())))
return
elif text[0] == "yes" and activator.Name in state:
dest_name = state[activator.Name][0]
price = state[activator.Name][1]
dest = destinations[dest_name]
m = Crossfire.ReadyMap(dest[0])
if not m:
whoami.Say("Oops, it looks like the landing site there is not clear. Let's try to go somewhere else.")
elif activator.PayAmount(price):
activator.Message("You pay the %s %s" % (whoami.Name, Crossfire.CostStringFromValue(price)))
activator.Message("You hop on the %s and it takes off. You enjoy a pleasant ride above the clouds before arriving at %s." % (whoami.Name, dest_name))
activator.Teleport(m, dest[1], dest[2])
else:
whoami.Say("It doesn't look like you can afford this trip. Please come back when you can.")
del(state[activator.Name])
return
dest = search_destination(msg)
if dest is not None:
dest_name = msg
price = fare(dest)
whoami.Say("Alright, let's go to %s. That will cost %s. Is that okay?" % (msg, Crossfire.CostStringFromValue(price)))
Crossfire.AddReply("yes", "Okay, let's go.")
Crossfire.AddReply("no", "No thanks.")
state[activator.Name] = (dest_name, price)
else:
whoami.Say("Welcome to Dragon Express. Where can I take you today?")
Crossfire.AddReply("what", "What is Dragon Express?")
Crossfire.AddReply("where", "Where can you take me?")
Crossfire.SetReturnValue(1)
if event.Subtype == Crossfire.EventType.SAY:
handle_say()
else:
whoami.Say("Hey, you can't get on without paying!")