Removed old file
[2dworld.git] / engine / game.py
blob41086f2ddcdb6b3ce5f84f20038cd08435b92167
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 import sys
5 import os
6 import math
7 import glob
8 import re
10 import pygame
12 try:
13 import _path
14 except:
15 pass
17 import tiledtmxloader
19 import actor
20 from hero import avatar_hero
22 # -----------------------------------------------------------------------------
24 def start(screen, map_filename, hero):
25 """
26 Use the paralax scrolling feature.
27 """
29 # parser the map (it is done here to initialize the
30 # window the same size as the map if it is small enough)
31 world_map = tiledtmxloader.tmxreader.TileMapParser().parse_decode(map_filename)
33 print world_map.properties
35 pygame.display.set_caption("Map viewer: " + map_filename + " ( " + str(world_map.pixel_width) +" x "+ str(world_map.pixel_height) + " )")
37 screen_width, screen_height = screen.get_size()
38 hero.moveto(1024, 768)
40 # load the images using pygame
41 resources = tiledtmxloader.helperspygame.ResourceLoaderPygame()
42 resources.load(world_map)
44 # prepare map rendering
45 assert world_map.orientation == "orthogonal"
47 # renderer
48 renderer = tiledtmxloader.helperspygame.RendererPygame()
50 # get position of hero
51 hero_pos_x = hero.rect.centerx
52 hero_pos_y = hero.rect.bottom
54 # dimensions of the hero for collision detection
55 hero_width = hero.rect.width
56 hero_height = 5
58 # cam_offset is for scrolling
59 cam_world_pos_x = hero.rect.centerx
60 cam_world_pos_y = hero.rect.centery
62 # set initial cam position and size
63 renderer.set_camera_position_and_size(cam_world_pos_x, cam_world_pos_y, \
64 screen_width, screen_height)
66 # retrieve the layers
67 sprite_layers = tiledtmxloader.helperspygame.get_layers_from_map(resources)
69 # filter layers
70 sprite_layers = [layer for layer in sprite_layers if not layer.is_object_group]
72 # detect metadata layer
73 metadata_layer = None
74 layer_num = 0
75 for layer in world_map.layers:
76 if not layer.is_object_group:
77 if layer.properties.has_key('metalayer'):
78 metadata_layer = layer
79 if layer.properties.has_key('player'):
80 hero_layer = layer_num
81 layer_num += 1
83 # add the hero the the right layer, it can be changed using 0-9 keys
84 sprite_layers[hero_layer].add_sprite(hero)
86 # create dictionary with the properties of every tile, indexed by gid
87 tile_properties = {}
88 for tile_set in world_map.tile_sets:
89 for tile in tile_set.tiles:
90 tile_properties[int(tile_set.firstgid) + int(tile.id)] = tile.properties
91 print tile_properties
93 # layer add/remove hero keys
94 num_keys = [pygame.K_0, pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, \
95 pygame.K_5, pygame.K_6, pygame.K_7, pygame.K_8, pygame.K_9]
97 # variables for the main loop
98 clock = pygame.time.Clock()
99 running = True
100 speed = 0.075 * 2.
101 # set up timer for fps printing
102 pygame.time.set_timer(pygame.USEREVENT, 1000)
104 speed_factor = 1.0
105 action_type = actor.WALK
107 # mainloop
108 while running:
109 dt = clock.tick()
111 # event handling
112 for event in pygame.event.get():
113 if event.type == pygame.QUIT:
114 running = False
115 elif event.type == pygame.USEREVENT:
116 print("fps: ", clock.get_fps())
117 elif event.type == pygame.KEYDOWN:
118 if event.key == pygame.K_ESCAPE:
119 running = False
120 elif event.key in num_keys:
121 # find out which layer to manipulate
122 idx = num_keys.index(event.key)
123 # make sure this layer exists
124 if idx < len(world_map.layers):
125 if sprite_layers[idx].contains_sprite(hero):
126 sprite_layers[idx].remove_sprite(hero)
127 print("removed hero sprite from layer", idx)
128 else:
129 sprite_layers[idx].add_sprite(hero)
130 print("added hero sprite to layer", idx)
131 else:
132 print("no such layer or more than 10 layers: " + str(idx))
133 elif event.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
134 speed_factor = 2.0
135 action_type = actor.RUN
136 elif event.type == pygame.KEYUP:
137 if event.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
138 speed_factor = 1.0
139 action_type = actor.WALK
141 # find directions
142 direction_x = pygame.key.get_pressed()[pygame.K_RIGHT] - \
143 pygame.key.get_pressed()[pygame.K_LEFT]
144 direction_y = pygame.key.get_pressed()[pygame.K_DOWN] - \
145 pygame.key.get_pressed()[pygame.K_UP]
147 # make sure the hero moves with same speed in all directions (diagonal!)
148 dir_len = math.hypot(direction_x, direction_y)
149 dir_len = dir_len if dir_len else 1.0
151 # update position
152 step_x = speed_factor * speed * dt * direction_x / dir_len
153 step_y = speed_factor * speed * dt * direction_y / dir_len
154 if metadata_layer is not None:
155 try:
156 step_x, step_y = check_collision(hero_pos_x, hero_pos_y, step_x, step_y, hero_width, hero_height, metadata_layer, tile_properties)
157 except:
158 pass
159 hero_pos_x += step_x
160 hero_pos_y += step_y
161 hero.rect.midbottom = (hero_pos_x, hero_pos_y)
162 hero.update(step_x, step_y, action_type)
164 # adjust camera according to the hero's position, follow him
165 # (don't make the hero follow the cam, maybe later you want different
166 # objects to be followd by the cam)
167 renderer.set_camera_position(hero.rect.centerx, hero.rect.centery)
169 # clear screen, might be left out if every pixel is redrawn anyway
170 screen.fill((0, 0, 0))
172 # render the map
173 for sprite_layer in sprite_layers:
174 if sprite_layer.is_object_group:
175 # we dont draw the object group layers
176 # you should filter them out if not needed
177 continue
178 else:
179 renderer.render_layer(screen, sprite_layer)
181 pygame.display.flip()
183 # -----------------------------------------------------------------------------
185 def check_collision(hero_pos_x, hero_pos_y, step_x, step_y, \
186 hero_width, hero_height, metadata_layer, tile_properties):
188 Checks collision of the hero against the world. Its not the best way to
189 handle collision detection but for this demo it is good enough.
191 :Returns: steps to add to heros current position.
193 # create hero rect
194 hero_rect = pygame.Rect(0, 0, hero_width, hero_height)
195 hero_rect.midbottom = (hero_pos_x, hero_pos_y)
197 # find the tile location of the hero
198 tile_x = int((hero_pos_x) // metadata_layer.tilewidth)
199 tile_y = int((hero_pos_y) // metadata_layer.tileheight)
201 # find the tiles around the hero and extract their rects for collision
202 tile_rects = []
203 for diry in (-1, 0 , 1):
204 for dirx in (-1, 0, 1):
205 gid = metadata_layer.content2D[tile_x + dirx][tile_y + diry]
206 if gid in tile_properties:
207 tile_width = metadata_layer.tilewidth
208 tile_pos_x = (tile_x + dirx) * tile_width
209 tile_height = metadata_layer.tileheight
210 tile_pos_y = (tile_y + diry) * tile_height
211 tile_rect = pygame.Rect(tile_pos_x, tile_pos_y, tile_width, tile_height)
212 tile_rects.append(tile_rect)
214 # save the original steps and return them if not canceled
215 res_step_x = step_x
216 res_step_y = step_y
218 # x direction, floor or ceil depending on the sign of the step
219 step_x = special_round(step_x)
221 # detect a collision and dont move in x direction if colliding
222 if hero_rect.move(step_x, 0).collidelist(tile_rects) > -1:
223 res_step_x = 0
225 # y direction, floor or ceil depending on the sign of the step
226 step_y = special_round(step_y)
228 # detect a collision and dont move in y direction if colliding
229 if hero_rect.move(0, step_y).collidelist(tile_rects) > -1:
230 res_step_y = 0
232 # return the step the hero should do
233 return res_step_x, res_step_y
235 # -----------------------------------------------------------------------------
237 def special_round(value):
239 For negative numbers it returns the value floored,
240 for positive numbers it returns the value ceiled.
242 # same as: math.copysign(math.ceil(abs(x)), x)
243 # OR:
244 # ## versus this, which could save many function calls
245 # import math
246 # ceil_or_floor = { True : math.ceil, False : math.floor, }
247 # # usage
248 # x = floor_or_ceil[val<0.0](val)
250 if value < 0:
251 return math.floor(value)
252 return math.ceil(value)