Advertisement
here2share

# CodeSkulptor_StarWars_Trench_demo.py

Jan 11th, 2015
367
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 26.21 KB | None | 0 0
  1. # CodeSkulptor_online_visual_demo.py ### Not a Pys60 project, but does great for simulations
  2. # http://www.codeskulptor.org/#demos-starwars.py
  3.  
  4. # Star Wars, written on 4th & 5th November 2012 by Steven Knock.
  5. #
  6. # In the spirit of remaking vintage arcade games, this is my version of the trench sequence
  7. # (3rd level) of Star Wars. I haven't seen the game for many years, so I apologise if this
  8. # remake is not accurate, but I just based it on memory.
  9. #
  10. # This was developed midway through the Introduction to Interactive Programming in Python course,
  11. # and represents the knowledge I had of the language at that point (i.e. no OOP).
  12. #
  13. # Current Version: v1.5
  14. #   in which I updated the code to work with CodeSkulptor's new integer and float rules
  15. #   and also changed the velocity damping factor from 0.9 to 0.7 to make movement more predictable.
  16. #
  17. # Earlier Versions:
  18. #   v1.4 (http://www.codeskulptor.org/#user9_WqwyV6z7OsvIYq8.py)
  19. #   . in which I utilised the new text width functions and adapted the game to run at variable frame rates.
  20. #
  21. #   v1.3 (http://www.codeskulptor.org/#user4-LHqyHJCu49j37Rf.py)
  22. #   . in which I fixed a bug that allowed barriers without holes to be generated.
  23. #
  24. #   v1.2 (http://www.codeskulptor.org/#user4-ltFlPO4h6ZD8LNC.py)
  25. #   . in which I added vertical lines to the trench walls and added more comments to the code.
  26. #
  27. #   v1.1 (http://www.codeskulptor.org/#user4-FQssuMMrmX8K9tg.py)
  28. #   . in which I provided a warning about the flashing effects and allowed them to be turned off,
  29. #     and also changed references from Photon Torpedoes to Proton Torpedoes.
  30. #
  31. #   v1.0 (http://www.codeskulptor.org/#user4-TUA5Ti3pPicfK3M.py)
  32. #
  33. # I've made the game challenging, but not too hard. I can destroy the Death Star about once every
  34. # three attempts, so I think that I pitched it about right, but of course you can edit the code
  35. # and shrink the dimensions of your X-Wing, or reduce your velocity if you find it too tough.
  36. #
  37. # Tip 1: remember that X-Wings are wider than they are tall.
  38. # Tip 2: don't forget to fire your proton torpedoes once your distance display reads zero.
  39. #
  40. # May the force be with you all.
  41.  
  42.  
  43. import math
  44. import random
  45. import simplegui
  46. import time
  47.  
  48. VERSION = "v1.5"
  49.  
  50. CANVAS_WIDTH = 800
  51. CANVAS_HEIGHT = 600
  52. TRENCH_LENGTH_M = 3500
  53. TRENCH_WIDTH_M = 10
  54. TRENCH_HEIGHT_M = 10
  55. TRENCH_WALL_INTERVAL_M = 20
  56. TRENCH_COLOUR = "#ddd"
  57. EXHAUST_PORT_POSITION_M = TRENCH_LENGTH_M - 100
  58. EXHAUST_PORT_WIDTH_M = TRENCH_WIDTH_M / 3.0
  59. PROTON_TORPEDO_RANGE_M = 200
  60. PROTON_TORPEDO_RADIUS_M = 0.3
  61. PROTON_TORPEDO_SPAN_M = 0.7
  62. LAUNCH_POSITION_M = EXHAUST_PORT_POSITION_M - PROTON_TORPEDO_RANGE_M
  63. DISTANCE_COLOUR = "Red"
  64. DEATH_STAR_RADIUS = CANVAS_HEIGHT * 0.4
  65. DEATH_STAR_COLOUR = "#aaa"
  66. LINE_WIDTH = 2
  67.  
  68. MODE_INTRO = 0
  69. MODE_GAME = 1
  70. MODE_VICTORY = 2
  71.  
  72. SHIP_WIDTH_M = 1.6
  73. SHIP_HEIGHT_M = 0.8
  74.  
  75. NEAR_PLANE_M = 0.1
  76. FAR_PLANE_M = 180.0
  77. SCALE_WIDTH = CANVAS_WIDTH / 2
  78. SCALE_HEIGHT = CANVAS_HEIGHT / 2
  79.  
  80. FORWARD_VELOCITY_MS = 60.0
  81. PROTON_TORPEDO_VELOCITY_MS = FORWARD_VELOCITY_MS * 1.1
  82. VELOCITY_MAX_MS = 15.0
  83. VELOCITY_DAMPEN = 0.7
  84. ACCELERATION_MSS = 100.0
  85.  
  86. TORPEDO_COLOUR = "Red"
  87. EXHAUST_PORT_COLOUR = "Red"
  88. INTRO_TEXT_COLOUR = "Yellow"
  89. WARNING_TEXT_COLOUR = "Red"
  90. PARTICLE_COLOUR = "White"
  91. FONT_STYLE = "sans-serif"
  92.  
  93. BARRIER_COLOURS = ( ( 255, 0, 0 ), ( 255, 192, 0 ), ( 0, 255, 0 ), ( 255, 255, 0 ), ( 0, 255, 255 ), ( 255, 0, 255 ) )
  94.  
  95. BLOCK_VERTEX = ( ( 0, 1 ), ( 1, 2 ), ( 2, 3 ), ( 3, 0 ), ( 0, 4 ), ( 1, 5 ), ( 2, 6 ), ( 3, 7 ), ( 4, 5 ), ( 5, 6 ), ( 6, 7 ), ( 7, 4 ), ( 0, 2 ), ( 1, 3 ) )
  96.  
  97. HEX_DIGITS = ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' )
  98.  
  99. # Ship Variables
  100. pos = []
  101. vel = []
  102. acc = []
  103.  
  104. # Proton Torpedo Variables
  105. pt_pos = []
  106. pt_launch_position = 0
  107.  
  108. # Exhaust Port in Range
  109. reached_launch_position = False
  110.  
  111. # Trench Barriers
  112. barriers = []
  113. current_barrier_index = 0
  114.  
  115. # Message
  116. message = ""
  117. message_delay = 0
  118. message_tick = 0
  119.  
  120. # Stars
  121. stars = []
  122.  
  123. # Victory
  124. explosion_countdown = 0
  125. particles = []
  126.  
  127. # Game State
  128. game_mode = MODE_INTRO
  129. dead = False
  130. violent_death = True
  131.  
  132. # Actual FPS
  133. last_time = 0
  134. fps = 60
  135.  
  136. # Return the centre point of the canvas
  137. def get_canvas_centre():
  138.     return ( CANVAS_WIDTH // 2, CANVAS_HEIGHT // 2 )
  139.  
  140. # Convert a number from 0 to 255 to a pair of hex digits - used in calculating RGB colour values
  141. def hex( n ):
  142.     n = min( int( n ), 255 )
  143.     d1 = HEX_DIGITS[ n // 16 ]
  144.     d2 = HEX_DIGITS[ n % 16 ]
  145.     return d1 + d2
  146.  
  147. # Creates an array of stars randomly scattered on the canvas
  148. def create_stars():
  149.     while len( stars ) < 300:
  150.         x = random.randrange( CANVAS_WIDTH )
  151.         y = random.randrange( CANVAS_HEIGHT )
  152.         stars.append( ( x, y ) )
  153.  
  154. # Creates all of the barriers that appear in the game. Each Barrier is represented by three elements:
  155. # Start Position - the distance along the trench that the barrier starts
  156. # Length - the length of the barrier
  157. # Blocks - an array of 9 ints, either 1 or 0, that indicate which blocks in a 3x3 square appear in the barrier.
  158. # The Barriers are placed in a list which is sequenced by the Start Position of the Barriers. This allows
  159. #   the rendering and collision code to consider only the barriers immediately surrounding the ship.
  160. def create_barriers():
  161.     global barriers
  162.     barriers = []
  163.  
  164.     # Determine Start Position and Last Position
  165.     s = 150.0
  166.     limit = LAUNCH_POSITION_M - 150
  167.     while s < limit:
  168.         # Create a totally solid barrier
  169.         blocks = []
  170.         for i in range( 0, 9 ):
  171.             blocks.append( 1 )
  172.  
  173.         # Punch a number of empty blocks in the barrier, based on how close to the end of the trench the barrier is.
  174.         empty_blocks = 1.0 - ( s / limit )
  175.         empty_blocks = int( ( empty_blocks * 8 ) ) + 2
  176.         for i in range( 0, empty_blocks ):
  177.             blocks[ random.randrange( 9 ) ] = 0
  178.  
  179.         # Calculate a random length
  180.         l = random.randrange( 5 ) + 5
  181.         barriers.append( ( s, l, blocks ) )
  182.         s += l
  183.         s += 40 + random.randrange( 30 )
  184.  
  185. # Initialise all game variables to prepare for a new game
  186. def init_game():
  187.     global game_mode, pos, vel, acc, pt_pos, pt_launch_position, reached_launch_position, current_barrier_index, dead, explosion_countdown
  188.     game_mode = MODE_GAME
  189.     pos = [ 0, 0, 0 ]
  190.     vel = [ 0, 0, FORWARD_VELOCITY_MS ]
  191.     acc = [ 0, 0, 0 ]
  192.     pt_pos = []
  193.     pt_launch_position = -1
  194.     reached_launch_position = False
  195.     dead = False
  196.     current_barrier_index = 0
  197.     explosion_countdown = 0
  198.     create_barriers()
  199.     set_message( "Use the Force" )
  200.  
  201. # Calculates the distance remaining until the ship reaches the torpoedo launch position
  202. def get_distance_to_launch_position():
  203.     return LAUNCH_POSITION_M - pos[ 2 ]
  204.  
  205. # Indicates whether the ship is 'close' to the launch position.
  206. def is_close_to_launch_position():
  207.     return math.fabs( get_distance_to_launch_position() ) < 100.0
  208.  
  209. # Fire the Proton Torpedoes, if they haven't already been launched.
  210. # They are initially positioned slightly under the ship.
  211. def launch_proton_torpedoes():
  212.     global pt_launch_position
  213.  
  214.     if pt_launch_position < 0 and is_close_to_launch_position():
  215.         pt_pos.append( [ pos[ 0 ] - PROTON_TORPEDO_SPAN_M, pos[ 1 ] + 1, pos[ 2 ] ] )
  216.         pt_pos.append( [ pos[ 0 ] + PROTON_TORPEDO_SPAN_M, pos[ 1 ] + 1, pos[ 2 ] ] )
  217.         pt_launch_position = pos[ 2 ]
  218.  
  219. # Sets the current message. The message appears for 3 seconds.
  220. def set_message( new_message ):
  221.     global message, message_delay, message_tick
  222.     message = new_message
  223.     message_delay = 3
  224.     message_tick = 0
  225.  
  226. # Projects a 3d point into a 2d canvas coordinate. The 3d coordinates are based on +x -> right, +y -> down +z -> away.
  227. # The origin of the 3d coordinate system is the ship's initial position in the middle of the start of the trench.
  228. def project( point ):
  229.     distance = point[ 2 ] - pos[ 2 ]
  230.     if distance <= NEAR_PLANE_M:
  231.         distance = NEAR_PLANE_M
  232.     x = ( point[ 0 ] - pos[ 0 ] ) / ( distance + NEAR_PLANE_M )
  233.     y = ( point[ 1 ] - pos[ 1 ] ) / ( distance + NEAR_PLANE_M )
  234.     x *= SCALE_WIDTH
  235.     y *= SCALE_HEIGHT
  236.     x += ( CANVAS_WIDTH // 2 )
  237.     y += ( CANVAS_HEIGHT // 2 )
  238.     return ( x, y )
  239.  
  240. # Displays the Death sequence. If the flashing colours are enabled, then a red rectangle is drawn onto the screen every other frame.
  241. def render_death( canvas ):
  242.     global message_tick
  243.     if dead:
  244.         if violent_death and ( message_tick % 2 == 0 ) :
  245.             canvas.draw_polygon( ( ( 0, 0 ), ( CANVAS_WIDTH, 0 ), ( CANVAS_WIDTH, CANVAS_HEIGHT ), ( 0, CANVAS_HEIGHT ) ), 1, "Red", "Red" )
  246.         message_tick += 1
  247.  
  248. # Draws the trench. Firstly, four lines are drawn from the player's z position towards the end of the trench.
  249. # Secondly, a rectangle is drawn at the end of the trench.
  250. # Thirdly, the lines along the wall are drawn.
  251. def render_trench( canvas ):
  252.     tw = TRENCH_WIDTH_M // 2
  253.     th = TRENCH_HEIGHT_M // 2
  254.     trench = ( [ -tw, -th ], [ tw, -th ], [ tw, th ], [ -tw, th ] )
  255.     trench_p = []
  256.     for t in trench:
  257.         near = list( t )
  258.         near.append( pos[ 2 ] )
  259.         far = list( t )
  260.         far.append( TRENCH_LENGTH_M )
  261.         near_p = project( near )
  262.         far_p = project( far )
  263.         canvas.draw_line( near_p, far_p, LINE_WIDTH, TRENCH_COLOUR )
  264.         trench_p.append( far_p )
  265.  
  266.     # Draw far wall
  267.     trench_p.append( trench_p[ 0 ] )
  268.     canvas.draw_polyline( trench_p, LINE_WIDTH, TRENCH_COLOUR )
  269.  
  270.     # Draw vertical walls
  271.     distance = ( int( pos[ 2 ] + TRENCH_WALL_INTERVAL_M ) // TRENCH_WALL_INTERVAL_M ) * TRENCH_WALL_INTERVAL_M
  272.     limit = min( pos[ 2 ] + FAR_PLANE_M, TRENCH_LENGTH_M )
  273.     while distance < limit:
  274.         for side in [ -1, 1 ]:
  275.             p1 = project( ( side * tw, -th, distance ) )
  276.             p2 = project( ( side * tw, th, distance ) )
  277.             canvas.draw_line( p1, p2, LINE_WIDTH, TRENCH_COLOUR )
  278.         distance += TRENCH_WALL_INTERVAL_M
  279.  
  280. # Draws a single barrier.
  281. def render_barrier( canvas, barrier ):
  282.     n = barrier[ 0 ]            # Barrier Start Position
  283.     f = n + barrier[ 1 ]        # Barrier End Position
  284.     m = barrier[ 2 ]            # Barrier Block Array
  285.     w = TRENCH_WIDTH_M / 3.0    # Block Width
  286.     h = TRENCH_HEIGHT_M / 3.0   # Block Height
  287.     hw = w / 2.0                # Block Half Width
  288.     hh = h / 2.0                # Block Half Height
  289.  
  290.     # Calculate the colour of the blocks, based on base colour and distance.
  291.     # The barrier's base colour is taken from its start position.
  292.     distance = 1.0 - 0.9 * ( n - pos[ 2 ] ) / FAR_PLANE_M
  293.     base_colour = BARRIER_COLOURS[ n % len( BARRIER_COLOURS ) ]
  294.     colour = "#"
  295.     for component in range( 0, 3 ):
  296.         colour += hex( base_colour[ component ] * distance )
  297.  
  298.     i = 0                   # Block Index ( 0 to 8 )
  299.     for y in range( -1, 2 ):
  300.         for x in range( -1, 2 ):
  301.             if m[ i ] == 1: # Test if Block is present
  302.                 px = x * w  # Coordinates at the centre of the block
  303.                 py = y * h
  304.                 cube = (    # Define a tuple containing the coordinates for this cube. They are indexed by BLOCK_VERTEX.
  305.                     ( px - hw, py - hh, n ),
  306.                     ( px + hw, py - hh, n ),
  307.                     ( px + hw, py + hh, n ),
  308.                     ( px - hw, py + hh, n ),
  309.                     ( px - hw, py - hh, f ),
  310.                     ( px + hw, py - hh, f ),
  311.                     ( px + hw, py + hh, f ),
  312.                     ( px - hw, py + hh, f )
  313.                 )
  314.  
  315.                 # Project the 3d coordinates into 2d canvas coordinates
  316.                 cube_p = []
  317.                 for p in cube:
  318.                     cube_p.append( project( p ) )
  319.  
  320.                 # Draw the lines
  321.                 for vi in BLOCK_VERTEX:
  322.                     canvas.draw_line( cube_p[ vi[ 0 ] ], cube_p[ vi[ 1 ] ], LINE_WIDTH, colour )
  323.             i += 1
  324.  
  325. # Draws all of the visible barriers. The game remembers the first visible barrier (current_barrier_index),
  326. # so that each frame it doesn't need to go through the entire list of barriers to get the first that is visible.
  327. # The visible barriers are always inserted at the front of their own list, which ensures that they are drawn back to front.
  328. def render_barriers( canvas ):
  329.     global current_barrier_index
  330.     visible_barriers = []
  331.  
  332.     index = current_barrier_index
  333.     while index < len( barriers ):
  334.         barrier = barriers[ index ]
  335.         index += 1
  336.         visible = ( barrier[ 0 ] + barrier[ 1 ] - pos[ 2 ] ) > NEAR_PLANE_M
  337.         visible = visible and ( barrier[ 0 ] - pos[ 2 ] < FAR_PLANE_M )
  338.         if visible:
  339.             visible_barriers.insert( 0, barrier )
  340.         elif pos[ 2 ] > barrier[ 0 ]:
  341.             current_barrier_index = index
  342.         else:
  343.           break
  344.  
  345.     for barrier in visible_barriers:
  346.         render_barrier( canvas, barrier )
  347.  
  348. def render_exhaust_port( canvas ):
  349.     if reached_launch_position:
  350.         y = TRENCH_HEIGHT_M / 2
  351.         z = EXHAUST_PORT_POSITION_M
  352.         w = EXHAUST_PORT_WIDTH_M
  353.         hw = w / 2
  354.         hole = ( ( -hw, y, z - hw ), ( hw, y, z - hw ), ( hw, y, z + hw ), ( -hw, y, z + hw ) )
  355.         coords = []
  356.         for p in hole:
  357.             coords.append( project( p ) )
  358.         coords.append( coords[ 0 ] )
  359.         canvas.draw_polyline( coords, LINE_WIDTH, EXHAUST_PORT_COLOUR )
  360.  
  361.         canvas.draw_line( project( ( -w, y, z ) ), project( ( -hw, y, z ) ), LINE_WIDTH, EXHAUST_PORT_COLOUR )
  362.         canvas.draw_line( project( ( w, y, z ) ), project( ( hw, y, z ) ), LINE_WIDTH, EXHAUST_PORT_COLOUR )
  363.         canvas.draw_line( project( ( 0, y, z - w ) ), project( ( 0, y, z - hw ) ), LINE_WIDTH, EXHAUST_PORT_COLOUR )
  364.         canvas.draw_line( project( ( 0, y, z + w ) ), project( ( 0, y, z + hw ) ), LINE_WIDTH, EXHAUST_PORT_COLOUR )
  365.  
  366. def render_torpedo( canvas, pos ):
  367.     centre = project( pos )
  368.     edge = project( [ pos[ 0 ] - PROTON_TORPEDO_RADIUS_M, pos[ 1 ], pos[ 2 ] ] )
  369.     radius = centre[ 0 ] - edge[ 0 ]
  370.     canvas.draw_circle( centre, radius, LINE_WIDTH, TORPEDO_COLOUR )
  371.  
  372. def render_torpedoes( canvas ):
  373.     if len( pt_pos ) > 0:
  374.         for p in pt_pos:
  375.             render_torpedo( canvas, p )
  376.  
  377. def render_distance( canvas ):
  378.     distance = int( get_distance_to_launch_position() )
  379.     if distance > 0:
  380.         distance_str = str( distance )
  381.         while len( distance_str ) < 5:
  382.             distance_str = "0" + distance_str
  383.         distance_str += "m"
  384.         draw_text_centre( canvas, distance_str, CANVAS_HEIGHT - 4, 29, DISTANCE_COLOUR )
  385.  
  386. def render_message( canvas ):
  387.     global message_delay
  388.     if ( message_delay > 0 ):
  389.         y = CANVAS_HEIGHT // 2 + 90
  390.         for line in message.split( "\n" ):
  391.             draw_text_centre( canvas, line, y, 35, "White" )
  392.             y += 45
  393.  
  394. def move_ship():
  395.     global pos, vel, acc, pt_launch_position
  396.  
  397.     # Pull up at the end of the Trench
  398.     if pos[ 2 ] > EXHAUST_PORT_POSITION_M:
  399.         acc[ 1 ] = -ACCELERATION_MSS
  400.         if pt_launch_position < 0:
  401.             set_message( "You forgot to fire your torpedoes" )
  402.             pt_launch_position = 0
  403.  
  404.     # Slow down when poised to launch torpedo
  405.     factor = float( fps )
  406.     if pt_launch_position < 0 and is_close_to_launch_position():
  407.         factor *= 4
  408.  
  409.     for i in range( 0, 3 ):
  410.         pos[ i ] += vel[ i ] / factor
  411.         if acc[ i ] != 0:
  412.             vel[ i ] += acc[ i ] / factor
  413.             if vel[ i ] < -VELOCITY_MAX_MS:
  414.                 vel[ i ] = -VELOCITY_MAX_MS
  415.             elif vel[ i ] > VELOCITY_MAX_MS:
  416.                 vel[ i ] = VELOCITY_MAX_MS
  417.         elif i < 2:
  418.             vel[ i ] *= VELOCITY_DAMPEN
  419.  
  420. def move_torpedoes():
  421.     global pt_pos, explosion_countdown
  422.     if len( pt_pos ) > 0:
  423.         hit = False
  424.         bullseye = False
  425.         for p in pt_pos:
  426.             # Check if the torpedo has reached the point at which it dives towards the floor of the trench
  427.             if p[ 2 ] - pt_launch_position >= PROTON_TORPEDO_RANGE_M:
  428.                 p[ 1 ] += PROTON_TORPEDO_VELOCITY_MS * 0.5 / fps
  429.             else:
  430.                 p[ 2 ] += PROTON_TORPEDO_VELOCITY_MS / fps
  431.  
  432.             # Check if the torpedo has hit the floor of the trench
  433.             if p[ 1 ] > TRENCH_HEIGHT_M / 2:
  434.                 hw = EXHAUST_PORT_WIDTH_M / 2
  435.                 z = EXHAUST_PORT_POSITION_M
  436.                 ex1 = -hw
  437.                 ex2 = hw
  438.                 ez1 = z - hw
  439.                 ez2 = z + hw
  440.                 # Check if torpedo entirely fit within the exhaust port
  441.                 if  p[ 0 ] - PROTON_TORPEDO_RADIUS_M >= ex1 and \
  442.                     p[ 0 ] + PROTON_TORPEDO_RADIUS_M <= ex2 and \
  443.                     p[ 2 ] - PROTON_TORPEDO_RADIUS_M >= ez1 and \
  444.                     p[ 2 ] + PROTON_TORPEDO_RADIUS_M <= ez2:
  445.                     bullseye = True
  446.                 hit = True
  447.         if hit:
  448.             pt_pos = []     # Delete the torpedos
  449.             if bullseye:
  450.                 set_message( "Great shot kid - That was one in a million" )
  451.                 explosion_countdown = 180
  452.             else:
  453.                 set_message( "Negative - It just impacted off the surface" )
  454.  
  455. # Keep the ship within the bounds of the trench.
  456. def constrain_ship():
  457.     tw = TRENCH_WIDTH_M // 2
  458.     th = TRENCH_HEIGHT_M // 2
  459.  
  460.     # Keep the ship within the horizontal span of the trench
  461.     m = SHIP_WIDTH_M / 2
  462.     if pos[ 0 ] < ( -tw + m ):
  463.         pos[ 0 ] = -tw + m
  464.     elif pos[ 0 ] > ( tw - m ):
  465.         pos[ 0 ] = tw - m
  466.  
  467.     # Keep the ship within the vertical span of the trench
  468.     m = SHIP_HEIGHT_M / 2
  469.     if pos[ 1 ] < ( -th + m ) and pt_launch_position < 0:       # Allow the ship to leave the trench after it has launched the torpedoes
  470.         pos[ 1 ] = -th + m
  471.     elif pos[ 1 ] > ( th - m ):
  472.         pos[ 1 ] = th - m
  473.  
  474. # Determine whether the ship has collided with any blocks
  475. def check_for_collisions():
  476.     global dead
  477.  
  478.     if current_barrier_index < len( barriers ):
  479.         barrier = barriers[ current_barrier_index ]
  480.  
  481.         # Check if we are in the same Z position as the barrier
  482.         if pos[ 2 ] > barrier[ 0 ] and pos[ 2 ] < barrier[ 0 ] + barrier[ 1 ]:
  483.             # Calculate the area that our ship occupies
  484.             x1 = pos[ 0 ] - SHIP_WIDTH_M / 2.0
  485.             x2 = x1 + SHIP_WIDTH_M
  486.             y1 = pos[ 1 ] - SHIP_HEIGHT_M / 2.0
  487.             y2 = y1 + SHIP_HEIGHT_M
  488.  
  489.             # Calculate block size
  490.             bw = TRENCH_WIDTH_M / 3.0
  491.             bh = TRENCH_HEIGHT_M / 3.0
  492.             bhw = bw / 2.0
  493.             bhh = bh / 2.0
  494.             for by in range( -1, 2 ):
  495.                 by1 = by * bh - bhh
  496.                 by2 = by1 + bh
  497.  
  498.                 # Check to see whether we intersect vertically
  499.                 if y1 < by2 and y2 > by1:
  500.                     for bx in range( -1, 2 ):
  501.                         block_index = ( by + 1 ) * 3 + bx + 1
  502.                         if barrier[ 2 ][ block_index ] == 1:
  503.                             bx1 = bx * bw - bhw
  504.                             bx2 = bx1 + bw
  505.  
  506.                             # Check to see whether we intersect horizontally
  507.                             if x1 < bx2 and x2 > bx1:
  508.                                 set_message( "Game Over" )
  509.                                 dead = True
  510.  
  511. def generate_messages():
  512.     global reached_launch_position
  513.  
  514.     if not reached_launch_position and is_close_to_launch_position():
  515.         reached_launch_position = True
  516.         set_message( "You're all clear kid, now let's\nblow this thing and go home" )
  517.  
  518. # This is the main game processing function.
  519. def render_game( canvas ):
  520.     global game_mode
  521.  
  522.     if not dead:
  523.         move_ship()
  524.         move_torpedoes()
  525.         constrain_ship()
  526.         check_for_collisions()
  527.         generate_messages()
  528.     elif message_delay <= 0:
  529.         game_mode = MODE_INTRO
  530.     render_death( canvas )
  531.     render_trench( canvas )
  532.     render_barriers( canvas )
  533.     render_exhaust_port( canvas )
  534.     render_torpedoes( canvas )
  535.     render_distance( canvas )
  536.     render_message( canvas )
  537.  
  538.     if pos[ 2 ] > TRENCH_LENGTH_M + 60:
  539.         game_mode = MODE_VICTORY if explosion_countdown > 0 else MODE_INTRO
  540.  
  541. def render_deathstar( canvas, fill_colour = None ):
  542.     centre = get_canvas_centre()
  543.     radius = DEATH_STAR_RADIUS
  544.     if fill_colour == None:
  545.         canvas.draw_circle( centre, radius, LINE_WIDTH, DEATH_STAR_COLOUR, "Black" )
  546.         canvas.draw_circle( ( centre[ 0 ] - radius * 0.35, centre[ 1 ] - radius * 0.5 ), radius * 0.2, LINE_WIDTH, DEATH_STAR_COLOUR )
  547.         canvas.draw_line( ( centre[ 0 ] - radius, centre[ 1 ] - 3 ), ( centre[ 0 ] + radius, centre[ 1 ] - 3 ), LINE_WIDTH, DEATH_STAR_COLOUR )
  548.         canvas.draw_line( ( centre[ 0 ] - radius, centre[ 1 ] + 3 ), ( centre[ 0 ] + radius, centre[ 1 ] + 3 ), LINE_WIDTH, DEATH_STAR_COLOUR )
  549.     else:
  550.         canvas.draw_circle( centre, radius, 1, fill_colour, fill_colour )
  551.  
  552. def render_stars( canvas ):
  553.     star_colours = []
  554.     for shade in range( 8, 16 ):
  555.         component = hex( 16 * shade )
  556.         colour = "#" + component + component + component
  557.         star_colours.append( colour )
  558.  
  559.     i = 0
  560.     l = len( star_colours )
  561.     for star in stars:
  562.         canvas.draw_circle( star, 1, 1, star_colours[ i % l ] )
  563.         i += 1
  564.  
  565. def draw_text_centre( canvas, text, y, size, colour ):
  566.     centre = get_canvas_centre()
  567.     pos = ( centre[ 0 ] - frame.get_canvas_textwidth( text, size, FONT_STYLE ) // 2, y )
  568.     canvas.draw_text( text, pos, size, colour, FONT_STYLE )
  569.  
  570. def draw_text_right( canvas, text, x, y, size, colour ):
  571.     pos = ( x - frame.get_canvas_textwidth( text, size, FONT_STYLE ), y )
  572.     canvas.draw_text( text, pos, size, colour, FONT_STYLE )
  573.  
  574. def render_intro_text( canvas ):
  575.     centre = get_canvas_centre()
  576.     draw_text_centre( canvas, "Star Wars", 190, 58, INTRO_TEXT_COLOUR )
  577.     draw_text_centre( canvas, "Press Space to begin your attack run", 340, 24, INTRO_TEXT_COLOUR )
  578.     draw_text_centre( canvas, "Use Cursor Keys to move", 420, 19, INTRO_TEXT_COLOUR )
  579.     draw_text_centre( canvas, "Use Space to launch Proton Torpedo", 440, 19, INTRO_TEXT_COLOUR )
  580.     draw_text_centre( canvas, "Thanks to Joe, Scott, John, Stephen & Rice University for providing a fantastic 'Introduction to Python' Course", 580, 16, INTRO_TEXT_COLOUR )
  581.     draw_text_right( canvas, VERSION, CANVAS_WIDTH - 16, 14, 14, INTRO_TEXT_COLOUR )
  582.  
  583.     x1 = centre[ 0 ] - 160
  584.     y1 = centre[ 1 ] + ( 185 if violent_death else 205 )
  585.     x2 = centre[ 0 ] + 160
  586.     y2 = centre[ 1 ] + 225
  587.     canvas.draw_polygon( ( ( x1, y1 ), ( x2, y1 ), ( x2, y2 ), ( x1, y2 ) ), 1, "Black", "Black" )
  588.     draw_text_centre( canvas, "Press 'Q' to turn " + ( "OFF" if violent_death else "ON" ) + " flashing colours", 520, 18, WARNING_TEXT_COLOUR )
  589.     if violent_death:
  590.         draw_text_centre( canvas, "Note: this game contains flashing colours which are not suitable for sufferers of epilepsy", 500, 18, WARNING_TEXT_COLOUR )
  591.  
  592. def create_particles():
  593.     global particles
  594.  
  595.     radius = DEATH_STAR_RADIUS
  596.     particles = []
  597.     for i in range( 0, 500 ):
  598.         a = random.random() * 2 * math.pi
  599.         m = random.random()
  600.         x = math.sin( a ) * m * radius
  601.         y = math.cos( a ) * m * radius
  602.         particles.append( [ x, y ] )
  603.  
  604. def render_particles( canvas ):
  605.     c = get_canvas_centre()
  606.     for p in particles:
  607.         x = p[ 0 ] + c[ 0 ]
  608.         y = p[ 1 ] + c[ 1 ]
  609.         canvas.draw_circle( ( x, y ), 1, 1, PARTICLE_COLOUR )
  610.  
  611. def move_particles():
  612.     c = get_canvas_centre()
  613.     for p in particles:
  614.         x = p[ 0 ] + c[ 0 ]
  615.         y = p[ 1 ] + c[ 1 ]
  616.         if x >= 0 and x < CANVAS_WIDTH and y >= 0 and y < CANVAS_HEIGHT:
  617.             v = 1.1
  618.             p[ 0 ] *= v
  619.             p[ 1 ] *= v
  620.  
  621. def render_victory( canvas ):
  622.     global game_mode, explosion_countdown
  623.  
  624.     render_stars( canvas )
  625.     if explosion_countdown <= 0:
  626.         if explosion_countdown > -160:
  627.             base_colour = ( 64, 32, 16 )
  628.             factor = -explosion_countdown / 10.0
  629.             colour = "#"
  630.             for c in range( 0, 3 ):
  631.                 colour += hex( base_colour[ c ] * factor )
  632.             render_deathstar( canvas, colour )
  633.         elif explosion_countdown == -160:
  634.             create_particles()
  635.         elif explosion_countdown > -400:
  636.             render_particles( canvas )
  637.             move_particles()
  638.         else:
  639.             game_mode = MODE_INTRO
  640.     else:
  641.         render_deathstar( canvas )
  642.     explosion_countdown -= 1
  643.  
  644. def render_intro( canvas ):
  645.     render_stars( canvas )
  646.     render_deathstar( canvas )
  647.     render_intro_text( canvas )
  648.  
  649. def update_time():
  650.     global last_time, message_delay, fps
  651.     t = time.time()
  652.     if last_time > 0:
  653.         delta = ( t - last_time )
  654.         fps = 1.0 / delta
  655.         if message_delay > 0:
  656.             message_delay -= delta
  657.     last_time = t
  658.  
  659. def render( canvas ):
  660.     update_time()
  661.     if game_mode == MODE_GAME:
  662.         render_game( canvas )
  663.     elif game_mode == MODE_VICTORY:
  664.         render_victory( canvas )
  665.     elif game_mode == MODE_INTRO:
  666.         render_intro( canvas )
  667.  
  668. def key_event( key, down ):
  669.     global game_mode, violent_death
  670.  
  671.     if game_mode == MODE_GAME:
  672.         factor = 1 if down else 0
  673.         if key == simplegui.KEY_MAP[ "left" ]:
  674.             acc[ 0 ] = -ACCELERATION_MSS * factor
  675.         if key == simplegui.KEY_MAP[ "right" ]:
  676.             acc[ 0 ] = ACCELERATION_MSS * factor
  677.         if key == simplegui.KEY_MAP[ "up" ]:
  678.             acc[ 1 ] = -ACCELERATION_MSS * factor
  679.         if key == simplegui.KEY_MAP[ "down" ]:
  680.             acc[ 1 ] = ACCELERATION_MSS * factor
  681.         if key == simplegui.KEY_MAP[ "space" ]:
  682.             launch_proton_torpedoes()
  683.     elif down and game_mode == MODE_INTRO:
  684.         if key == simplegui.KEY_MAP[ "space" ]:
  685.             init_game()
  686.     elif down and game_mode == MODE_VICTORY and explosion_countdown <= 0:
  687.         game_mode = MODE_INTRO
  688.     if down and key == simplegui.KEY_MAP[ "Q" ]:
  689.         violent_death = not violent_death
  690.  
  691. def key_down( key ):
  692.     key_event( key, True )
  693.  
  694. def key_up( key ):
  695.     key_event( key, False )
  696.  
  697. create_stars()
  698.  
  699. # Create a frame and assign callbacks to event handlers
  700. frame = simplegui.create_frame( "Star Wars", CANVAS_WIDTH, CANVAS_HEIGHT )
  701. frame.set_draw_handler( render )
  702. frame.set_keydown_handler( key_down )
  703. frame.set_keyup_handler( key_up )
  704.  
  705. # Start the frame animation
  706. frame.start()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement