3 # Orange Guy's Quest mainfile
5 #Contains code from pymike's public domain collision and camera tutorials
6 #Pymike's website: http://pymike.pynguins.com/
7 #This code has been heavily modified and is unrecognizable unless you look
10 # Copyright (c) 2011-2020 Philip Pavlick
12 # <swashdev@pm.me> wrote this file. Feel free to do whatever you want
13 # with it so long as you don't hold me liable for any damages; there is no
14 # warranty. In exchange, if you ever find yourself thinking "I can't do
15 # this," or "I'll never be that good," I want you to stop, take a deep breath,
16 # and say "Yes I can." Then prove you can. Don't prove it to me; don't prove
17 # it to your friends and family; don't prove it to your boss; prove it to
18 # yourself. This software is already free; now free yourself.
21 # For more information about the rationale behind this licensing, see
22 # https://www.pavlick.net/fyl/
28 from pygame
.locals import *
30 print "Welcome to Orange Guy's Quest, version 1.3, by Philip Pavlick."
32 ##########################
33 # Configuration options: #
34 ##########################
36 # Set LEVELPATH to the file where you have your levels saved. By default,
37 # this is ./orange-levels. Don't set it to ./extraLevels.txt because those
39 # Can be set with -l <file> or --level <file> in the command line
40 LEVELPATH
= "orange-levels"
42 # Much like LEVELPATH, determines the path for the introductory levels.
43 # Can be accessed with --help, -?, or --intro, but should not be modified.
44 INTROLEVELS
= "help.txt"
46 # WINDOWS changes whether \ or / are used for directory trees. You shouldn't
47 # need to change this unless your system is only able to use \. Most versions
48 # of windows will run just fine with only /.
51 # Set USERECTS to True to force the old-school Atari2600-style blocky
52 # graphics or False to use the Retro pixel graphics. Any other value will
54 # Can be set to False with -r or -s, or True with -a or -b, in the command line
57 # Set these to the pixel values you want the game to use for the screen. Note
58 # that the game was originally programmed for a 640x480 window, and it looks
59 # just fine in that resolution.
60 # Can be set with -W in the command line, or individually with -x or -w for
61 # WIN_X and -y or -h for WIN_Y
65 # Set BIGPLAYER to True to restore the player's original large hitbox. This is
66 # not recommended, but I thought people might get a chuckle out of this.
67 # Can be set with -B in the command line
70 # Set this to True if you want annoying debug messages in your console.
71 # Can be set with -D in the command line
74 ##########################
75 # Pre-code configuration #
76 ##########################
82 while len( vars ) > 0:
83 global USERECTS
,LEVELPATH
,BIGPLAYER
,DEBUG
87 elif opt
== "-b" or opt
== '-a':
94 WIN_X
= int(vars.pop(0))
95 WIN_Y
= int(vars.pop(0))
96 elif opt
== "-x" or opt
== "-w":
97 WIN_X
= int(vars.pop(0))
98 elif opt
== "-y" or opt
== "-h":
99 WIN_Y
= int(vars.pop(0))
100 elif opt
== "-l" or opt
== "--levels":
102 LEVELPATH
= vars.pop(0)
104 raise SystemExit, "Not enough parameters for --levels: Must specify a file."
105 elif opt
in ["-?", "--help", "--levels"]:
106 LEVELPATH
= INTROLEVELS
113 while USERECTS
not in [True,False]:
114 print "Use [R]etro or [A]ncient graphics? [R/A]"
115 USERECTS
=raw_input("[\'\b").upper()
122 #os.environ["SDL_VIDEO_CENTERED"] = "1"
126 pygame
.display
.set_caption("Orange Guy's Quest!!")
127 screen
= pygame
.display
.set_mode((WIN_X
, WIN_Y
))
129 print "Create: PyGame screen"
131 #pygame.display.toggle_fullscreen()
133 ##################################
134 # Pre-code variable declarations #
135 ##################################
137 clock
= pygame
.time
.Clock()
138 walls
= [] # List to hold the walls
144 tele_rect
=pygame
.Rect(0,0,16,16)
151 # Important: See ``fonts/readme-proggy.txt'' for the Proggy license
152 font
= pygame
.font
.Font("fonts" + d
+ "ProggySquareSZ.ttf", 24)
157 # Sprite declarations:
160 ######################
161 # Class declarations #
162 ######################
164 # Class for the orange dude
165 class Player(object):
175 self
.rect
= pygame
.Rect(32, 32, 8, 16)
177 self
.rect
= pygame
.Rect(32, 32, 16, 16)
179 self
.sprite
= orange_images
.get_sprite( "player", d
, DEBUG
)
181 def move(self
, dx
, dy
):
183 # Move each axis separately. Note that this checks for collisions both times.
185 self
.move_single_axis(dx
, 0)
187 self
.move_single_axis(0, dy
)
189 def move_single_axis(self
, dx
, dy
):
195 # If you collide with a wall, move out based on velocity
197 if self
.rect
.colliderect(wall
.rect
):
198 if wall
.ID
not in ["Enemy","spike","Key","Portal","Destination"]:
199 if dx
> 0: # Moving right; Hit the left side of the wall
200 self
.rect
.right
= wall
.rect
.left
201 if dx
< 0: # Moving left; Hit the right side of the wall
202 self
.rect
.left
= wall
.rect
.right
203 if dy
> 0: # Moving down; Hit the top side of the wall
204 self
.rect
.bottom
= wall
.rect
.top
205 self
.jump
=False#Edited by me to simulate landing
209 #if wall.ID=="Mover":
210 # self.rect.x+=wall.direction
211 if dy
< 0: # Moving up; Hit the bottom side of the wall
212 self
.rect
.top
= wall
.rect
.bottom
215 elif wall
.ID
in ["Enemy","spike"]:
217 print "Event: Player killed by "+wall
.ID
219 text
= font
.render("You are dead. Press BACKSPACE to restart.", 1, (255, 128, 0))
220 textpos
= text
.get_rect(centerx
=screen
.get_width()/2)
224 if object.ID
=="Door":
225 if object.Color
==wall
.Color
:
227 elif wall
.ID
== "Portal":
229 print "Event: Player touched a portal"
231 if object.ID
=="Destination":
232 self
.rect
= object.rect
234 print "Zzzap! Sent player to", self
.rect
.x
, self
.rect
.y
236 # Nice class to hold a wall rect
241 def __init__(self
, pos
):
244 self
.rect
= pygame
.Rect(pos
[0], pos
[1], 16, 16)
253 def __init__(self
,coord
,direction
):
256 super(Mover
,self
).__init
__(coord
)
258 if self
.direction
==1:
259 if self
.difference
<self
.maxDiff
*16:
266 if self
.difference
>-(self
.maxDiff
*16):
273 dx
= self
.h
* self
.direction
274 dy
= self
.v
* self
.direction
276 # If you collide with a wall, move out based on velocity
278 if wall
.ID
=="Door" and self
.rect
.colliderect(wall
.rect
):
279 if dx
> 0: # Moving right; Hit the left side of the wall
280 self
.rect
.right
= wall
.rect
.left
282 if dx
< 0: # Moving left; Hit the right side of the wall
283 self
.rect
.left
= wall
.rect
.right
285 if dy
> 0: # Moving down; Hit the top of the wall
286 self
.rect
.bottom
= wall
.rect
.top
289 self
.rect
.top
= wall
.rect
.bottom
291 #if wall.ID=="Player" and self.rect.colliderect(wall.rect):
292 #wall.move(self.direction,1)
296 openx
=1#1 for right, -1 for left, 0 for none
297 openy
=1#1 for down, -1 for up, 0 for none
303 self
.rect
.x
+=(16*self
.openx
)
304 self
.rect
.y
+=(16*self
.openy
)
307 print "Event: Open door"
308 def __init__(self
, pos
,openx
,openy
,color
="green"):
309 if color
.lower()=="red":
310 self
.color
=(255,255,255)
312 self
.sprite
= "HIDDEN"
317 self
.sprite
= orange_images
.get_sprite( "door", d
, DEBUG
)
318 self
.rect
=pygame
.Rect(pos
[0],pos
[1],16,16)
326 def __init__(self
,pos
,color
="green"):
330 self
.sprite
= orange_images
.get_sprite( "key2", d
, DEBUG
)
331 self
.color
=(255,255,0)
335 self
.sprite
= orange_images
.get_sprite( "key", d
, DEBUG
)
338 super(Key
,self
).__init
__(pos
)
344 class Destination(Wall
):
346 def __init__(self
, pos
):
350 self
.rect
= pygame
.Rect(pos
[0], pos
[1], 16, 16)
352 self
.rect
= pygame
.Rect(pos
[0], pos
[1], 8, 16)
364 def __init__(self
,x
,y
,size1
,size2
,direction
,maximum
=5,type="random"):
365 self
.rect
=pygame
.Rect(x
,y
,size1
,size2
)
366 if direction
not in [-1,1]:
367 self
.direction
=random
.choice([-1,1])
369 self
.direction
=direction
371 maximum
=random
.randrange(6)
382 self
.sprite
= orange_images
.get_sprite( \
383 random
.choice( ["rat","bat"] ), d
, DEBUG
)
385 self
.sprite
= orange_images
.get_sprite( type.lower(), d
, DEBUG
)
390 # Holds the level layout in a list of strings.
395 global walls
,player
,end_rect
,lvlExit
,end_rect_offset
,tele_rect
,camera
397 print "Work: Building level"
398 # Parse the level string above. W = wall, E = exit
402 for row
in self
.level
:
407 print "Work: Message collection stopped... final \
408 message:\n\"" + self
.Message
+ "\""
415 print "Comment found--ignoring following text"
424 end_rect
= pygame
.Rect(x
, y
, 16, 16)
426 lvlExit
= orange_images
.get_sprite( "exit", d
, DEBUG
)
429 print "Event: Place ending object at", x
, y
431 end_rect
= pygame
.Rect(x
+ 4, y
, 8, 16)
433 lvlExit
= orange_images
.get_sprite( "end", d
, DEBUG
)
436 print "Event: Place ending object at", x
, y
440 key
=Key((x
,y
),color
="red")
446 door
=Door((x
,y
),0,-1,color
=Color
)
453 door
=Door((x
,y
),0,1,color
=Color
)
460 door
=Door((x
,y
),-1,0,color
=Color
)
467 door
=Door((x
,y
),1,0,color
=Color
)
474 door
=Door((x
,y
),-1,-1,color
=Color
)
481 door
=Door((x
,y
),1,-1,color
=Color
)
488 door
=Door((x
,y
),-1,1,color
=Color
)
495 door
=Door((x
,y
),1,1,color
=Color
)
500 Destination( (x
, y
) )
505 print "Event: Place player object at", x
, y
507 mine_rect
=Enemy(x
,y
,16,16,2,type="rat")
508 walls
.append(mine_rect
)
510 mine_rect
=Enemy(x
,y
,16,16,2,type="ratking")
511 mine_rect
.species
= "ratking"
512 walls
.append(mine_rect
)
514 mine_rect
=Enemy(x
,y
,16,16,2,type="bat")
515 walls
.append(mine_rect
)
517 spike
=Spike(x
,y
,16,16,2,type="spike")
520 tele_rect
=pygame
.Rect(x
,y
,16,16)
523 print "Work: Message found in level; collecting..."
531 if len( self
.Message
) > 0:
532 global text
, textpos
, message_delay
, font
533 text
= font
.render(self
.Message
, 1, (255, 128, 0))
534 textpos
= text
.get_rect(centerx
=screen
.get_width()/2)
535 if len( self
.Message
) > 10:
536 message_delay
= 500 + (10 * (len( self
.Message
) - 10))
543 def __init__(self
,lObjects
,Message
=""):
547 print "Create: Level object"
550 # Returns a new rect for drawing by the camera's offset.
552 return pygame
.Rect(rect
.x
- camera
.x
, rect
.y
- camera
.y
,
555 #########################################
556 # Final initializations before mainloop #
557 #########################################
562 levels
= orange_levels
.get_levels( LEVELPATH
, DEBUG
)
566 print "Work: Passing level strings into level list"
567 for level_string
in levels
:
568 Levels
.append( Level( level_string
) )
570 if len( Levels
) <= 0:
571 raise SystemExit, "Did not find any valid levels in ." + d
+ LEVELPATH
+ \
572 "\nCheck ." + d
+ "doc" + d
+ "levels.txt for help"
574 player
= Player() # Create the player
576 # This is the camera, simply a rect.
577 camera
= pygame
.Rect(0, 0, screen
.get_width(), screen
.get_height())
579 print "Create: camera"
583 player_rect_offset
= -4
585 player_rect_offset
= 0
587 # Build the first level from data
595 quitMsg
= "Thanks for playing!"
598 global jumpDY
,text
,message_delay
602 for e
in pygame
.event
.get():
603 if e
.type == pygame
.QUIT
or (e
.type == pygame
.KEYDOWN
and e
.key
== pygame
.K_ESCAPE
):
605 elif e
.type == pygame
.KEYDOWN
and e
.key
== pygame
.K_BACKSPACE
:
607 print "Event: Level reset"
611 walls
.remove(walls
[0])
614 Levels
[levelIndex
].create()
620 # Move the player if an arrow key is pressed
621 key
= pygame
.key
.get_pressed()
622 if key
[pygame
.K_LEFT
]:
624 if key
[pygame
.K_RIGHT
]:
626 if not player
.dJump
:#Edited to simulate jumping
629 player
.jump
=True#Edited to simulate jumping
635 player
.move(0, jumpDY
)
638 camera
.x
=player
.rect
.x
-int(screen
.get_width()/2)
639 camera
.y
=player
.rect
.y
-int(screen
.get_height()/2)
641 # Just added this to make it slightly fun ;)
642 if player
.rect
.colliderect(end_rect
) or player
.rect
.colliderect(tele_rect
):
647 walls
.remove(walls
[0])
654 print "Event: Level win"
655 if levelIndex
< len( Levels
):
656 Levels
[levelIndex
].create()
658 # A special message if you've gotten the sword (the sword doesn't
659 # exist yet in the game, so this just displays when you win)
665 screen
.fill((0, 0, 0))
668 # Translate the object's rect to the camera's offset for drawing
669 if wall
.rect
.right
>= camera
.left \
670 and wall
.rect
.left
<= camera
.right \
671 and wall
.rect
.bottom
>= camera
.top \
672 and wall
.rect
.top
<= camera
.bottom
:
676 wall
.rect
= translate(wall
.rect
)
677 if wall
.ID
in ["Enemy","Mover"]:
679 if wall
.ID
!= "Destination":
680 if USERECTS
==True or wall
.ID
== "Portal":
681 pygame
.draw
.rect(screen
,wall
.color
,wall
.rect
)
682 if wall
.ID
== "Portal":
683 pygame
.draw
.rect( screen
, (0, 0, 0), \
684 pygame
.Rect( wall
.rect
.x
+ 4, wall
.rect
.y
+ 4, \
685 wall
.rect
.w
- 8, wall
.rect
.h
- 8 ) )
686 if wall
.ID
== "Enemy":
687 if wall
.species
== "ratking":
688 pygame
.draw
.rect( screen
, (255, 255, 0), \
689 pygame
.Rect( wall
.rect
.x
+ 4, wall
.rect
.y
- 4, 8, 4 ) )
692 if wall
.ID
in ["Door","Key","spike","Enemy"] and draw
:
693 if wall
.sprite
!="HIDDEN":
694 if wall
.ID
== "Enemy" and wall
.species
== "ratking":
695 screen
.blit(wall
.sprite
,(wall
.rect
.x
,wall
.rect
.y
- 1))
697 screen
.blit(wall
.sprite
,(wall
.rect
.x
,wall
.rect
.y
))
699 pygame
.draw
.rect(screen
,(255,255,255),wall
.rect
)
700 elif draw
and wall
.ID
in ["Wall","Mover"]:
701 pygame
.draw
.rect(screen
, (255, 255, 255), wall
.rect
)
703 player
.rect
=translate(player
.rect
)
704 tele_rect
=translate(tele_rect
)
705 end_rect
=translate(end_rect
)
707 pygame
.draw
.rect(screen
,(255,255,255),tele_rect
)
709 screen
.blit(lvlExit
,(end_rect
.x
+ end_rect_offset
,end_rect
.y
))
711 screen
.blit(player
.sprite
, \
712 (player
.rect
.x
+ player_rect_offset
,player
.rect
.y
))
714 screen
.blit(orange_images
.get_sprite( "dead", d
, DEBUG
), \
715 (player
.rect
.x
+ player_rect_offset
,player
.rect
.y
))
717 pygame
.draw
.rect(screen
,(255,0,0),end_rect
)
719 pygame
.draw
.rect(screen
,(255,128,0),player
.rect
)
721 pygame
.draw
.rect(screen
,(255,128,0), \
722 pygame
.Rect(player
.rect
.x
, player
.rect
.y
+8, 16, 8))
725 # We are guaranteed by the player's `update' function that `text' here
726 # has been set, as has `textpos'
727 screen
.blit( text
, textpos
)
728 elif text
!= None and message_delay
> 0:
729 screen
.blit(text
,textpos
)
732 pygame
.display
.flip()
740 screen
.fill((0, 0, 0))
742 text
= font
.render( "Congratulations!", 1, (255, 128, 0) )
743 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
746 screen
.blit( text
, textpos
)
748 text
= font
.render( "You've recovered the legendary sword", 1, (255, 128, 0) )
749 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
752 screen
.blit( text
, textpos
)
754 text
= font
.render( "of the Dragon King!", 1, (255, 128, 0) )
755 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
758 screen
.blit( text
, textpos
)
760 text
= font
.render( "You've reached the end of the game.", 1, (255, 128, 0) )
761 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
764 screen
.blit( text
, textpos
)
766 text
= font
.render( "Press ESCAPE to finish.", 1, (255, 128, 0) )
767 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
770 screen
.blit( text
, textpos
)
772 text
= font
.render( "Original game by Philip Pavlick", 1, (255, 128, 0) )
773 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
776 screen
.blit( text
, textpos
)
778 text
= font
.render( "Special thanks to my family and friends", 1, (255, 128, 0) )
779 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
782 screen
.blit( text
, textpos
)
784 text
= font
.render( "who got a kick out of this dumb computer game", 1, (255, 128, 0) )
785 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
788 screen
.blit( text
, textpos
)
790 text
= font
.render( "I wrote ages ago", 1, (255, 128, 0) )
791 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
794 screen
.blit( text
, textpos
)
796 text
= font
.render( "(seriously, this source code is terrible!)", 1, (255, 128, 0) )
797 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
800 screen
.blit( text
, textpos
)
802 text
= font
.render( "For more and better games,", 1, (255, 128, 0) )
803 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
806 screen
.blit( text
, textpos
)
808 text
= font
.render( "check back at my website at", 1, (255, 128, 0) )
809 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
812 screen
.blit( text
, textpos
)
814 text
= font
.render( "www.pavlick.net", 1, (255, 128, 0) )
815 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
818 screen
.blit( text
, textpos
)
820 text
= font
.render( "or", 1, (255, 128, 0) )
821 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
824 screen
.blit( text
, textpos
)
826 text
= font
.render( "swashdev.github.io", 1, (255, 128, 0) )
827 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
830 screen
.blit( text
, textpos
)
832 text
= font
.render( "Thank you so much for playing!", 1, (255, 128, 0) )
833 textpos
= text
.get_rect( centerx
= screen
.get_width() / 2 )
836 screen
.blit( text
, textpos
)
841 pygame
.draw
.rect( screen
, player
.color
, \
842 pygame
.Rect( screen
.get_width() / 2, 144, \
845 pygame
.draw
.rect( screen
, player
.color
, \
846 pygame
.Rect( screen
.get_width() / 2, 144, \
849 pygame
.draw
.rect( screen
, (255, 0, 0), \
850 pygame
.Rect( (screen
.get_width() / 2) - 6, 134, \
855 screen
.blit( orange_images
.get_sprite( "won" ), \
856 pygame
.Rect( (screen
.get_width() / 2) - 6, 134, \
859 pygame
.display
.flip()
865 for e
in pygame
.event
.get():
866 if e
.type == pygame
.QUIT
or (e
.type == pygame
.KEYDOWN
and e
.key
== pygame
.K_ESCAPE
):