Python Extensions

From ManorManual

Revision as of 21:18, 2 September 2007 by Scotsman (Talk | contribs)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search

Contents

[edit] Python Manual

This manual assumes you are already familiar with Python (www.python.org). If you aren’t already familiar with Python, no fear. If you have experience with other programming languages, it’s fairly easy to pick up the basics of Python programming from looking at code samples.

Each script that is loaded is placed in a separate Python module. The cyborg script is placed in a module named “cyborg”. Spot scripts are placed in modules named “spot_##” where ## is the ID of the spot. Spot scripts are also loaded in order, so later spots can import spot modules that have already been loaded.

In addition, all scripts that wish to make calls to The Manor client will need to import the manor module.

[edit] Entry Points

If these routines are present in your script, they will be called as detailed. If your script does not contain at least one of these entry points, it won’t be called by The Manor client software.

mnr_signon
cyborg only
Called when a connection is established to a Manor server.
mnr_enter(userID)Called when a user enters the room.
mnr_leave(userID)Called when a user leaves the room.
mnr_spothit(spotID)
spot script only
This is only called for the spot script of the spot the user has clicked on.
mnr_spothitex(spotID)
spot script only
This is called when the user clicks on any spot in the room.
chatText mnr_inchat(userID, chatText)Called when chat text is received by the client, before it is displayed to the user.
UserIDWhich user initiated the message.
chatTextThe text of the message

Your script should do any processing desired on the text, and then return the chatText.

Your script may modify the chatText if desired, or clear the text.
chatText mnr_outchat(chatText)Called when chat text is sent by the user, before the text is actually sent to the server.
mnr_statechange(spotID, stateID)Called when the state of a spot changes.
handled mnr_render(gworld, layer) spot script onlyCalled during room rendering operations.
gworldThe room display gworld. This is an opaque gworld.
layerWhat layer of the room is being rendered
0Nothing has been rendered
1Room background has been rendered
2Spots have been rendered
3All rendering completed
handledreturning non zero stops any further rendering
handled mnr_keydown(char, key)Called when the user presses a key
charCharacter code of the key pressed
keyKey code of the key pressed.
NOTE: Key codes differ between Macintosh and Windows systems
handledreturning non zero stops any further processing for the key down
handled mnr_mousedown(x, y)
spot script only

Called when the user presses the mouse button

x, yMouse position within the room
handledreturning non zero stops any further processing for the mouse down

[edit] Manor Calls

[edit] Output functions

say(chatText)Send chatText
whisper([userID, (userID...)], chatText)Send chatText to the users indicated by userID list as a whisper
logstr(text)Places text in the log
logErrStr(text)Places text in the log as an error (red text)
localMsg(msg)Displays a system message balloon. Only the user sees this message, it is not transmitted to the server.
statusMsg (msg, logit)Set a status message
dirtyRect(rect)Marks a section of the room as needing to be rendered.

[edit] User functions

isRegistered()Returns 1 if user is registered
myRegHash()Returns the users reg hash, if registered
userID myIDReturns this users ID
name getUserName(userID)Returns the name of the user indicated by userID
count getRoomUserCountReturns the number of users in the room
x,y getUserPos(userID)Returns the position of the user indicated by userID
userID getIdxRoomUser(userIdx)Returns the id for an indexed user. UserIdx is 0 <= userIdx < getRoomUserCount

[edit] Spot functions

hitSpot(spotID)Simulates user clicking a spot. Any smart spot actions will be taken after calling any spothit scripts
setSpotState(spotID, stateID, global)Sets the current state of a spot
spotIDThe spot whose state will be set.
stateIDstate the spot is to be set to
globalif 0, spot is set locally, if 1 it is set globally

NOTE: If the spot contains state images, the new state image will not be displayed until the next time the client cycles.

stateID getSpotState(spotID)Returns the current state of a spot
title getSpotTitle(spotID)Returns the title of a spot
count getSpotCountReturns the number of spots in the room
spotID getIdxSpot(spotIdx)Return the ID of the indexed spot. spotIdx is 0 <= spotIdx < getSpotCount
roomID getSpotDest(spotID)Returns spots destination room ID
rect getSpotBounds(spotID)Returns spots bounding rectangle
inSpot(spotID)Return true if user is within the bounds of spotID.
isLocked(spotID)Returns true if spotID is locked. If spotID does not exist or is not closable will return false.
lock(spotID)Locks a spot. If spotID does not exist or is not closable an exception will be set.
unlock(spotID)UnLocks a spot. If spotID does not exist or is not closable an exception will be set.

[edit] Prop functions

setProp(propList)Sets the current worn props to those in proplist.
propID getTopPropID()Returns the id of the top most prop
putProp(propID, x, y)Place a prop in the room
propIThe prop to place in the room.
xHorizontal position for the prop
yVertical position for the prop
clearProps()Remove all the worn props, i.e. naked
dropProp(x, y)Drop the top prop in the the room at the location indicated by x and y
donProp(propID)Put the prop on as the top most prop
doffProp()Remove the top most prop
numProps() Returns the number of currently worn props
removeProp(propID) Removes propID from currently worn props
faceColor(r, g, b)Change the face color to the rgb value in r, g, and b
face(faceNum)Change the face to faceNum. FaceNum must be 0 – 15.

[edit] Navigation functions

gotoRoom(roomNum)Change the current room to roomNum
roomName getRoomName()Returns the name of the current room
roomNum getRoomNumber()Returns the number of the current room
siteName getSiteName()Returns the name of the current Manor
openConnection(url)Open a url. If the url is a manor url, a connection will be opened to the manor server. Otherwise the appropriate application will be launched for the URL.
setPos(x, y)Set the users position
x,y getMouseRmPos()Returns the current mouse positions clipped to the bounds of the room.
isDown stillDown()Returns non-zero if the mouse button is still down.

[edit] Misc functions

boolean isKeyDown(keyCode)Returns 1 is the key is down. Note keycodes are different on Mac vs Windows systems.
clientType getClientType()Returns the client type. 0 = Mac OS9, 1 = Windows, 2 = Mac OSX
mills milliseconds()Returns the number of milliseconds (1000ths of a second) since system start
ticks tickcount()Returns the number of ticks (60ths of a second) since system start
setTimer(ticks, function, params)Sets a timer function.

ticks The tick time the timer should be triggered. function The function to be called when the timer triggers The function is of the form: func(param) params Parameters to pass to the trigger function. If you are calling this from a class, this must be “self”.

playSound(sound)Plays a sound file from the cache. If the sound file is not already in the cache an exception is raised and the call returns. "sound" can either be just the name of the file to play (i.e. "sound.wav") or it can be a full URL (i.e. "http://www.madwolfsw.com/sound.wav). In most cases you will just want to use the name of the file to use your sites sounds.
fetchSound(sound, function, params)Checks a sound is in the cache. If the sound is not in the cache, or is out of date, downloads the sound to the cache. "sound" can either be just the name of the file to play (i.e. "sound.wav") or it can be a full URL (i.e. "http://www.madwolfsw.com/sound.wav). In most cases you will just want to use the name of the file to use your sites sounds.

function The function to be called when the sound is downloaded. The function is of the form: func(param) params Parameters to pass to the completion function. If you are calling this from a class, this must be “self”.

NOTE: If this is the first time the sound file has been used in a session it can take several seconds for the download to complete even if the sound is already cached. This is normal as it is the time it takes to verify the sound file is up to date.

fetchSoundObject(sound, function, params)Checks a sound is in the cache. If the sound is not in the cache, or is out of date, downloads the sound to the cache. "sound" can either be just the name of the file to load (i.e. "sound.wav") or it can be a full URL (i.e. "http://www.madwolfsw.com/sound.wav). In most cases you will just want to use the name of the file to use your sites sounds. Once the sound is available it will be passed as an object to your completion function.

function The function to be called when the sound is downloaded. The function is of the form: func(sound, params) params Parameters to pass to the completion function. If you are calling this from a class, this must be “self”.

NOTE: If this is the first time the sound file has been used in a session it can take several seconds for the download to complete even if the sound is already cached. This is normal as it is the time it takes to verify the sound file is up to date.

playSoundObject(soundObject, interupt, loop)Plays a sound object. Sound objects should be kept small as they are kept in memory. They are intended for use of sound effects in games.

soundObject The sound object returned by fetchSoundObject interupt if not zero any other sound currently playing will be stopped loop if not zero this sound will be played until stopped

stopSounds()Stops any sounds that may be currently playing
fetchImageObject(image, function, params, forceDownload)Import a jpg or png file to an image object. This first checks the cache for the image file. If the file is not already cached, or is out of date, the image is downloaded.
broadcastData(msgID, isGlobal, data)Broadcasts data to all the users. Global broadcasting requires being authenticated to a group with global message permissions.

msgID 15 character string identifying the type of message isGlobal if not zero, message is to be broadcast server wide data any object to be sent

sendData(msgID, [userID, (userID...)], data)Sends data to specific users..

msgID 15 character string identifying the type of message userID list of users to send data to data any object to be sent

regDataReceive(msgID, func, param)Registers a function to receive message types

msgID 15 character string identifying the type of message func Function to be called when msgID is received. The function is of the form: func(userID, data, param) param parameter to pass to receive function

fetchTextObject(filename, function, params)</td
post(cgi, postData, func, param)</td

[edit] Drawing

[edit] Object Modules

[edit] rect

The rect object defines rectangles used by other graphics objects.

myRect = rect(left, top, right, bottom)

FunctionBehavior
offset(x, y)Offset the rectangle by x, y pixels
inset(x,y)Inset the rectangle by x, y pixels
t, l, b, r = boundsreturns the bounds of the rectangle


UnaryBehavior
|Union
&Intersection
=Copy
==Equal to
!=Not equal
>Area greater than
<Area less than

[edit] shape

shapes are predefined line drawings that can be manipulated as a single object. They also draw slightly faster than drawing a comparable image with the gworld move and line functions.

myShape = shape()

FunctionBehavior
move(x, y)Add a move point to the shape. x and y are relative to shape center.
line(x, y)Add a line point to the shape. x and y are relative to shape center.
draw(gworld, x, y)Draw the shape
rotate(a)Rotate the image to angle in radians. Zero is straight up, angles proceeding clockwise. Rotation is always from the original shape definition.
scale(percent)Scale the shape
rect = bounds()Return the bounding rectangle

[edit] gworld

gworlds are offscreen graphics worlds. This is the workhorse object for graphics operations.

myGworld = gworld(boundRect

FunctionBehavior
beginMultiDrawPrepare the gworld for multiple draw calls
endMultiDrawEnd multiple draw sequence
foreColor(a, r, g, b)Set the foreground color to alpha, red, green, blue. Alpha values are ignored when working with opaque gworlds.
drawString(string)Draw a string at the current pen position
stringWidth(string)Returns the pixel width of the string
setFont(string)Set the font. String is a comma delimited string of font names.
setFontSize(size)Set the font size in points
fontHeight()Returns the pixel height of the font
fontAscent()Returns the ascent height of the font
penSize(h, v)Set the horizontal and vertical size of the pen
moveTo(x, y)Change the pen position
lineTo(x, y)Draw a line from the current pen position
frameRect(rect)Draw a frame around a rectangle
fillRect(rect)Fill a rectangle with the foreground color
frameOval(rect)Draw an oval within the bounds of a rectangle
fillOval(rect)Fill an oval within the bounds of a rectangle
copyimage(imageObject, srcRect, destRect)copy srcRect from imageObject to destRect in gworld
copygworld(srcGWorld, srcRect, destRect)Copy srcRect from srcGWorld to destRect in gworld

[edit] SAMPLE CODE

[edit] Bouncing Text

This was the initial test script for very early gworld functionality

import manor

global xpos, ypos, xdir, ydir

xpos = 105
ypos = 112
xdir = 3
ydir = 3

def mover(param):
    global xpos, ypos, xdir, ydir

    manor.dirtyRect(rect(xpos - 50, ypos + 3, xpos + 50, ypos - 13))
    xpos += xdir
    ypos += ydir
    if (xpos > 510):
        xdir = -3
    if (xpos < 4):
        xdir = 3
    if (ypos > 380):
        ydir = -3
    if (ypos < 4):
        ydir = 3
    manor.dirtyRect(rect(xpos - 50, ypos + 3, xpos + 50, ypos - 13))
    manor.setTimer(manor.tickcount() +1, mover, 0)
    
def mnr_render(gworld, layer):
    global xpos, ypos, xdir, ydir

    handled = 0
    if layer == 1:
        gworld.moveTo(xpos, ypos)
        gworld.foreColor(0xFF, 0xFF, 0xFF, 0xFF)
        gworld.drawString("test")
        handled = 1
    return handled
    
def mnr_enter(userID):
    if userID == manor.myID() or manor.myID() == 0:
        manor.setTimer(manor.tickcount() + 1, mover, 0)

[edit] Bouncing Ball

A variation of the bouncing text to display a bouncing ball in response to the user saying "ball" or "no ball" to stop. Assumes a room of 750x384

import manor

global xpos, ypos, xdir, ydir
global ball_on

xpos = 105
ypos = 112
xdir = 1
ydir = 1
ball_on = 0

def mover(param):
    global xpos, ypos, xdir, ydir, ball_on

    manor.dirtyRect(rect(xpos - 5, ypos - 5, xpos + 6, ypos + 6))
    xpos += xdir
    ypos += ydir
    if (xpos > 740):
        xdir = -1
    if (xpos < 5):
        xdir = 1
    if (ypos > 379):
        ydir = -1
    if (ypos < 5):
        ydir = 1
    manor.dirtyRect(rect(xpos - 5, ypos - 5, xpos + 6, ypos + 6))
    
    if ball_on != 0:
        manor.setTimer(0, mover, 0)
    
def mnr_render(gworld, layer):
    global ball_on

    if (layer == 1) and (ball_on == 1):
        gworld.foreColor(0xFF, 0xFF, 0, 0)
        gworld.fillOval(rect(xpos - 5, ypos - 5, xpos + 5, ypos + 5))
    return 0
    
def mnr_outchat(chatText):
    global ball_on
    
    if chatText == "ball":
        ball_on = 1
        manor.setTimer(0, mover, 0)
        chatText = ""
        
    if chatText == "no ball":
        ball_on = 0
        chatText = ""
        
    return chatText

[edit] StarHawk

This is a much more complex script to implement a live action, multiplayer video game. The script is not finished as it still needs functions for the player to change the control keys and some other niceties, but it does show what can be done and how to do it.

The script uses three spots in the room, Spot 1 contains the script itself and defines the location of the centeral "star". Spot 2 defines the playing field, and Spot 3 defines where the players stats should be displayed.

import manor
import math
import random

global initialized, numPlayers, players, torps, lastPing, star_x, star_y, play_w, play_h

initialized = 0
numPlayers = 0
lastPing = 0
players = []
torps = []
star_x = 256
star_y = 192
play_w = 512
play_h = 384

class spaceObject:
    global star_x, star_y, play_w, play_h
    
    xpos = 105
    ypos = 150
    course = 0
    vel = 1.2
    lastUpdate = 0
    
    def calcPos(self):
        oldx = self.xpos
        oldy = self.ypos
        
        self.xpos += math.sin(math.radians(self.course)) * self.vel
        self.ypos += math.cos(math.radians(self.course)) * self.vel

        gravXDif = star_x - oldx
        gravYDif = star_y - oldy
        grav = 2 * self.vel / math.sqrt(gravXDif**2 + gravYDif**2)
                
        if gravYDif == 0:
            gravC = 90
            if gravXDif < 0:
                gravC = 270
        else:
            if gravYDif > 0:
                gravC = math.degrees(math.atan(gravXDif / gravYDif))
                if gravC < 0:
                    gravC += 360
            else:
                gravC = math.degrees(math.atan(gravXDif / gravYDif)) + 180
        
        self.xpos += math.sin(math.radians(gravC)) * grav
        self.ypos += math.cos(math.radians(gravC)) * grav
        
    def calcCourseSpd(self, xdif, ydif):
        self.vel = math.sqrt(xdif**2 + ydif**2)
            
        if ydif == 0:
            self.course = 90
            if xdif < 0:
                self.course = 270
        else:
            if ydif > 0:
                self.course = math.degrees(math.atan(xdif / ydif))
                if self.course < 0:
                    self.course += 360
            else:
                self.course = math.degrees(math.atan(xdif / ydif)) + 180
        
        if (self.xpos > play_w - 4):
            self.xpos = 4
        if (self.xpos < 4):
            self.xpos = play_w - 4
        if (self.ypos > play_h - 4):
            self.ypos = 4
        if (self.ypos < 4):
            self.ypos = play_h - 4

class torpedo(spaceObject):
    
    def __init__(self, x, y, course, vel, orientation, owner, serial, launchTime):
        self.xpos = x
        self.ypos = y
        self.course = course
        self.vel = vel
        self.orientation = orientation
        self.lastUpdate = manor.tickcount()
        self.fuel = 500
        self.owner = owner
        self.serial = serial
        self.launchTime = launchTime
        self.armed = 0
        
    def calcPos(self):
        currTick = manor.tickcount()
        timeDisp = currTick - self.lastUpdate
        
        if timeDisp != 0:
            manor.dirtyRect(rect(int(self.xpos) - 3, int(self.ypos) - 3, int(self.xpos) + 3, int(self.ypos) + 3))
            for time in range(timeDisp):
                oldx = self.xpos
                oldy = self.ypos
        
                spaceObject.calcPos(self)
                
                if self.fuel > 0:
                    thrust = 0.05/2.0
                    self.fuel -= 3
                    self.xpos += math.sin(math.radians(self.orientation)) * thrust
                    self.ypos += math.cos(math.radians(self.orientation)) * thrust
                            
                spaceObject.calcCourseSpd(self, self.xpos - oldx, self.ypos - oldy)
                
            manor.dirtyRect(rect(int(self.xpos) - 3, int(self.ypos) - 3, int(self.xpos) + 3, int(self.ypos) + 3))
        self.lastUpdate = currTick;
        
    def draw(self, gworld):
        ixpos = int(self.xpos)
        iypos = int(self.ypos)
        
        if self.fuel > 0:
            if self.armed:
                gworld.foreColor(0xFF, 0xFF, 0x80, 0x80)
            else:
                gworld.foreColor(0xFF, 0xFF, 0xFF, 0xFF)
        else:
            gworld.foreColor(0xFF, 0xFF, 0, 0)

        gworld.fillOval(rect(ixpos - 3, iypos - 3, ixpos + 3, iypos + 3))
    
class ship(spaceObject):
    
    def __init__(self, x, y):
        self.explodeLev = 0
        self.shipOr = 180
        self.eng_on = 0
        self.thrst_lft = 0
        self.thrst_rt = 0
        self.thrst_brk = 0
        self.thrst_fwd = 0       
        self.rotVel = 0
        self.shipImg = shape()
        self.engineImg = shape()
        self.leftThrstImg = shape()
        self.rightThrstImg = shape()
        self.fwdThrstImg = shape()
        self.brkThrstImg = shape()
        self.fuel = 5000
        self.torps = 10
        self.xpos = x
        self.ypos = y
        self.lastUpdate = manor.tickcount()
        
        self.shipImg.move(-4, 0)
        self.shipImg.line(-13, -8)
        self.shipImg.line(-13, -4)
        self.shipImg.line(-4, 7)
        self.shipImg.line(4, 7)
        self.shipImg.line(13, -4)
        self.shipImg.line(13, -8)
        self.shipImg.line(4, 0)
        self.shipImg.line(1, -13)
        self.shipImg.line(-1, -13)
        self.shipImg.line(-4, 0)
        self.shipImg.move(-13, -8)
        self.shipImg.line(-13, -14)
        self.shipImg.move(13, -8)
        self.shipImg.line(13, -14)
        self.shipImg.move(-2, 0)
        self.shipImg.line(0, -6)
        self.shipImg.line(2, 0)
        self.shipImg.line(-2, 0)
        
        self.engineImg.move(-3, 7)
        self.engineImg.line(0, 14)
        self.engineImg.line(3, 7)
        
        self.leftThrstImg.move(-2, -12)
        self.leftThrstImg.line(-5, -12)
        self.leftThrstImg.move(4, 7)
        self.leftThrstImg.line(7, 7)
        
        self.rightThrstImg.move(2, -12)
        self.rightThrstImg.line(5, -12)
        self.rightThrstImg.move(-4, 7)
        self.rightThrstImg.line(-7, 7)
        
        self.fwdThrstImg.move(-4, 7)
        self.fwdThrstImg.line(-4, 10)
        self.fwdThrstImg.move(4, 7)
        self.fwdThrstImg.line(4, 10)
        
        self.brkThrstImg.move(-6, -1)
        self.brkThrstImg.line(-6, -4)
        self.brkThrstImg.move(6, -1)
        self.brkThrstImg.line(6, -4)
        
    def calcPos(self):
        manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        
        currTick = manor.tickcount()
        timeDisp = currTick - self.lastUpdate
        
        if timeDisp != 0:
            for time in range(timeDisp):
                oldx = self.xpos
                oldy = self.ypos
        
                spaceObject.calcPos(self)

                if self.rotVel != 0:
                    self.shipOr += self.rotVel
                    if self.shipOr < 0:
                        self.shipOr += 360
                    if self.shipOr > 360:
                        self.shipOr -= 360
                
                if self.explodeLev == 0:
                    if self.fuel > 0:
                        if self.thrst_lft != 0:
                            self.rotVel -= 0.05/5.0
                            self.fuel -= 3
        
                        if self.thrst_rt != 0:
                            self.rotVel += 0.05/5.0
                            self.fuel -= 3
                    
                        thrust = 0
                    
                        if self.thrst_brk != 0:
                            thrust -= 0.05/5.0
                            self.fuel -= 3
                    
                        if self.thrst_fwd != 0:
                            thrust += 0.05/5.0
                            self.fuel -= 3
                        
                        if self.eng_on != 0:
                            thrust += 0.08/5.0
                            self.fuel -= 6
                    
                        if thrust != 0:
                            self.xpos += math.sin(math.radians(self.shipOr)) * thrust
                            self.ypos += math.cos(math.radians(self.shipOr)) * thrust
                        
                        if self.fuel < 0:
                            self.fuel = 0
                    else:
                        self.thrst_lft = 0
                        self.thrst_rt = 0
                        self.thrst_brk = 0
                        self.thrst_fwd = 0
                        self.eng_on = 0
                else:
                    if self.explodeLev > 0:
                        self.explodeLev -= 1
                        if self.explodeLev == 0:
                            self.explodeLev = -1
                            
                spaceObject.calcCourseSpd(self, self.xpos - oldx, self.ypos - oldy)
                
        
            self.shipImg.rotate(math.radians(self.shipOr))
            self.engineImg.rotate(math.radians(self.shipOr))
            self.leftThrstImg.rotate(math.radians(self.shipOr))
            self.rightThrstImg.rotate(math.radians(self.shipOr))
            self.fwdThrstImg.rotate(math.radians(self.shipOr))
            self.brkThrstImg.rotate(math.radians(self.shipOr))
            
            manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
            manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
            manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
            manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
            manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
            manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
                     
        self.lastUpdate = currTick
                
    def clean(self):
        manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))
    
    def draw(self, gworld):
        ixpos = int(self.xpos)
        iypos = int(self.ypos)
        
        if self.explodeLev > 0:
            color = 0xFF - (60 - self.explodeLev) * 4
            gworld.foreColor(0xFF, color, color, color)
            gworld.fillOval(self.shipImg.bounds().offset(ixpos, iypos))
        else:        
            gworld.foreColor(0xFF, 0, 0xFF, 0)
            self.shipImg.draw(gworld, ixpos, iypos)
        
            if self.eng_on != 0:
                gworld.foreColor(0xFF, 0, 0xFF, 0xFF)
                self.engineImg.draw(gworld, ixpos, iypos)
        
            if self.thrst_lft != 0:
                gworld.foreColor(0xFF, 0, 0xFF, 0xFF)
                self.leftThrstImg.draw(gworld, ixpos, iypos)
        
            if self.thrst_rt != 0:
                gworld.foreColor(0xFF, 0, 0xFF, 0xFF)
                self.rightThrstImg.draw(gworld, ixpos, iypos)

            if self.thrst_fwd != 0:
                gworld.foreColor(0xFF, 0, 0xFF, 0xFF)
                self.fwdThrstImg.draw(gworld, ixpos, iypos)
                
            if self.thrst_brk != 0:
                gworld.foreColor(0xFF, 0, 0xFF, 0xFF)
                self.brkThrstImg.draw(gworld, ixpos, iypos)
        
class player(ship):
    def __init__(self, userID, stats):
        self.userID = userID
        xpos, ypos, shipOr, course, vel, rotVel, thrst_lft, thrst_rt, thrst_brk, thrst_fwd, eng_on, fuel = stats

        ship.__init__(self, xpos, ypos)

        manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))

        self.shipOr = shipOr
        self.course = course
        self.vel = vel
        self.rotVel = rotVel
        self.thrst_lft = thrst_lft
        self.thrst_rt = thrst_rt
        self.thrst_brk = thrst_brk
        self.thrst_fwd = thrst_fwd
        self.eng_on = eng_on
        self.pingTime = 0
        self.fuel = fuel
        
        self.shipImg.rotate(math.radians(self.shipOr))
        self.engineImg.rotate(math.radians(self.shipOr))
        self.leftThrstImg.rotate(math.radians(self.shipOr))
        self.rightThrstImg.rotate(math.radians(self.shipOr))
        
        manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))

    def update(self, stats):
        manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))

        self.xpos, self.ypos, self.shipOr, self.course, self.vel, self.rotVel, self.thrst_lft, self.thrst_rt, self.thrst_brk, self.thrst_fwd, self.eng_on, self.fuel = stats

        self.shipImg.rotate(math.radians(self.shipOr))
        self.engineImg.rotate(math.radians(self.shipOr))
        self.leftThrstImg.rotate(math.radians(self.shipOr))
        self.rightThrstImg.rotate(math.radians(self.shipOr))

        manor.dirtyRect(self.shipImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.engineImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.leftThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.rightThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.fwdThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(self.brkThrstImg.bounds().offset(int(self.xpos), int(self.ypos)))
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))

        self.lastUpdate = manor.tickcount() - self.pingTime
    
    def calcPos(self):
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))
        
        ship.calcPos(self)
        
        manor.dirtyRect(rect(int(self.xpos) - 50, int(self.ypos) + 20, int(self.xpos) + 50, int(self.ypos) +36))

    def draw(self, gworld):
        ixpos = int(self.xpos)
        iypos = int(self.ypos)
        ship.draw(self, gworld)
        
        gworld.foreColor(0xFF, 0xFF, 0xFF, 0xFF)
        gworld.moveTo(ixpos - 49, iypos + 30)
        gworld.drawString(manor.getUserName(int(self.userID)))
        
#----------------------------------------------------------------------------
# sendPos
# 
# Send my current position to the room
#

def sendPos():
    global myship, lastSend
    
    parms = [myship.xpos, myship.ypos, myship.shipOr, myship.course, myship.vel, myship.rotVel, 
    myship.thrst_lft, myship.thrst_rt, myship.thrst_brk, myship.thrst_fwd, myship.eng_on, myship.fuel]
    manor.broadcastData("mypos", 0, parms)
    lastSend = manor.tickcount()

#----------------------------------------------------------------------------
# sendExplode
# 
# Send I blew up
#

def sendExplode():
    global myship
    
    parms = [myship.xpos, myship.ypos, myship.shipOr, myship.course, myship.vel, myship.rotVel]
    manor.broadcastData("idead", 0, parms)

#----------------------------------------------------------------------------
# recvExplode
# 
# Recv explode
#

def recvExplode(userID, data, parms):
    global players
    
    for i in players:
        if i.userID == userID:
            i.clean()
            i.lastUpdate = manor.tickcount() - i.pingTime
            i.xpos, i.ypos, i.shipOr, i.course, i.vel, i.rotVel = data
            if i.explodeLev == 0:
                i.explodeLev = 60

def sendTorp():
    global myship
    
    parms = [myship.xpos, myship.ypos, myship.course, myship.vel, myship.shipOr, myship.torps]
    manor.broadcastData("torp", 0, parms)
    
def torpRecv(userID, data, parms):
    global torps, players
    
    xpos, ypos, course, vel, shipOr, serial = data
    pingTime = 0
    for i in players:
        if i.userID == userID:
            pingTime = i.pingTime
            
    torps.append(torpedo(xpos, ypos, course, vel, shipOr, userID, serial, manor.tickcount() - pingTime))
    
#----------------------------------------------------------------------------
# sendPing
# 
# Send a timing ping
#

def sendPing():
    manor.broadcastData("ping", 0, manor.tickcount())

#----------------------------------------------------------------------------
# pingRecv
#
# Respond to ping

def pingRecv(userID, data, parms):
    manor.sendData("pong", [userID], data)

#----------------------------------------------------------------------------
# pongRecv
#
# Receive a pong

def pongRecv(userID, data, parms):
    global players, numPlayers
    
    if numPlayers != 0:
        for i in players:
            if i.userID == userID:
                i.pingTime = (manor.tickcount() - data) / 2
                #print "pingtime ",i.pingTime

#----------------------------------------------------------------------------
# mover
# 
# Calculate all the movement
#

def mover(param):
    global myship, numPlayers, players, lastSend, lastPing, torps, star_x, star_y
    
    sendInfo = 0
    
    if manor.tickcount() - lastPing > 300:
        sendPing()
        
    if myship.explodeLev == 0:
        statsRect = manor.getSpotBounds(3)
        
        if (manor.getClientType() == 1 and manor.isKeyDown(102)) or (manor.getClientType() != 1 and manor.isKeyDown(88)):
            if myship.thrst_lft == 0:
                sendInfo = 1
            myship.thrst_lft = 1
            manor.dirtyRect(statsRect)
        else:
            if myship.thrst_lft == 1:
                sendInfo = 1
            myship.thrst_lft = 0
    
        if (manor.getClientType() == 1 and manor.isKeyDown(100)) or (manor.getClientType() != 1 and manor.isKeyDown(86)):
            if myship.thrst_rt == 0:
                sendInfo = 1
            myship.thrst_rt = 1
            manor.dirtyRect(statsRect)
        else:
            if myship.thrst_rt == 1:
                sendInfo = 1
            myship.thrst_rt = 0

        if (manor.getClientType() == 1 and manor.isKeyDown(101)) or (manor.getClientType() != 1 and manor.isKeyDown(87)):
            if myship.thrst_fwd == 0:
                sendInfo = 1
            myship.thrst_fwd = 1
            manor.dirtyRect(statsRect)
        else:
            if myship.thrst_fwd == 1:
                sendInfo = 1
            myship.thrst_fwd = 0

        if (manor.getClientType() == 1 and manor.isKeyDown(98)) or (manor.getClientType() != 1 and manor.isKeyDown(84)):
            if myship.thrst_brk == 0:
                sendInfo = 1
            myship.thrst_brk = 1
            manor.dirtyRect(statsRect)
        else:
            if myship.thrst_brk == 1:
                sendInfo = 1
            myship.thrst_brk = 0
        
        if (manor.getClientType() == 1 and manor.isKeyDown(104)) or (manor.getClientType() != 1 and manor.isKeyDown(91)):
            if myship.eng_on == 0:
                sendInfo = 1
            myship.eng_on = 1
            manor.dirtyRect(statsRect)
        else:
            if myship.eng_on == 1:
                sendInfo = 1
            myship.eng_on = 0
            
    elif myship.explodeLev == -1:
        statsRect = manor.getSpotBounds(3)
        myship.clean()
        myship.xpos = random.random() * 7 * 32
        myship.ypos = random.random() * 7 * 32
        myship.vel = .1
        myship.explodeLev = 0
        myship.fuel = 5000
        myship.torps = 10
        myship.rotVel = 0
        manor.dirtyRect(statsRect)
        sendInfo = 1
        
    myship.calcPos()
    
    if math.sqrt((myship.xpos - star_x)**2 + (myship.ypos - star_y)**2) < 32 and myship.explodeLev == 0:
        myship.explodeLev = 60
        sendExplode()
    
    if sendInfo == 1:
        sendPos()
    
    count = 0
    for i in torps:
        i.calcPos()
        if math.sqrt((i.xpos - star_x)**2 + (i.ypos - star_y)**2) < 32:
            del torps[count]
        else:
            if i.launchTime + 60 <= manor.tickcount():
                i.armed = 1
            if i.launchTime + 300 <= manor.tickcount():
                del torps[count]
            else:
                if i.armed:
                    if math.sqrt((i.xpos - myship.xpos)**2 + (i.ypos - myship.ypos)**2) < 16:
                        if myship.explodeLev == 0:
                            myship.explodeLev = 60
                            sendExplode()
                        del torps[count]
                    for p in players:
                        if math.sqrt((i.xpos - p.xpos)**2 + (i.ypos - p.ypos)**2) < 16:
                            del torps[count]
        count += 1
        
    if numPlayers != 0:
        for i in players:
            i.calcPos()
            if math.sqrt((i.xpos - star_x)**2 + (i.ypos - star_y)**2) < 32 and i.explodeLev == 0:
                i.explodeLev = 60
            elif math.sqrt((i.xpos - myship.xpos)**2 + (i.ypos - myship.ypos)**2) < 25:
                if myship.explodeLev == 0:
                    myship.explodeLev = 60
                    sendExplode()
            
    manor.setTimer(manor.tickcount() + 1, mover, 0)

#----------------------------------------------------------------------------
# posRecv
#
# Receive player position update, add to player list if new

def posRecv(userID, data, parms):
    global players, numPlayers
    
    if numPlayers == 0:
        players.append(player(userID, data))
        sendPos()
        numPlayers += 1
    else:
        found = 0
        for i in players:
            if i.userID == userID:
                found = 1
                i.update(data)
                i.explodeLev = 0
        
        if found == 0:
            players.append(player(userID, data))
            sendPos()
            numPlayers += 1
        
#----------------------------------------------------------------------------
# Render event
#

def mnr_render(gworld, layer):
    global myship, initialized, numPlayers, players, torps

    handled = 0
    if layer == 2:
        if initialized == 1:
            myship.draw(gworld)
            top, left, bottom, right = (manor.getSpotBounds(3)).bounds()
            names = [["Fuel:", str(myship.fuel)], ["Torpedoes:", str(myship.torps)], ["Hull temp:", "0"]]
            currLine = top + gworld.fontAscent()

            for i in names:
                gworld.foreColor(0xFF, 0xFF, 0xFF, 0xFF)
                gworld.moveTo(left + 60 - gworld.stringWidth(i[0]), currLine)
                gworld.drawString(i[0])
                
                gworld.foreColor(0xFF, 0, 0xFF, 0)
                gworld.moveTo(left + 65, currLine)
                gworld.drawString(i[1])
                
                currLine += gworld.fontHeight()
             
        for i in torps:
            i.draw(gworld)
            
        if numPlayers != 0:
            for i in players:
                i.draw(gworld)
                
        handled = 1
    return handled

#----------------------------------------------------------------------------
# Enter event
#

def mnr_enter(userID):
    global myship, initialized, numPlayers, player, star_x, star_y, play_w, play_h
    
    if userID == manor.myID() or manor.myID() == 0:
        print("StarHawk Copyright ©2004 MadWolf Software");
        print("This script may be freely redistributed provided this notice is kept intact");
        myship = ship(115, 190)
        t, l, b, r = manor.getSpotBounds(1).bounds()
        star_x = l + (r - l) / 2
        star_y = t + (b - t) / 2
        t, l, b, r = manor.getSpotBounds(2).bounds()
        play_w = r - l
        play_h = b - t
        initialized = 1
        manor.setTimer(manor.tickcount() + 1, mover, 0)
        manor.regDataReceive("mypos", posRecv, 0)
        manor.regDataReceive("idead", recvExplode, 0)
        manor.regDataReceive("ping", pingRecv, 0)
        manor.regDataReceive("pong", pongRecv, 0)
        manor.regDataReceive("torp", torpRecv, 0)
        sendPos()

def mnr_leave(userID):
    global players, numPlayers
    
    count = 0
    for i in players:
        if i.userID == userID:
            i.clean()
            del players[count]
            numPlayers -= 1
        count += 1
    
def mnr_keydown(char, key):
    global myship, torps
    
    handled = 0
    
    #print key
    
    if (manor.getClientType() == 1):
        if key == 100 or key == 102 or key == 104 or key == 101 or key == 98 or key == 103:
            handled = 1
    elif key == 91 or key == 86 or key == 88 or key == 87 or key == 84 or key == 89:
        handled = 1
    if ((manor.getClientType() != 1 and key == 89) or (manor.getClientType() == 1 and key == 103)) and myship.torps > 0 and myship.explodeLev == 0:
        torps.append(torpedo(myship.xpos, myship.ypos, myship.course, myship.vel, myship.shipOr, 0, myship.torps, manor.tickcount()))
        sendTorp()
        myship.torps -= 1
        manor.dirtyRect(manor.getSpotBounds(3))
            
    return handled
Personal tools