├── .gitattributes
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── assets
├── crunch.wav
├── effect_death.png
├── effect_death.txt
├── effect_vanish.png
├── effect_vanish.txt
├── enemy_eagle.png
├── enemy_eagle.txt
├── enemy_opossum.png
├── enemy_opossum.txt
├── hero.png
├── hero.txt
├── hero_alt.act
├── hurt.wav
├── jump.wav
├── layer_background.png
├── layer_background.tmx
├── layer_background.tsx
├── layer_foreground.png
├── layer_foreground.tmx
├── layer_foreground.tsx
├── objects.png
├── objects.tsx
├── pickup.wav
├── score.png
├── score.txt
├── ui.png
├── ui.tsx
└── vulture.wav
├── screenshot.png
└── src
├── UI.py
├── actor.py
├── eagle.py
├── effect.py
├── game.py
├── opossum.py
├── platformer.py
├── player.py
├── raster_effect.py
├── rectangle.py
├── score.py
├── sound.py
├── tilengine.py
└── world.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # Windows image file caches
7 | Thumbs.db
8 | ehthumbs.db
9 |
10 | # Folder config file
11 | Desktop.ini
12 |
13 | # Recycle Bin used on file shares
14 | $RECYCLE.BIN/
15 |
16 | *.lnk
17 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - 2.7
4 | - 3.6
5 | install:
6 | - sudo cp lib/libTilengine.so /usr/lib/x86_64-linux-gnu
7 | - sudo chmod a+x /usr/lib/x86_64-linux-gnu/libTilengine.so
8 | - sudo apt install libsdl2-dev libsdl2-mixer-dev
9 | - pip install pysdl2
10 | script:
11 | - python platformer.py
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Marc Palacios (megamarc)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tilengine python platformer
2 | This project aims to teach actual game mechanics using the free, cross-platform [Tilengine retro graphics engine](http://www.tilengine.org) under python.
3 |
4 | 
5 |
6 | ## Features
7 | The features implemented so far are:
8 | * Two layer parallax scrolling
9 | * Sprite and tileset animations
10 | * Raster effects for sky gradient color, cloud motion, sea water *linescroll* and hills movement on a single background layer
11 | * Basic list of game entities (actors)
12 | * Three character states: idle, running and jumping
13 | * Tileset attributes in Tiled editor: *type* and *priority*
14 | * Player/level interaction: the player can jump on platforms, get blocked by walls and pick gems
15 | * Inertial control and acceleration
16 | * Slopes
17 | * Active game entities management
18 | * Define game entities (enemies, etc) inside tmx object layer and load into a list
19 | * Enemy behavior and spawn active enemies from loaded entities list
20 | * Enemies can hurt player and make it bounce
21 | * Basic sound effects with SDL_Mixer library
22 |
23 | ## Prerequisites
24 | This project depends on three external components that must be installed separately:
25 |
26 | ### Tilengine
27 | http://www.tilengine.org
28 |
29 | Each supported platform has its own methods for build or install binaries, please follow method of your own platform.
30 |
31 | ### SDL2 and SDL2_Mixer
32 | https://www.libsdl.org/
33 |
34 | SDL2 (Simple DirectMedia Layer) is an open-source, cross-platform library for managing windows, user input, graphics and sound. Both tilengine and this project use SDL2 internally. You must install the runtime binaries into your system.
35 |
36 | **Windows and OSX:**
37 |
38 | Download prebuilt binaries here:
39 |
40 | https://www.libsdl.org/download-2.0.php
41 |
42 | https://www.libsdl.org/projects/SDL_mixer/
43 |
44 | **Debian-based linux:**
45 |
46 | Open a terminal window and install directly from package manager:
47 | ```
48 | sudo apt install libsdl2-2.0-0 libsdl2-mixer-2.0-0
49 | ```
50 |
51 | ### SDL2 python binding
52 | http://pysdl2.readthedocs.io
53 |
54 | You must also install the binding for using SDL2 from python language. From a terminal window type the following command:
55 | ```
56 | pip install pysdl2
57 | ```
58 |
59 | ## Source code organisation
60 | This is a breif overview about modules breakdown in `/src` directory
61 |
62 | |Module | Role
63 | |-------------------------------------------|---------------------
64 | |[`platformer.py`](src/platformer.py) | Main module: init and game loop
65 | |[`tilengine.py`](src/tilengine.py) | Tilengine binding for python
66 | |[`raster_effect.py`](src/raster_effect.py) | Tilengine raster effects for background
67 | |[`game.py`](src/game.py) | Game backbone, global instances
68 | |[`actor.py`](src/actor.py) | Base class for all game entities
69 | |[`player.py`](src/player.py) | Player class
70 | |[`opossum.py`](src/opossum.py) | Terrestrial enemy class
71 | |[`eagle.py`](src/eagle.py) | Flying enemy class
72 | |[`score.py`](src/score.py) | Pop-up animation of score class
73 | |[`effect.py`](src/effect.py) | Generic one-shot animation class
74 | |[`world.py`](src/world.py) | World/level class
75 | |[`UI.py`](src/ui.py) | HUD UI class (score, time...)
76 | |[`rectangle.py`](src/rectangle.py) | Simple helper class for rectangles
77 | |[`sound.py`](src/sound.py) | Sound effects manager
78 |
79 | ## Acknowledge
80 | Graphic assets are copyrighted and owned by their original authors
81 | * Backgrounds created by ansimuz: https://ansimuz.itch.io/magic-cliffs-environment
82 | * Player character created by Jesse M: https://jesse-m.itch.io/jungle-pack
83 |
--------------------------------------------------------------------------------
/assets/crunch.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/crunch.wav
--------------------------------------------------------------------------------
/assets/effect_death.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/effect_death.png
--------------------------------------------------------------------------------
/assets/effect_death.txt:
--------------------------------------------------------------------------------
1 | death-1,0,0,40,41
2 | death-2,40,0,40,41
3 | death-3,0,41,40,41
4 | death-4,40,41,40,41
5 | death-5,80,0,40,41
6 | death-6,80,41,40,41
--------------------------------------------------------------------------------
/assets/effect_vanish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/effect_vanish.png
--------------------------------------------------------------------------------
/assets/effect_vanish.txt:
--------------------------------------------------------------------------------
1 | vanish1,0,0,16,16
2 | vanish2,16,0,16,16
3 | vanish3,32,0,16,16
4 | vanish4,48,0,16,16
5 | vanish5,64,0,16,16
6 | vanish6,80,0,16,16
7 | vanish7,96,0,16,16
8 | vanish8,112,0,16,16
9 |
--------------------------------------------------------------------------------
/assets/enemy_eagle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/enemy_eagle.png
--------------------------------------------------------------------------------
/assets/enemy_eagle.txt:
--------------------------------------------------------------------------------
1 | fly1,0,0,40,40
2 | fly2,40,0,40,40
3 | fly3,80,0,40,40
4 | fly4,120,0,40,40
5 |
--------------------------------------------------------------------------------
/assets/enemy_opossum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/enemy_opossum.png
--------------------------------------------------------------------------------
/assets/enemy_opossum.txt:
--------------------------------------------------------------------------------
1 | opossum-1,0,0,36,24
2 | opossum-2,0,24,36,24
3 | opossum-3,36,0,36,24
4 | opossum-4,36,24,36,24
5 | opossum-5,0,48,36,24
6 | opossum-6,36,48,36,24
--------------------------------------------------------------------------------
/assets/hero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/hero.png
--------------------------------------------------------------------------------
/assets/hero.txt:
--------------------------------------------------------------------------------
1 | idle1,0,0,24,36
2 | idle2,24,0,24,36
3 | idle3,48,0,24,36
4 | idle4,72,0,24,36
5 | idle5,96,0,24,36
6 | idle6,120,0,24,36
7 | idle7,144,0,24,36
8 | idle8,168,0,24,36
9 | idle9,0,36,24,36
10 | idle10,24,36,24,36
11 | idle11,48,36,24,36
12 | jump1,72,36,24,36
13 | jump2,96,36,24,36
14 | run1,0,72,24,36
15 | run2,24,72,24,36
16 | run3,48,72,24,36
17 | run4,72,72,24,36
18 | run5,96,72,24,36
19 | run6,120,72,24,36
20 | run7,144,72,24,36
21 | run8,168,72,24,36
22 |
--------------------------------------------------------------------------------
/assets/hero_alt.act:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/hero_alt.act
--------------------------------------------------------------------------------
/assets/hurt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/hurt.wav
--------------------------------------------------------------------------------
/assets/jump.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/jump.wav
--------------------------------------------------------------------------------
/assets/layer_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/layer_background.png
--------------------------------------------------------------------------------
/assets/layer_background.tmx:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/assets/layer_background.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/assets/layer_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/layer_foreground.png
--------------------------------------------------------------------------------
/assets/layer_foreground.tmx:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/assets/layer_foreground.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
--------------------------------------------------------------------------------
/assets/objects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/objects.png
--------------------------------------------------------------------------------
/assets/objects.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/assets/pickup.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/pickup.wav
--------------------------------------------------------------------------------
/assets/score.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/score.png
--------------------------------------------------------------------------------
/assets/score.txt:
--------------------------------------------------------------------------------
1 | inc_5,0,0,8,8
2 | dec_5,8,0,8,8
3 | add_1,16,0,8,8
4 |
--------------------------------------------------------------------------------
/assets/ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/ui.png
--------------------------------------------------------------------------------
/assets/ui.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/assets/vulture.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/assets/vulture.wav
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megamarc/TilenginePythonPlatformer/7e031e5ffd59006944dcc9a3d8719db9a5d8e82a/screenshot.png
--------------------------------------------------------------------------------
/src/UI.py:
--------------------------------------------------------------------------------
1 | """ UI HUD elements (time, score...) """
2 |
3 | from tilengine import Tileset, Tilemap, Tile
4 | import game
5 |
6 | class UI(object):
7 | """ UI elements """
8 | def __init__(self):
9 | self.cols = game.WIDTH//8
10 | self.layer = game.engine.layers[0]
11 | self.layer.setup(Tilemap.create(4, self.cols, None), Tileset.fromfile("ui.tsx"))
12 | self.layer.set_clip(0, 0, game.WIDTH, 32)
13 |
14 | def update_time(self, time):
15 | text = "{:03}".format(time)
16 | col = (self.cols - len(text)) // 2
17 | tile = Tile()
18 | for digit in text:
19 | base_index = int(digit)
20 | tile.index = base_index + 11
21 | self.layer.tilemap.set_tile(1, col, tile)
22 | tile.index = base_index + 21
23 | self.layer.tilemap.set_tile(2, col, tile)
24 | col += 1
25 |
--------------------------------------------------------------------------------
/src/actor.py:
--------------------------------------------------------------------------------
1 | """ Base class for all game entities (actors) """
2 |
3 | import game
4 |
5 | class Direction:
6 | """ player orientations """
7 | Right, Left = range(2)
8 |
9 | class Actor(object):
10 | """ Generic active game entity base class """
11 | spriteset = None
12 | def __init__(self, item_ref, x, y):
13 | self.x = x
14 | self.y = y
15 | self.sprite = game.engine.sprites[game.engine.get_available_sprite()]
16 | self.sprite.setup(self.spriteset)
17 | self.item = item_ref
18 | game.actors.append(self)
19 |
20 | def __del__(self):
21 | self.sprite.disable()
22 | if self.item is not None:
23 | self.item.alive = False
24 |
25 | def kill(self):
26 | """ definitive kill of active game entity, removing from spawn-able item list too """
27 | game.world.objects.remove(self.item)
28 | self.item = None
29 | game.actors.remove(self)
30 |
--------------------------------------------------------------------------------
/src/eagle.py:
--------------------------------------------------------------------------------
1 | """ Flying enemy, waves across screen """
2 |
3 | from math import sin, radians
4 | from tilengine import Spriteset, Sequence, Flags
5 | from actor import Actor, Direction
6 | import game
7 |
8 | class Eagle(Actor):
9 | """ Flying enemy """
10 | size = (40, 40)
11 | seq_fly = None
12 |
13 | def __init__(self, item_ref, x, y):
14 |
15 | # init class members once
16 | if Eagle.spriteset is None:
17 | Eagle.spriteset = Spriteset.fromfile("enemy_eagle")
18 | Eagle.seq_fly = Sequence.create_sprite_sequence(Eagle.spriteset, "fly", 6)
19 |
20 | Actor.__init__(self, item_ref, x, y)
21 | self.frame = 0
22 | self.base_y = y
23 | self.xspeed = -3
24 | self.direction = Direction.Left
25 | self.sprite.set_animation(Eagle.seq_fly, 0)
26 | self.collision_points = (4, 20, 36)
27 |
28 | def update(self):
29 | """ Update once per frame """
30 | self.x += self.xspeed
31 | self.y = self.base_y + int(sin(radians(self.frame*4))*15)
32 | self.frame += 1
33 | if self.frame is 10:
34 | game.sounds.play("eagle", 3)
35 | screen_x = self.x - game.world.x
36 |
37 | if self.direction is Direction.Left:
38 | if screen_x < 10:
39 | self.direction = Direction.Right
40 | self.xspeed = -self.xspeed
41 | self.sprite.set_flags(Flags.FLIPX)
42 | game.sounds.play("eagle", 3)
43 | else:
44 | for point in self.collision_points:
45 | game.player.check_hit(self.x, self.y + point, self.direction)
46 | else:
47 | if screen_x > 590:
48 | self.direction = Direction.Left
49 | self.xspeed = -self.xspeed
50 | self.sprite.set_flags(0)
51 | game.sounds.play("eagle", 3)
52 | else:
53 | for point in self.collision_points:
54 | game.player.check_hit(self.x + self.size[0], self.y + point, self.direction)
55 | self.sprite.set_position(screen_x, self.y)
56 | return True
57 |
--------------------------------------------------------------------------------
/src/effect.py:
--------------------------------------------------------------------------------
1 | """ Generic, reusable one-shot animation (explosions, vanish, smoke...) """
2 |
3 | from actor import Actor
4 | import game
5 |
6 | class Effect(Actor):
7 | """ placeholder for simple sprite effects """
8 | def __init__(self, x, y, spriteset, sequence):
9 | self.spriteset = spriteset
10 | Actor.__init__(self, None, x, y)
11 | self.sprite.set_animation(sequence, 1)
12 |
13 | def update(self):
14 | """ updates effect state once per frame """
15 | self.sprite.set_position(self.x - game.world.x, self.y)
16 | if self.sprite.get_animation_state() is False:
17 | return False
18 | return True
19 |
--------------------------------------------------------------------------------
/src/game.py:
--------------------------------------------------------------------------------
1 | """ Game backbone, shared instances used in other modules """
2 |
3 | # global constants
4 | WIDTH = 640 # framebuffer width
5 | HEIGHT = 360 # framebuffer height
6 | MAX_LAYERS = 3 # backgroudn layers
7 | MAX_SPRITES = 32 # max sprites
8 | ASSETS_PATH = "../assets"
9 |
10 | # global game objects, delayed creation
11 |
12 | engine = () # tilengine main instance
13 | window = () # tilengine window instance
14 | actors = () # list that contains every active game entity
15 | ui = () # UI items
16 | world = () # world/level instance
17 | player = () # player instance
18 | sounds = () # sound effects handler
19 |
--------------------------------------------------------------------------------
/src/opossum.py:
--------------------------------------------------------------------------------
1 | """ Terrestrial enemy, chases player on floor """
2 |
3 | from tilengine import Spriteset, Sequence, Flags
4 | from actor import Actor, Direction
5 | import game
6 |
7 | class Opossum(Actor):
8 | """ Floor enemy. Chases player in a 80 pixel radius """
9 | size = (36, 24)
10 | seq_walk = None
11 |
12 | def __init__(self, item_ref, x, y):
13 |
14 | # init class members once
15 | if Opossum.spriteset is None:
16 | Opossum.spriteset = Spriteset.fromfile("enemy_opossum")
17 | Opossum.seq_walk = Sequence.create_sprite_sequence(Opossum.spriteset, "opossum-", 6)
18 |
19 | Actor.__init__(self, item_ref, x, y)
20 | self.xspeed = -2
21 | self.direction = Direction.Left
22 | self.sprite.set_animation(Opossum.seq_walk, 0)
23 |
24 | def update(self):
25 | """ Update once per frame """
26 | self.x += self.xspeed
27 | if self.direction is Direction.Left:
28 | if self.x - game.player.x < -80:
29 | self.direction = Direction.Right
30 | self.xspeed = -self.xspeed
31 | self.sprite.set_flags(Flags.FLIPX)
32 | else:
33 | game.player.check_hit(self.x, self.y + self.size[1]//2, self.direction)
34 | else:
35 | if self.x - game.player.x > 80 and self.direction is Direction.Right:
36 | self.direction = Direction.Left
37 | self.xspeed = -self.xspeed
38 | self.sprite.set_flags(0)
39 | else:
40 | game.player.check_hit(self.x + self.size[0], self.y + self.size[1]//2, self.direction)
41 |
42 | self.sprite.set_position(self.x - game.world.x, self.y)
43 | return True
44 |
--------------------------------------------------------------------------------
/src/platformer.py:
--------------------------------------------------------------------------------
1 | """ Tilengine python platformer demo """
2 |
3 | from tilengine import Engine, Window
4 | from raster_effect import raster_effect
5 | from world import World
6 | from player import Player
7 | from UI import UI
8 | from sound import Sound
9 | import game
10 |
11 | # init tilengine
12 | game.engine = Engine.create(game.WIDTH, game.HEIGHT, game.MAX_LAYERS, game.MAX_SPRITES, 0)
13 | game.engine.set_load_path(game.ASSETS_PATH)
14 |
15 | # set raster callback
16 | game.engine.set_raster_callback(raster_effect)
17 |
18 | # init global game entities
19 | game.actors = list()
20 | game.world = World()
21 | game.player = Player()
22 | game.ui = UI()
23 |
24 | # load sound effects
25 | game.sounds = Sound(4, game.ASSETS_PATH)
26 | game.sounds.load("jump", "jump.wav")
27 | game.sounds.load("crush", "crunch.wav")
28 | game.sounds.load("pickup", "pickup.wav")
29 | game.sounds.load("hurt", "hurt.wav")
30 | game.sounds.load("eagle", "vulture.wav")
31 |
32 | # create window & start main loop
33 | game.window = Window.create()
34 | game.world.start()
35 | while game.window.process():
36 | for actor in game.actors:
37 | if not actor.update():
38 | game.actors.remove(actor)
39 |
--------------------------------------------------------------------------------
/src/player.py:
--------------------------------------------------------------------------------
1 | """ Main player game entity """
2 |
3 | from tilengine import Spriteset, Sequence, Palette, Input, Flags, TileInfo
4 | from actor import Actor, Direction
5 | from world import Medium, Tiles
6 | from eagle import Eagle
7 | from opossum import Opossum
8 | from rectangle import Rectangle
9 | from effect import Effect
10 | from score import Score
11 | import game
12 |
13 | tiles_info = (TileInfo(), TileInfo(), TileInfo(), TileInfo())
14 |
15 | class State:
16 | """ player states """
17 | Undefined, Idle, Run, Jump, Hit = range(5)
18 |
19 |
20 | class Player(Actor):
21 | """ main player entity """
22 | size = (24, 36)
23 | xspeed_delta = 12
24 | xspeed_limit = 200
25 | yspeed_delta = 10
26 | yspeed_limit = 350
27 | jspeed_delta = 5
28 |
29 | seq_idle = None
30 | seq_jump = None
31 | seq_run = None
32 | spriteset_death = None
33 | seq_death = None
34 |
35 | def __init__(self):
36 |
37 | # init class members once
38 | if Player.spriteset is None:
39 | Player.spriteset = Spriteset.fromfile("hero")
40 | Player.seq_idle = Sequence.create_sprite_sequence(Player.spriteset, "idle", 4)
41 | Player.seq_jump = Sequence.create_sprite_sequence(Player.spriteset, "jump", 24)
42 | Player.seq_run = Sequence.create_sprite_sequence(Player.spriteset, "run", 5)
43 | Player.spriteset_death = Spriteset.fromfile("effect_death")
44 | Player.seq_death = Sequence.create_sprite_sequence(Player.spriteset_death, "death-", 5)
45 |
46 | Actor.__init__(self, None, 60, 188)
47 | self.state = State.Undefined
48 | self.direction = Direction.Right
49 | self.xspeed = 0
50 | self.yspeed = 0
51 | self.set_idle()
52 | self.sprite.set_position(self.x, self.y)
53 | self.width = self.size[0]
54 | self.height = self.size[1]
55 | self.medium = Medium.Floor
56 | self.jump = False
57 | self.immunity = 0
58 | self.rectangle = Rectangle(0, 0, self.width, self.height)
59 |
60 |
61 | self.palettes = (self.spriteset.palette, Palette.fromfile("hero_alt.act"))
62 |
63 | def set_idle(self):
64 | """ sets idle state, idempotent """
65 | if self.state is not State.Idle:
66 | self.sprite.set_animation(Player.seq_idle, 0)
67 | self.state = State.Idle
68 | self.xspeed = 0
69 |
70 | def set_running(self):
71 | """ sets running state, idempotent """
72 | if self.state is not State.Run:
73 | self.sprite.set_animation(Player.seq_run, 0)
74 | self.state = State.Run
75 |
76 | def set_jump(self):
77 | """ sets jump state, idempotent """
78 | if self.state is not State.Jump:
79 | self.yspeed = -280
80 | self.sprite.set_animation(Player.seq_jump, 0)
81 | self.state = State.Jump
82 | self.medium = Medium.Air
83 | game.sounds.play("jump", 0)
84 |
85 | def set_bounce(self):
86 | """ bounces on top of an enemy """
87 | self.yspeed = -150
88 | self.state = State.Jump
89 | self.medium = Medium.Air
90 |
91 | def set_hit(self, enemy_direction):
92 | """ sets hit animation by an enemy """
93 | self.direction = enemy_direction
94 | if self.direction is Direction.Left:
95 | self.xspeed = -self.xspeed_limit
96 | self.sprite.set_flags(0)
97 | else:
98 | self.xspeed = self.xspeed_limit
99 | self.sprite.set_flags(Flags.FLIPX)
100 | self.yspeed = -150
101 | self.state = State.Hit
102 | self.medium = Medium.Air
103 | self.sprite.disable_animation()
104 | self.sprite.set_picture(12)
105 | self.immunity = 90
106 | game.sounds.play("hurt", 0)
107 | game.world.add_timer(-5)
108 | Score(-5, self.x, self.y)
109 |
110 | def update_direction(self):
111 | """ updates sprite facing depending on direction """
112 | if game.window.get_input(Input.RIGHT):
113 | direction = Direction.Right
114 | elif game.window.get_input(Input.LEFT):
115 | direction = Direction.Left
116 | else:
117 | direction = self.direction
118 | if self.direction is not direction:
119 | self.direction = direction
120 | if self.direction is Direction.Right:
121 | self.sprite.set_flags(0)
122 | else:
123 | self.sprite.set_flags(Flags.FLIPX)
124 |
125 | def update_floor(self):
126 | """ process input when player is in floor medium """
127 | if game.window.get_input(Input.RIGHT) and self.xspeed < Player.xspeed_limit:
128 | self.xspeed += self.xspeed_delta
129 | self.set_running()
130 | elif game.window.get_input(Input.LEFT) and self.xspeed > -Player.xspeed_limit:
131 | self.xspeed -= Player.xspeed_delta
132 | self.set_running()
133 | elif abs(self.xspeed) < Player.xspeed_delta:
134 | self.xspeed = 0
135 | elif self.xspeed > 0:
136 | self.xspeed -= Player.xspeed_delta
137 | elif self.xspeed < 0:
138 | self.xspeed += Player.xspeed_delta
139 | if self.xspeed == 0:
140 | self.set_idle()
141 | if game.window.get_input(Input.A):
142 | if self.jump is not True:
143 | self.set_jump()
144 | self.jump = True
145 | else:
146 | self.jump = False
147 |
148 | def update_air(self):
149 | """ process input when player is in air medium """
150 | if game.window.get_input(Input.RIGHT) and self.xspeed < Player.xspeed_limit:
151 | self.xspeed += self.jspeed_delta
152 | elif game.window.get_input(Input.LEFT) and self.xspeed > -Player.xspeed_limit:
153 | self.xspeed -= self.jspeed_delta
154 |
155 | def check_left(self, x, y):
156 | """ checks/adjusts environment collision when player is moving to the left """
157 | game.world.foreground.get_tile(x, y + 4, tiles_info[0])
158 | game.world.foreground.get_tile(x, y + 18, tiles_info[1])
159 | game.world.foreground.get_tile(x, y + 34, tiles_info[2])
160 | if Tiles.Wall in (tiles_info[0].type, tiles_info[1].type, tiles_info[2].type):
161 | self.x = (tiles_info[0].col + 1) * 16
162 | self.xspeed = 0
163 | game.world.pick_gem(tiles_info)
164 |
165 | def check_right(self, x, y):
166 | """ checks/adjusts environment collision when player is moving to the right """
167 | game.world.foreground.get_tile(x + self.width, y + 4, tiles_info[0])
168 | game.world.foreground.get_tile(x + self.width, y + 18, tiles_info[1])
169 | game.world.foreground.get_tile(x + self.width, y + 34, tiles_info[2])
170 | if Tiles.Wall in (tiles_info[0].type, tiles_info[1].type, tiles_info[2].type):
171 | self.x = (tiles_info[0].col * 16) - self.width
172 | self.xspeed = 0
173 | game.world.pick_gem(tiles_info)
174 |
175 | def check_top(self, x, y):
176 | """ checks/adjusts environment collision when player is jumping """
177 | game.world.foreground.get_tile(x + 0, y, tiles_info[0])
178 | game.world.foreground.get_tile(x + 12, y, tiles_info[1])
179 | game.world.foreground.get_tile(x + 24, y, tiles_info[2])
180 | if Tiles.Wall in (tiles_info[0].type, tiles_info[1].type, tiles_info[2].type):
181 | self.y = (tiles_info[0].row + 1) * 16
182 | self.yspeed = 0
183 | game.world.pick_gem(tiles_info)
184 |
185 | def check_bottom(self, x, y):
186 | """ checks/adjusts environment collision when player is falling or running """
187 | ground = False
188 |
189 | game.world.foreground.get_tile(x + 0, y + self.height, tiles_info[0])
190 | game.world.foreground.get_tile(x + 12, y + self.height, tiles_info[1])
191 | game.world.foreground.get_tile(x + 24, y + self.height, tiles_info[2])
192 | game.world.foreground.get_tile(x + 12, y + self.height - 1, tiles_info[3])
193 |
194 | # check up slope
195 | if tiles_info[3].type is Tiles.SlopeUp:
196 | slope_height = 16 - tiles_info[3].xoffset
197 | if self.yspeed >= 0 and tiles_info[3].yoffset > slope_height:
198 | self.y -= (tiles_info[3].yoffset - slope_height)
199 | ground = True
200 |
201 | # check down slope
202 | elif tiles_info[3].type is Tiles.SlopeDown:
203 | slope_height = tiles_info[3].xoffset + 1
204 | if self.yspeed >= 0 and tiles_info[3].yoffset > slope_height:
205 | self.y -= (tiles_info[3].yoffset - slope_height)
206 | ground = True
207 |
208 | # check inner slope (avoid falling between staircased slopes)
209 | elif tiles_info[1].type is Tiles.InnerSlopeUp:
210 | if self.xspeed > 0:
211 | self.y = (tiles_info[1].row * 16) - self.height - 1
212 | else:
213 | self.x -= 1
214 | ground = True
215 |
216 | elif tiles_info[1].type is Tiles.InnerSlopeDown:
217 | if self.xspeed > 0:
218 | self.x += 1
219 | else:
220 | self.y = (tiles_info[1].row * 16) - self.height - 1
221 | ground = True
222 |
223 | # check regular floor
224 | elif Tiles.Floor in (tiles_info[0].type, tiles_info[1].type, tiles_info[2].type):
225 | self.y = (tiles_info[0].row * 16) - self.height
226 | ground = True
227 |
228 | # adjust to ground
229 | if ground is True:
230 | self.yspeed = 0
231 | if self.medium is Medium.Air:
232 | self.medium = Medium.Floor
233 | if self.xspeed == 0:
234 | self.set_idle()
235 | else:
236 | self.set_running()
237 | else:
238 | self.medium = Medium.Air
239 | game.world.pick_gem(tiles_info)
240 |
241 | def check_jump_on_enemies(self, x, y):
242 | """ checks jumping above an enemy. If so, kills it, bounces and spawns a death animation """
243 | px, py = x+self.width/2, y+self.height
244 | for actor in game.actors:
245 | actor_type = type(actor)
246 | if actor_type in (Eagle, Opossum):
247 | ex, ey = actor.x + actor.size[0]/2, actor.y
248 | if abs(px - ex) < 25 and 5 < py - ey < 20:
249 | game.world.add_timer(5)
250 | actor.kill()
251 | self.set_bounce()
252 | Effect(actor.x, actor.y - 10, self.spriteset_death, self.seq_death)
253 | game.sounds.play("crush", 2)
254 | return
255 |
256 | def check_hit(self, x, y, direction):
257 | """ returns if get hurt by enemy at select position and direction"""
258 | if self.immunity is 0 and self.rectangle.check_point(x, y):
259 | self.set_hit(direction)
260 |
261 | def update(self):
262 | """ process input and updates state once per frame """
263 | oldx = self.x
264 | oldy = self.y
265 |
266 | # update immunity
267 | if self.immunity is not 0:
268 | pal_index0 = (self.immunity >> 2) & 1
269 | self.immunity -= 1
270 | pal_index1 = (self.immunity >> 2) & 1
271 | if self.immunity is 0:
272 | pal_index1 = 0
273 | if pal_index0 != pal_index1:
274 | self.sprite.set_palette(self.palettes[pal_index1])
275 |
276 | # update sprite facing
277 | self.update_direction()
278 |
279 | # user input: move character depending on medium
280 | if self.medium is Medium.Floor:
281 | self.update_floor()
282 | elif self.medium is Medium.Air:
283 | if self.state is not State.Hit:
284 | self.update_air()
285 | if self.yspeed < Player.yspeed_limit:
286 | self.yspeed += Player.yspeed_delta
287 |
288 | self.x += (self.xspeed / 100.0)
289 | self.y += (self.yspeed / 100.0)
290 |
291 | # clip to game.world limits
292 | if self.x < 0.0:
293 | self.x = 0.0
294 | elif self.x > game.world.foreground.width - self.width:
295 | self.x = game.world.foreground.width - self.width
296 |
297 | # check and fix 4-way collisions depending on motion direction
298 | intx = int(self.x)
299 | inty = int(self.y)
300 | if self.yspeed < 0:
301 | self.check_top(intx, inty)
302 | elif self.yspeed >= 0:
303 | self.check_bottom(intx, inty)
304 | if self.xspeed < 0:
305 | self.check_left(intx, inty)
306 | elif self.xspeed > 0:
307 | self.check_right(intx, inty)
308 | if self.yspeed > 0:
309 | self.check_jump_on_enemies(intx, inty)
310 |
311 | if self.x != oldx or self.y != oldy:
312 | self.rectangle.update_position(int(self.x), int(self.y))
313 | self.sprite.set_position(int(self.x) - game.world.x, int(self.y))
314 | return True
315 |
--------------------------------------------------------------------------------
/src/raster_effect.py:
--------------------------------------------------------------------------------
1 | """
2 | Demonstrates raster effects for tilengine:
3 | - linescroll for medium layer
4 | - gradient color for sky
5 | """
6 |
7 | from tilengine import Color
8 | import game
9 |
10 | # sky gradient color
11 | SKY_COLORS = (Color.fromstring("#78D7F2"), Color.fromstring("#E2ECF2"))
12 |
13 | def lerp(pos_x, x0, x1, fx0, fx1):
14 | """ integer linear interpolation """
15 | return fx0 + (fx1 - fx0) * (pos_x - x0) // (x1 - x0)
16 |
17 | def interpolate_color(x, x1, x2, color1, color2):
18 | """ linear interpolation between two Color objects """
19 | r = lerp(x, x1, x2, color1.r, color2.r)
20 | g = lerp(x, x1, x2, color1.g, color2.g)
21 | b = lerp(x, x1, x2, color1.b, color2.b)
22 | return Color(r, g, b)
23 |
24 | def raster_effect(line):
25 | """ raster effect callback, called every rendered scanline """
26 |
27 | # sky color gradient
28 | if 0 <= line <= 128:
29 | color = interpolate_color(line, 0, 128, SKY_COLORS[0], SKY_COLORS[1])
30 | game.engine.set_background_color(color)
31 |
32 | # sets cloud position at frame start
33 | if line == 0:
34 | game.world.background.set_position(int(game.world.clouds), 0)
35 |
36 | # linescroll on main background
37 | elif 160 <= line <= 208:
38 | pos1 = game.world.x//10
39 | pos2 = game.world.x//3
40 | xpos = lerp(line, 160, 208, pos1, pos2)
41 | game.world.background.set_position(xpos, 0)
42 |
43 | # bottom background
44 | elif line == 256:
45 | game.world.background.set_position(game.world.x//2, 0)
46 |
--------------------------------------------------------------------------------
/src/rectangle.py:
--------------------------------------------------------------------------------
1 | """ Basic rectangle helper for hitboxes """
2 |
3 | class Rectangle(object):
4 | """ aux rectangle """
5 | def __init__(self, x, y, w, h):
6 | self.width = w
7 | self.height = h
8 | self.update_position(x, y)
9 |
10 | def update_position(self, x, y):
11 | self.x1 = x
12 | self.y1 = y
13 | self.x2 = x + self.width
14 | self.y2 = y + self.height
15 |
16 | def check_point(self, x, y):
17 | """ returns if point is contained in rectangle """
18 | return self.x1 <= x <= self.x2 and self.y1 <= y <= self.y2
19 |
--------------------------------------------------------------------------------
/src/score.py:
--------------------------------------------------------------------------------
1 | """ Effect that shows pop-up score on player actions """
2 |
3 | from tilengine import Spriteset
4 | from actor import Actor
5 | import game
6 |
7 | class Score(Actor):
8 | def __init__(self, value, x, y):
9 |
10 | # init class members once
11 | if Score.spriteset is None:
12 | Score.spriteset = Spriteset.fromfile("score")
13 |
14 | Actor.__init__(self, None, int(x), int(y))
15 | if value is 5:
16 | self.sprite.set_picture(0)
17 | elif value is -5:
18 | self.sprite.set_picture(1)
19 | elif value is 1:
20 | self.sprite.set_picture(2)
21 |
22 | self.t0 = game.window.get_ticks()
23 | self.t1 = self.t0 + 1000
24 |
25 | def update(self):
26 | now = game.window.get_ticks()
27 | p = (now - self.t0) / (self.t1 - self.t0)
28 | p = -(p * (p - 2))
29 | self.sprite.set_position(int(self.x - game.world.x), int(self.y - p*16))
30 | return now < self.t1
31 |
--------------------------------------------------------------------------------
/src/sound.py:
--------------------------------------------------------------------------------
1 | """ Sound effect helper """
2 |
3 | from sdl2 import *
4 | from sdl2.sdlmixer import *
5 |
6 | class Sound(object):
7 | """ Manages sound effects """
8 | def __init__(self, num_channels, path):
9 | SDL_Init(SDL_INIT_AUDIO)
10 | Mix_Init(0)
11 | Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 2048)
12 | Mix_AllocateChannels(num_channels)
13 | self._sounds = dict()
14 | if path is None:
15 | self.path = "./"
16 | else:
17 | self.path = path + "/"
18 |
19 | def __del__(self):
20 | for s in self._sounds:
21 | Mix_FreeChunk(s)
22 |
23 | def load(self, name, file):
24 | self._sounds[name] = Mix_LoadWAV((self.path + file).encode())
25 |
26 | def play(self, name, channel):
27 | Mix_PlayChannel(channel, self._sounds[name], 0)
28 |
--------------------------------------------------------------------------------
/src/tilengine.py:
--------------------------------------------------------------------------------
1 | """
2 | Python wrapper for Tilengine retro graphics engine
3 | Updated to library version 2.11.0
4 | http://www.tilengine.org
5 | """
6 |
7 | """
8 | Copyright (c) 2017-2022 Marc Palacios Domenech
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 | """
28 |
29 | # pylint: disable=C0103
30 | # pylint: disable=W0614
31 | # pylint: disable=W0312
32 | # pylint: disable=R0201
33 | from sys import platform as _platform
34 | from ctypes import *
35 | from os import path
36 | from typing import List, Union, Optional
37 |
38 |
39 | # constants --------------------------------------------------------------------
40 |
41 | class WindowFlags:
42 | """
43 | List of flag values for window creation
44 | """
45 | FULLSCREEN = (1 << 0)
46 | VSYNC = (1 << 1)
47 | S1 = (1 << 2)
48 | S2 = (2 << 2)
49 | S3 = (3 << 2)
50 | S4 = (4 << 2)
51 | S5 = (5 << 2)
52 | NEAREST = (1 << 6)
53 |
54 |
55 | class Flags:
56 | """
57 | List of flags for tiles and sprites
58 | """
59 | FLIPX = (1 << 15) # horizontal flip
60 | FLIPY = (1 << 14) # vertical flip
61 | ROTATE = (1 << 13) # row/column flip (unsupported, Tiled compatibility)
62 | PRIORITY = (1 << 12) # tile goes in front of sprite layer
63 | MASKED = (1 << 11) # sprite won't be drawn inside masked region
64 | TILESET = (7 << 8) # tileset index
65 |
66 |
67 | class Error:
68 | """
69 | List of possible error codes returned by :meth:`Engine.get_last_error()`
70 | """
71 | OK = 0 # No error
72 | OUT_OF_MEMORY = 1 # Not enough memory
73 | IDX_LAYER = 2 # Layer index out of range
74 | IDX_SPRITE = 3 # Sprite index out of range
75 | IDX_ANIMATION = 4 # Animation index out of range
76 | IDX_PICTURE = 5 # Picture or tile index out of range
77 | REF_TILESET = 6 # Invalid Tileset reference
78 | REF_TILEMAP = 7 # Invalid Tilemap reference
79 | REF_SPRITESET = 8 # Invalid Spriteset reference
80 | REF_PALETTE = 9 # Invalid Palette reference
81 | REF_SEQUENCE = 10 # Invalid SequencePack reference
82 | REF_SEQPACK = 11 # Invalid Sequence reference
83 | REF_BITMAP = 12 # Invalid Bitmap reference
84 | NULL_POINTER = 13 # Null pointer as argument
85 | FILE_NOT_FOUND = 14 # Resource file not found
86 | WRONG_FORMAT = 15 # Resource file has invalid format
87 | WRONG_SIZE = 16 # A width or height parameter is invalid
88 | UNSUPPORTED = 17 # Unsupported function
89 | REF_LIST = 18 # Invalid ObjectList reference
90 |
91 |
92 | class LogLevel:
93 | """
94 | Log levels for :meth:`Engine.set_log_level()`
95 | """
96 | NONE, ERRORS, VERBOSE = range(3)
97 |
98 |
99 | class Blend:
100 | """
101 | Available blending modes
102 | """
103 | NONE, MIX25, MIX50, MIX75, ADD, SUB, MOD, CUSTOM = range(8)
104 | MIX = MIX50
105 |
106 |
107 | class Input:
108 | """
109 | Available inputs to query in :meth:`Window.get_input`
110 | """
111 | NONE, UP, DOWN, LEFT, RIGHT, A, B, C, D, E, F, START, QUIT, CRT = range(14)
112 | BUTTON1, BUTTON2, BUTTON3, BUTTON4, BUTTON5, BUTTON6 = range(A, START)
113 | P1, P2, P3, P4 = range(0, 128, 32)
114 |
115 |
116 | PLAYER1, PLAYER2, PLAYER3, PLAYER4 = range(4)
117 |
118 |
119 | class Overlay:
120 | """
121 | Unised, kept for backwards compatibility with pre-2.10 release
122 | """
123 | NONE, SHADOWMASK, APERTURE, SCANLINES, CUSTOM = range(5)
124 |
125 |
126 | class CRT:
127 | """
128 | Types of crt effect for release 2.10
129 | """
130 | SLOT, APERTURE, SHADOW = range(3)
131 |
132 |
133 | class TilengineException(Exception):
134 | """
135 | Tilengine exception class
136 | """
137 | def __init__(self, value):
138 | self.value = value
139 |
140 | def __str__(self):
141 | return repr(self.value)
142 |
143 |
144 | # structures ------------------------------------------------------------------
145 | class Tile(Structure):
146 | """
147 | Tile data contained in each cell of a :class:`Tilemap` object
148 |
149 | :attr:`index`: tile index
150 | :attr:`flags`: sum of :class:`Flags` values
151 | """
152 | _fields_ = [
153 | ("index", c_ushort),
154 | ("flags", c_ushort)
155 | ]
156 |
157 |
158 | class ColorStrip(Structure):
159 | """
160 | Data used to define each frame of a color cycle for :class:`Sequence` objects
161 | """
162 | _fields_ = [
163 | ("delay", c_int),
164 | ("first", c_ubyte),
165 | ("count", c_ubyte),
166 | ("dir", c_ubyte)
167 | ]
168 |
169 | def __init__(self, delay: int, first: int, count: int, direction: int):
170 | self.delay = delay
171 | self.first = first
172 | self.count = count
173 | self.dir = direction
174 |
175 |
176 | class SequenceInfo(Structure):
177 | """Sequence info returned by :meth:`Sequence.get_info`"""
178 | _fields_ = [
179 | ("name", c_char * 32),
180 | ("num_frames", c_int)
181 | ]
182 |
183 |
184 | class SequenceFrame(Structure):
185 | """
186 | Data used to define each frame of an animation for :class:`Sequence` objects
187 | """
188 | _fields_ = [
189 | ("index", c_int),
190 | ("delay", c_int)
191 | ]
192 |
193 | def __init__(self, index: int, delay: int):
194 | self.index = index
195 | self.delay = delay
196 |
197 |
198 | class SpriteInfo(Structure):
199 | """
200 | Data returned by :meth:`Spriteset.get_sprite_info` with dimensions of the required sprite
201 | """
202 | _fields_ = [
203 | ("w", c_int),
204 | ("h", c_int)
205 | ]
206 |
207 |
208 | class TileInfo(Structure):
209 | """
210 | Data returned by :meth:`Layer.get_tile` about a given tile inside a background layer
211 | """
212 | _fields_ = [
213 | ("index", c_ushort),
214 | ("flags", c_ushort),
215 | ("row", c_int),
216 | ("col", c_int),
217 | ("xoffset", c_int),
218 | ("yoffset", c_int),
219 | ("color", c_ubyte),
220 | ("type", c_ubyte),
221 | ("empty", c_bool)
222 | ]
223 |
224 |
225 | class SpriteData(Structure):
226 | """
227 | Data used to create :class:`Spriteset` objects
228 | """
229 | _fields_ = [
230 | ("name", c_char * 64),
231 | ("x", c_int),
232 | ("y", c_int),
233 | ("w", c_int),
234 | ("h", c_int)
235 | ]
236 |
237 | def __init__(self, name: str, x: int, y: int, width: int, height: int):
238 | self.name = _encode_string(name)
239 | self.x = x
240 | self.y = y
241 | self.w = width
242 | self.h = height
243 |
244 |
245 | class TileAttributes(Structure):
246 | """
247 | Data used to create :class:`Tileset` objects
248 | """
249 | _fields_ = [
250 | ("type", c_ubyte),
251 | ("priority", c_bool)
252 | ]
253 |
254 | def __init__(self, tile_type: int, priority: bool):
255 | self.type = tile_type
256 | self.priority = priority
257 |
258 |
259 | class PixelMap(Structure):
260 | """
261 | Data passed to :meth:`Layer.set_pixel_mapping` in a list
262 | """
263 | _fields_ = [
264 | ("dx", c_short),
265 | ("dy", c_short)
266 | ]
267 |
268 |
269 | class ObjectInfo(Structure):
270 | """
271 | Object item info returned by :meth:`ObjectInfo.get_info()`
272 | """
273 | _fields_ = [
274 | ("id", c_ushort),
275 | ("gid", c_ushort),
276 | ("flags", c_ushort),
277 | ("x", c_int),
278 | ("y", c_int),
279 | ("width", c_int),
280 | ("height", c_int),
281 | ("type", c_ubyte),
282 | ("visible", c_bool),
283 | ("name", c_char * 64)
284 | ]
285 |
286 |
287 | class TileImage(Structure):
288 | """
289 | Image Tile items for TLN_CreateImageTileset()
290 | """
291 | _fields_ = [
292 | ("bitmap", c_void_p),
293 | ("id", c_ushort),
294 | ("type", c_ubyte)
295 | ]
296 |
297 |
298 | class SpriteState(Structure):
299 | """
300 | Sprite state for :meth:`Sprite.get_state()`
301 | """
302 | _fields_ = [
303 | ("x", c_int),
304 | ("y", c_int),
305 | ("w", c_int),
306 | ("h", c_int),
307 | ("flags", c_ushort),
308 | ("palette", c_void_p),
309 | ("spriteset", c_void_p),
310 | ("index", c_int),
311 | ("enabled", c_bool),
312 | ("collision", c_bool)
313 | ]
314 |
315 |
316 | class Color(object):
317 | """
318 | Represents a color value in RGB format
319 | """
320 | def __init__(self, r: int, g: int, b: int):
321 | self.r = r
322 | self.g = g
323 | self.b = b
324 |
325 | @classmethod
326 | def fromstring(cls, string: str):
327 | """ creates a color from a ccs-style #rrggbb string """
328 | r = int(string[1:3], 16)
329 | g = int(string[3:5], 16)
330 | b = int(string[5:7], 16)
331 | return Color(r, g, b)
332 |
333 |
334 | # module internal variables
335 | _tln: "Engine" # handle to shared native library
336 | _window: "Window" # singleton window
337 | _window_created = False # singleton window is created
338 |
339 | # select library depending on OS
340 | _library = ""
341 | if _platform == "linux" or _platform == "linux2":
342 | _library = "libTilengine.so"
343 | elif _platform == "win32":
344 | _library = "Tilengine.dll"
345 | elif _platform == "darwin":
346 | _library = "Tilengine.dylib"
347 | else:
348 | raise OSError("Unsupported platform: must be Windows, Linux or Mac OS")
349 |
350 | # load native library. Try local path, if not, system path
351 | if path.isfile(_library):
352 | _tln = cdll.LoadLibrary(f"./{_library}")
353 | else:
354 | _tln = cdll.LoadLibrary(_library)
355 |
356 | # callback types for user functions
357 | _video_callback_function = CFUNCTYPE(None, c_int)
358 | _blend_function = CFUNCTYPE(c_ubyte, c_ubyte, c_ubyte)
359 |
360 |
361 | # convert string to c_char_p
362 | def _encode_string(string: Optional[str]):
363 | if string is not None:
364 | return string.encode()
365 | return None
366 |
367 | # convert c_char_p to string
368 | def _decode_string(byte_array: Optional[bytearray]):
369 | if byte_array is not None:
370 | return byte_array.decode()
371 | return None
372 |
373 |
374 | # error handling --------------------------------------------------------------
375 | _tln.TLN_GetLastError.restype = c_int
376 | _tln.TLN_GetErrorString.argtypes = [c_int]
377 | _tln.TLN_GetErrorString.restype = c_char_p
378 |
379 |
380 | # raises exception depending on error code
381 | def _raise_exception(result: bool=False):
382 | if result is not True:
383 | error = _tln.TLN_GetLastError()
384 | error_string = _tln.TLN_GetErrorString(error)
385 | raise TilengineException(error_string.decode())
386 |
387 | # World management
388 | _tln.TLN_LoadWorld.argtypes = [c_char_p, c_int]
389 | _tln.TLN_LoadWorld.restype = c_bool
390 | _tln.TLN_SetWorldPosition.argtypes = [c_int, c_int]
391 | _tln.TLN_SetWorldPosition.restype = None
392 | _tln.TLN_SetLayerParallaxFactor.argtypes = [c_int, c_float, c_float]
393 | _tln.TLN_SetLayerParallaxFactor.restype = c_bool
394 | _tln.TLN_SetSpriteWorldPosition.argtypes = [c_int, c_int, c_int]
395 | _tln.TLN_SetSpriteWorldPosition.restype = c_bool
396 | _tln.TLN_ReleaseWorld.argtypes = None
397 | _tln.TLN_ReleaseWorld.restype = None
398 |
399 | # basic management ------------------------------------------------------------
400 | _tln.TLN_Init.argtypes = [c_int, c_int, c_int, c_int, c_int]
401 | _tln.TLN_Init.restype = c_void_p
402 | _tln.TLN_DeleteContext.argtypes = [c_void_p]
403 | _tln.TLN_DeleteContext.restype = c_bool
404 | _tln.TLN_SetContext.argtypes = [c_void_p]
405 | _tln.TLN_SetContext.restype = c_bool
406 | _tln.TLN_GetContext.restype = c_void_p
407 | _tln.TLN_GetNumObjects.restype = c_int
408 | _tln.TLN_GetVersion.restype = c_int
409 | _tln.TLN_GetUsedMemory.restype = c_int
410 | _tln.TLN_SetBGColor.argtypes = [c_ubyte, c_ubyte, c_ubyte]
411 | _tln.TLN_SetBGColorFromTilemap.argtypes = [c_void_p]
412 | _tln.TLN_SetBGColorFromTilemap.restype = c_bool
413 | _tln.TLN_SetBGBitmap.argtypes = [c_void_p]
414 | _tln.TLN_SetBGBitmap.restype = c_bool
415 | _tln.TLN_SetBGPalette.argtypes = [c_void_p]
416 | _tln.TLN_SetBGPalette.restype = c_bool
417 | _tln.TLN_SetRenderTarget.argtypes = [c_void_p, c_int]
418 | _tln.TLN_UpdateFrame.argtypes = [c_int]
419 | _tln.TLN_SetLoadPath.argtypes = [c_char_p]
420 | _tln.TLN_SetLogLevel.argtypes = [c_int]
421 | _tln.TLN_OpenResourcePack.argtypes = [c_char_p, c_char_p]
422 | _tln.TLN_OpenResourcePack.restype = c_bool
423 | _tln.TLN_SetSpritesMaskRegion.argtypes = [c_int, c_int]
424 |
425 | class Engine(object):
426 | """
427 | Main object for engine creation and rendering
428 |
429 | :ivar layers: tuple of Layer objects, one entry per layer
430 | :ivar sprites: tuple of Sprite objects, one entry per sprite
431 | :ivar animations: tuple of Animation objects, one entry per animation
432 | :ivar version: library version number
433 | """
434 | def __init__(self, handle: c_void_p, num_layers: int, num_sprites: int, num_animations: int):
435 | self._as_parameter_ = handle
436 | self.layers = tuple([Layer(n) for n in range(num_layers)])
437 | self.sprites = tuple([Sprite(n) for n in range(num_sprites)])
438 | self.animations = tuple([Animation(n) for n in range(num_animations)])
439 | self.version = _tln.TLN_GetVersion()
440 | self.cb_raster_func = None
441 | self.cb_frame_func = None
442 | self.cb_blend_func = None
443 | self.library = _tln
444 |
445 | version = [2,11,0] # expected library version
446 | req_version = (version[0] << 16) + (version[1] << 8) + version[2]
447 | if self.version < req_version:
448 | maj_version = self.version >> 16
449 | min_version = (self.version >> 8) & 255
450 | bug_version = self.version & 255
451 | print("WARNING: Library version is %d.%d.%d, expected at least %d.%d.%d!" % \
452 | (maj_version, min_version, bug_version, version[0], version[1], version[2]))
453 |
454 | @classmethod
455 | def create(cls, width: int, height: int, num_layers: int, num_sprites: int, num_animations: int) -> "Engine":
456 | """
457 | Static method that creates a new instance of the engine
458 |
459 | :param width: horizontal resolution in pixels
460 | :param height: vertical resolution in pixels
461 | :param num_layers: max number of background layers
462 | :param num_sprites: max number of sprites
463 | :param num_animations: number of color-cycle animations
464 | :return: new instance
465 | """
466 | handle = _tln.TLN_Init(width, height, num_layers, num_sprites, num_animations)
467 | if handle is not None:
468 | _engine = Engine(handle, num_layers, num_sprites, num_animations)
469 | return _engine
470 | else:
471 | _raise_exception()
472 |
473 | def __del__(self):
474 | _tln.TLN_DeleteContext(self)
475 |
476 | def get_num_objects(self) -> int:
477 | """
478 | :return: the number of objets used by the engine so far
479 | """
480 | return _tln.TLN_GetNumObjects()
481 |
482 | def get_used_memory(self) -> int:
483 | """
484 | :return: the total amount of memory used by the objects
485 | """
486 | return _tln.TLN_GetUsedMemory()
487 |
488 | def set_background_color(self, param):
489 | """
490 | Sets the background color
491 |
492 | :param param: can be a Color object or a Tilemap object. In this case, \
493 | it assigns de background color as defined inside the tilemap
494 | """
495 | param_type = type(param)
496 | if param_type is Color:
497 | _tln.TLN_SetBGColor(param.r, param.g, param.b)
498 | elif param_type is Tilemap:
499 | _tln.TLN_SetBGColorFromTilemap(param)
500 |
501 | def disable_background_color(self):
502 | """
503 | Disales background color rendering. If you know that the last background layer will always
504 | cover the entire screen, you can disable it to gain some performance
505 | """
506 | _tln.TLN_DisableBGColor()
507 |
508 | def set_background_bitmap(self, bitmap: "Bitmap"):
509 | """
510 | Sets a static bitmap as background
511 |
512 | :param bitmap: Bitmap object to set as background. Set None to disable it.
513 | """
514 | ok = _tln.TLN_SetBGBitmap(bitmap)
515 | _raise_exception(ok)
516 |
517 | def set_background_palette(self, palette: "Palette"):
518 | """
519 | Sets the palette for the background bitmap. By default it is assigned the palette
520 | of the bitmap passed in :meth:`Engine.set_background_bitmap`
521 |
522 | :param palette: Palette object to set
523 | """
524 | ok = _tln.TLN_SetBGPalette(palette)
525 | _raise_exception(ok)
526 |
527 | def set_raster_callback(self, raster_callback):
528 | """
529 | Enables raster effects processing, like a virtual HBLANK interrupt where any render parameter can be modified between scanlines.
530 |
531 | :param raster_callback: name of the user-defined function to call for each scanline. Set None to disable. \
532 | This function takes one integer parameter that indicates the current scanline, between 0 and vertical resolution.
533 |
534 | Example::
535 |
536 | def my_raster_callback(num_scanline):
537 | if num_scanline is 32:
538 | engine.set_background_color(Color(0,0,0))
539 |
540 | engine.set_raster_callback(my_raster_callback)
541 | """
542 | if raster_callback is None:
543 | self.cb_raster_func = None
544 | else:
545 | self.cb_raster_func = _video_callback_function(raster_callback)
546 | _tln.TLN_SetRasterCallback(self.cb_raster_func)
547 |
548 | def set_frame_callback(self, frame_callback):
549 | """
550 | Enables user callback for each drawn frame, like a virtual VBLANK interrupt
551 |
552 | :param frame_callback: name of the user-defined function to call for each frame. Set None to disable. \
553 | This function takes one integer parameter that indicates the current frame.
554 |
555 | Example::
556 |
557 | def my_frame_callback(num_frame):
558 | engine.set_background_color(Color(0,0,0))
559 |
560 | engine.set_frame_callback(my_frame_callback)
561 | """
562 | if frame_callback is None:
563 | self.cb_frame_func = None
564 | else:
565 | self.cb_frame_func = _video_callback_function(frame_callback)
566 | _tln.TLN_SetFrameCallback(self.cb_frame_func)
567 |
568 | def set_render_target(self, pixels, pitch):
569 | """
570 | Sets the output surface for rendering
571 |
572 | :param pixels: Pointer to the start of the target framebuffer
573 | :param pitch: Number of bytes per each scanline of the framebuffer
574 | """
575 | _tln.TLN_SetRenderTarget(pixels, pitch)
576 |
577 | def update_frame(self, num_frame=0):
578 | """
579 | Draws the frame to the previously specified render target
580 |
581 | :param num_frame: optional frame number for animation control
582 | """
583 | _tln.TLN_UpdateFrame(num_frame)
584 |
585 | def set_load_path(self, path: str):
586 | """
587 | Sets base path for all data loading static methods `fromfile`
588 |
589 | :param path: Base path. Files will load at path/filename. Set None to use current directory
590 | """
591 | _tln.TLN_SetLoadPath(_encode_string(path))
592 |
593 | def set_custom_blend_function(self, blend_function):
594 | """
595 | Sets custom blend function to use in sprites or background layers when `BLEND_CUSTOM` mode
596 | is selected with the :meth:`Layer.set_blend_mode` and :meth:`Sprite.set_blend_mode` methods.
597 |
598 | :param blend_function: name of the user-defined function to call when blending that takes \
599 | two integer arguments: source component intensity, destination component intensity, and returns \
600 | the desired intensity.
601 |
602 | Example::
603 |
604 | # do 50%/50% blending
605 | def blend_50(src, dst):
606 | return (src + dst) / 2
607 |
608 | engine.set_custom_blend_function(blend_50)
609 | """
610 | self.cb_blend_func = _blend_function(blend_function)
611 | _tln.TLN_SetCustomBlendFunction(self.cb_blend_func)
612 |
613 | def set_log_level(self, log_level: int):
614 | """
615 | Sets output messages
616 | """
617 | _tln.TLN_SetLogLevel(log_level)
618 |
619 | def get_available_sprite(self) -> int:
620 | """
621 | :return: Index of the first unused sprite (starting from 0) or -1 if none found
622 | """
623 | index = _tln.TLN_GetAvailableSprite()
624 | return index
625 |
626 | def get_available_animation(self) -> int:
627 | """
628 | :return: Index of the first unused animation (starting from 0) or -1 if none found
629 | """
630 | index = _tln.TLN_GetAvailableAnimation()
631 | return index
632 |
633 | def open_resource_pack(self, filename: str, key: str=''):
634 | """
635 | Opens the resource package with optional aes-128 key and binds it
636 | :param filename: file with the resource package (.dat extension)
637 | :param key: optional null-terminated ASCII string with aes decryption key
638 | """
639 | ok = _tln.TLN_OpenResourcePack(_encode_string(str(filename)), _encode_string(str(key)))
640 | _raise_exception(ok)
641 |
642 | def close_resource_pack(self):
643 | """
644 | Closes currently opened resource package and unbinds it
645 | """
646 | return _tln.TLN_CloseResourcePack()
647 |
648 | def set_sprites_mask_region(self, top: int, bottom: int):
649 | """
650 | Defines a sprite masking region between the two scanlines. Sprites masked with Sprite.enable_mask_region() won't be drawn inside this region
651 |
652 | :param top: upper scaline of the exclusion region
653 | :param bottom: lower scanline of the exclusion region
654 | """
655 | ok = _tln.TLN_SetSpritesMaskRegion(top, bottom)
656 | _raise_exception(ok)
657 |
658 | def load_world(self, filename: str, first_layer: int=0):
659 | """
660 | Loads and assigns complete TMX file
661 |
662 | :param filename: main .tmx file to load
663 | :first_layer: optional layer index to start to assign, by default 0
664 | """
665 | ok =_tln.TLN_LoadWorld(filename, first_layer)
666 | _raise_exception(ok)
667 |
668 | def set_world_position(self, x: int, y: int):
669 | """
670 | Sets global world position, moving all layers in sync according to their parallax factor
671 |
672 | :param x: horizontal position in world space
673 | :param y: vertical position in world space
674 | """
675 | ok = _tln.TLN_SetWorldPosition(x, y)
676 | _raise_exception(ok)
677 |
678 | def release_world(self):
679 | """
680 | Releases world resources loaded with Engine.load_world
681 | """
682 | _tln.TLN_ReleaseWorld()
683 |
684 |
685 | # window management -----------------------------------------------------------
686 | _tln.TLN_CreateWindow.argtypes = [c_char_p, c_int]
687 | _tln.TLN_CreateWindow.restype = c_bool
688 | _tln.TLN_CreateWindowThread.argtypes = [c_char_p, c_int]
689 | _tln.TLN_CreateWindowThread.restype = c_bool
690 | _tln.TLN_ProcessWindow.restype = c_bool
691 | _tln.TLN_IsWindowActive.restype = c_bool
692 | _tln.TLN_GetInput.argtypes = [c_int]
693 | _tln.TLN_GetInput.restype = c_bool
694 | _tln.TLN_EnableInput.argtypes = [c_int, c_bool]
695 | _tln.TLN_AssignInputJoystick.argtypes = [c_int, c_int]
696 | _tln.TLN_DefineInputKey.argtypes = [c_int, c_int, c_uint]
697 | _tln.TLN_DefineInputButton.argtypes = [c_int, c_int, c_ubyte]
698 | _tln.TLN_DrawFrame.argtypes = [c_int]
699 | _tln.TLN_EnableCRTEffect.argtypes = [c_int, c_ubyte, c_ubyte, c_ubyte, c_ubyte, c_ubyte, c_ubyte, c_bool, c_ubyte]
700 | _tln.TLN_ConfigCRTEffect.argtypes = [c_int, c_bool]
701 | _tln.TLN_GetTicks.restype = c_int
702 | _tln.TLN_Delay.argtypes = [c_int]
703 | _tln.TLN_GetWindowWidth.restype = c_int
704 | _tln.TLN_GetWindowHeight.restype = c_int
705 |
706 | class Window(object):
707 | """
708 | Built-in window manager for easy setup and testing
709 |
710 | :ivar num_frame: current frame being drawn, starting from 0
711 | :ivar width: actual window width (after scaling)
712 | :ivar height: actual window height (after scaling)
713 | """
714 | def __init__(self):
715 | self.cb_sdl_func = None
716 |
717 | @classmethod
718 | def create(cls, title:str='Tilengine window', overlay=None, flags:int=WindowFlags.VSYNC) -> "Window":
719 | """
720 | Static method that creates a single-threaded window that must be used in conjunction with
721 | :meth:`Window.process` in a loop
722 |
723 | :param overlay: obsolete, kept for compatibility with pre 2.10 release with old CRT effect
724 | :param flags: optional flags combination of :class:`WindowFlags` values
725 | :return: instance of the created window
726 | """
727 | global _window, _window_created
728 |
729 | """Added the ability to choose the window title ~AleK3y"""
730 | _tln.TLN_SetWindowTitle(_encode_string(str(title)))
731 |
732 | if _window_created:
733 | return _window
734 | ok = _tln.TLN_CreateWindow(_encode_string(overlay), flags)
735 | if ok is True:
736 | _window = Window()
737 | _window.num_frame = 0
738 | _window.width = _tln.TLN_GetWindowWidth()
739 | _window.height = _tln.TLN_GetWindowHeight()
740 | _window_created = True
741 | return _window
742 | else:
743 | _raise_exception(ok)
744 |
745 | @classmethod
746 | def create_threaded(cls, overlay=None, flags:int=WindowFlags.VSYNC) -> "Window":
747 | """
748 | Static method that creates a multi-threaded window that runs in its own thread without user loop.
749 | Used mainly in python interactive console
750 |
751 | :param overlay: obsolete, kept for compatibility with pre 2.10 release with old CRT effect
752 | :param flags: optional flags combination of :class:`WindowFlags` values
753 | """
754 | global _window
755 | if _window is not None:
756 | return _window
757 | ok = _tln.TLN_CreateWindowThread(_encode_string(overlay), flags)
758 | if ok is True:
759 | _window = Window()
760 | _window.width = _tln.TLN_GetWindowWidth()
761 | _window.height = _tln.TLN_GetWindowHeight()
762 | return _window
763 | else:
764 | _raise_exception(ok)
765 |
766 | def process(self):
767 | """
768 | Does basic window housekeeping in signgle-threaded window, created with :meth:`Window.create`.
769 | This method must be called in a loop by the main thread.
770 | :return: True if window is active or False if the user has requested to end the application
771 | (by pressing Esc key or clicking the close button)
772 | """
773 | _tln.TLN_DrawFrame(self.num_frame)
774 | self.num_frame += 1
775 | return _tln.TLN_ProcessWindow()
776 |
777 | def is_active(self) -> bool:
778 | """
779 | :return: True if window is active or False if the user has requested to end the application \
780 | (by pressing Esc key or clicking the close button)
781 | """
782 | return _tln.TLN_IsWindowActive()
783 |
784 | def get_input(self, input_id: int) -> bool:
785 | """
786 | Returns the state of a given input
787 |
788 | :param input_id: one of the :class:`Input` defined values. By default it requests input of player 1. \
789 | To request input of a given player, add one of the possible P1 - P4 values.
790 |
791 | :return: True if that input is pressed or False if not
792 |
793 | Example::
794 |
795 | # check if player 1 is pressing right:
796 | value = window.get_input(Input.RIGHT)
797 |
798 | # check if player 2 is pressing action button 1:
799 | value = window.get_input(Input.P2 + Input.BUTTON1)
800 | """
801 | return _tln.TLN_GetInput(input_id)
802 |
803 | def enable_input(self, player: int, state: bool):
804 | """
805 | Enables or disables input for specified player
806 |
807 | :param player: player identifier to configure (PLAYER1 - PLAYER4)
808 | :param state: True to enable, False to disable
809 | """
810 | _tln.TLN_EnableInput(player, state)
811 |
812 | def assign_joystick(self, player: int, joystick_index: int):
813 | """
814 |
815 | :param player: player identifier to configure (PLAYER1 - PLAYER4)
816 | :param joystick_index: zero-based joystick index to assign. 0 = first, 1 = second.... Disable with -1
817 | """
818 | _tln.TLN_AssignInputJoystick(player, joystick_index)
819 |
820 | def define_input_key(self, player: int, input: int, key: int):
821 | """
822 | Assigns a keyboard input to a player
823 |
824 | :param player: player identifier to configure (PLAYER1 - PLAYER4)
825 | :param input: input to assign, member of :class:`Input`
826 | :param key: ASCII key value or scancode as defined in SDL.h
827 | """
828 | _tln.TLN_DefineInputKey(player, input, key)
829 |
830 | def define_input_button(self, player: int, input: int, button: int):
831 | """
832 | Assigns a joystick button input to a player
833 |
834 | :param player: player identifier to configure (PLAYER1 - PLAYER4)
835 | :param input: input to assign, member of :class:`Input`
836 | :param button: button index
837 | """
838 | _tln.TLN_DefineInputButton(player, input, button)
839 |
840 | def draw_frame(self, num_frame=0):
841 | """
842 | Deprecated, kept for old source code compatibility. Subsumed by :meth:`Window.process`.
843 | """
844 |
845 | def wait_redraw(self):
846 | """
847 | In multi-threaded windows, it waits until the current frame has finished rendering.
848 | """
849 | _tln.TLN_WaitRedraw()
850 |
851 | def config_crt_effect(self, crt_type: int, rf_blur: bool):
852 | """
853 | Enables CRT simulation post-processing effect to give true retro appeareance
854 |
855 | :param crt_type: One possible value of \ref CRT class
856 | :param rf_blur: Optional RF (horizontal) blur, increases CPU usage
857 | """
858 | _tln.TLN_ConfigCRTEffect(crt_type, rf_blur)
859 |
860 | def enable_crt_effect(self, overlay_id, overlay_blend, threshold, v0, v1, v2, v3, blur, glow_factor):
861 | """
862 | Obsolete, kept for backward compatibility with pre- 2.10 release. Use config_crt_effect() instead.
863 | """
864 | _tln.TLN_EnableCRTEffect(overlay_id, overlay_blend, threshold, v0, v1, v2, v3, blur, glow_factor)
865 |
866 | def disable_crt_effect(self):
867 | """
868 | Disables the CRT post-processing effect enabled with :meth:`Window.enable_crt_effect`
869 | """
870 | _tln.TLN_DisableCRTEffect()
871 |
872 | def set_sdl_callback(self, sdl_callback):
873 | """
874 | Sets callback to process other SDL2 events inside the window
875 | """
876 |
877 | if sdl_callback is None:
878 | self.cb_sdl_func = None
879 | else:
880 | from sdl2 import SDL_Event
881 | _sdl_callback_function = CFUNCTYPE(None, SDL_Event)
882 | self.cb_sdl_func = _sdl_callback_function(sdl_callback)
883 | _tln.TLN_SetSDLCallback(self.cb_sdl_func)
884 |
885 |
886 | def get_ticks(self) -> int:
887 | """
888 | :return: the number of milliseconds since application start
889 | """
890 | return _tln.TLN_GetTicks()
891 |
892 | def delay(self, msecs: int):
893 | """
894 | Suspends execition for a fixed time
895 |
896 | :param msecs: number of milliseconds to pause
897 | """
898 | _tln.TLN_Delay(msecs)
899 |
900 |
901 | # spritesets management -----------------------------------------------------------
902 | _tln.TLN_CreateSpriteset.argtypes = [c_void_p, POINTER(SpriteData), c_int]
903 | _tln.TLN_CreateSpriteset.restype = c_void_p
904 | _tln.TLN_LoadSpriteset.argtypes = [c_char_p]
905 | _tln.TLN_LoadSpriteset.restype = c_void_p
906 | _tln.TLN_CloneSpriteset.argtypes = [c_void_p]
907 | _tln.TLN_CloneSpriteset.restype = c_void_p
908 | _tln.TLN_GetSpriteInfo.argtypes = [c_void_p, c_int, POINTER(SpriteInfo)]
909 | _tln.TLN_GetSpriteInfo.restype = c_bool
910 | _tln.TLN_GetSpritesetPalette.argtypes = [c_void_p]
911 | _tln.TLN_GetSpritesetPalette.restype = c_void_p
912 | _tln.TLN_SetSpritesetData.argtypes = [c_void_p, c_int, POINTER(SpriteData), POINTER(c_ubyte), c_int]
913 | _tln.TLN_SetSpritesetData.restype = c_bool
914 | _tln.TLN_DeleteSpriteset.argtypes = [c_void_p]
915 | _tln.TLN_DeleteSpriteset.restype = c_bool
916 |
917 |
918 | class Spriteset(object):
919 | """
920 | The Spriteset object holds the graphic data used to render moving objects (sprites)
921 |
922 | :ivar palette: original palette attached inside the resource file
923 | """
924 | def __init__(self, handle: c_void_p, owner: bool=True):
925 | self._as_parameter_ = handle
926 | self.owner = owner
927 | self.library = _tln
928 | self.palette = Palette(_tln.TLN_GetSpritesetPalette(handle), False)
929 |
930 | @classmethod
931 | def create(cls, bitmap: "Bitmap", sprite_data: POINTER(SpriteData)) -> "Spriteset":
932 | """
933 | Static method that creates an empty spriteset
934 |
935 | :param bitmap: Bitmap object containing the packaged sprite pictures
936 | :param sprite_data: list of SpriteData tuples describing each sprite pictures
937 | :return: instance of the created object
938 | """
939 | handle = _tln.TLN_CreateSpriteset(bitmap, sprite_data, len(sprite_data))
940 | if handle is not None:
941 | return Spriteset(handle)
942 | else:
943 | _raise_exception()
944 |
945 | @classmethod
946 | def fromfile(cls, filename: str) -> "Spriteset":
947 | """
948 | Static method that loads a spriteset from a pair of png/txt files
949 |
950 | :param filename: png filename with bitmap data
951 | :return: instance of the created object
952 | """
953 | handle = _tln.TLN_LoadSpriteset(_encode_string(filename))
954 | if handle is not None:
955 | return Spriteset(handle)
956 | else:
957 | _raise_exception()
958 |
959 | def clone(self) -> "Spriteset":
960 | """
961 | Creates a copy of the object
962 |
963 | :return: instance of the copy
964 | """
965 | handle = _tln.TLN_CloneSpriteset(self)
966 | if handle is not None:
967 | return Spriteset(handle)
968 | else:
969 | _raise_exception()
970 |
971 | def set_sprite_data(self, entry: int, data: POINTER(SpriteData), pixels: POINTER(c_byte), pitch: int):
972 | """
973 | Sets attributes and pixels of a given sprite inside a spriteset
974 |
975 | :param entry: The entry index inside the spriteset to modify [0, num_sprites - 1]
976 | :param data: Pointer to a user-provided SpriteData structure with sprite description
977 | :param pixels: Pointer to user-provided pixel data block
978 | :param pitch: Number of bytes per scanline of the source pixel data
979 | """
980 | ok = _tln.TLN_SetSpritesetData(self, entry, data, pixels, pitch)
981 | _raise_exception(ok)
982 |
983 | def get_sprite_info(self, entry: int, info: POINTER(SpriteInfo)):
984 | """
985 | Gets info about a given sprite into an user-provided SpriteInfo tuple
986 |
987 | :param entry: sprite index to query
988 | :param info: SpriteInfo to get the data
989 | """
990 | ok = _tln.TLN_GetSpriteInfo(self, entry, info)
991 | _raise_exception(ok)
992 |
993 | def __del__(self):
994 | if self.owner:
995 | ok = self.library.TLN_DeleteSpriteset(self)
996 | _raise_exception(ok)
997 |
998 |
999 | # tilesets management ---------------------------------------------------------
1000 | _tln.TLN_CreateTileset.argtypes = [c_int, c_int, c_int, c_void_p, c_void_p, POINTER(TileAttributes)]
1001 | _tln.TLN_CreateTileset.restype = c_void_p
1002 | _tln.TLN_LoadTileset.argtypes = [c_char_p]
1003 | _tln.TLN_LoadTileset.restype = c_void_p
1004 | _tln.TLN_CloneTileset.argtypes = [c_void_p]
1005 | _tln.TLN_CloneTileset.restype = c_void_p
1006 | _tln.TLN_SetTilesetPixels.argtypes = [c_void_p, c_int, POINTER(c_byte), c_int]
1007 | _tln.TLN_SetTilesetPixels.restype = c_bool
1008 | _tln.TLN_GetTileWidth.argtypes = [c_void_p]
1009 | _tln.TLN_GetTileWidth.restype = c_int
1010 | _tln.TLN_GetTileHeight.argtypes = [c_void_p]
1011 | _tln.TLN_GetTileHeight.restype = c_int
1012 | _tln.TLN_GetTilesetNumTiles.argtypes = [c_void_p]
1013 | _tln.TLN_GetTilesetNumTiles.restype = c_int
1014 | _tln.TLN_GetTilesetPalette.argtypes = [c_void_p]
1015 | _tln.TLN_GetTilesetPalette.restype = c_void_p
1016 | _tln.TLN_GetTilesetSequencePack.argtypes = [c_void_p]
1017 | _tln.TLN_GetTilesetSequencePack.restype = c_void_p
1018 | _tln.TLN_FindSpritesetSprite.argtypes = [c_void_p, c_char_p]
1019 | _tln.TLN_FindSpritesetSprite.restype = c_int
1020 | _tln.TLN_DeleteTileset.argtypes = [c_void_p]
1021 | _tln.TLN_DeleteTileset.restype = c_bool
1022 |
1023 |
1024 | class Tileset(object):
1025 | """
1026 | The Tileset object holds the graphic tiles used to render background layers from a Tilemap
1027 |
1028 | :ivar tile_width: width of each tile
1029 | :ivar tile_height: height of each tile
1030 | :ivar num_tiles: number of unique tiles
1031 | :ivar palette: original palette attached inside the resource file
1032 | :ivar sequence_pack: optional SequencePack embedded inside the Tileset for tileset animation
1033 | """
1034 | def __init__(self, handle: c_void_p, owner: bool=True):
1035 | self._as_parameter_ = handle
1036 | self.owner = owner
1037 | self.library = _tln
1038 | self.tile_width = _tln.TLN_GetTileWidth(handle)
1039 | self.tile_height = _tln.TLN_GetTileHeight(handle)
1040 | self.num_tiles = _tln.TLN_GetTilesetNumTiles(handle)
1041 | self.palette = Palette(_tln.TLN_GetTilesetPalette(handle), False)
1042 | self.sequence_pack = SequencePack(_tln.TLN_GetTilesetSequencePack(handle), False)
1043 |
1044 | @classmethod
1045 | def create(cls, num_tiles: int, width: int, height: int, palette: "Palette", sequence_pack: Optional["SequencePack"]=None, attributes: Optional[POINTER(TileAttributes)]=None) -> "Tileset":
1046 | """
1047 | Static method that creates an empty Tileset at runtime
1048 |
1049 | :param num_tiles: number of unique tiles
1050 | :param width: Width of each tile (must be multiple of 8)
1051 | :param height: Height of each tile (must be multiple of 8)
1052 | :param palette: Palette object
1053 | :param sequence_pack: Optional SequencePack with associated Tileset animations
1054 | :param attributes: Optional list of TileAttributes, one element per tile in the tileset
1055 | :return: instance of the created object
1056 | """
1057 | handle = _tln.TLN_CreateTileset(num_tiles, width, height, palette, sequence_pack, attributes)
1058 | if handle is not None:
1059 | return Tileset(handle)
1060 | else:
1061 | _raise_exception()
1062 |
1063 | @classmethod
1064 | def fromfile(cls, filename: str) -> "Tileset":
1065 | """
1066 | Static method that loads a Tiled TSX tileset from file
1067 |
1068 | :param filename: TSX file with the tileset
1069 | :return:
1070 | """
1071 | handle = _tln.TLN_LoadTileset(_encode_string(filename))
1072 | if handle is not None:
1073 | return Tileset(handle)
1074 | else:
1075 | _raise_exception()
1076 |
1077 | def clone(self) -> "Tileset":
1078 | """
1079 | Creates a copy of the object
1080 |
1081 | :return: instance of the copy
1082 | """
1083 | handle = _tln.TLN_CloneTileset(self)
1084 | if handle is not None:
1085 | return Tileset(handle)
1086 | else:
1087 | _raise_exception()
1088 |
1089 | def set_pixels(self, entry: int, data: POINTER(c_byte), pitch: int):
1090 | """
1091 | Sets pixel data for a single tile
1092 |
1093 | :param entry: Number of tile to set [0, num_tiles - 1]
1094 | :param data: List of bytes with pixel data, one byte per pixel
1095 | :param pitch: Number of bytes per line in source data
1096 | """
1097 | ok = _tln.TLN_SetTilesetPixels(self, entry, data, pitch)
1098 | _raise_exception(ok)
1099 |
1100 | def __del__(self):
1101 | if self.owner:
1102 | ok = self.library.TLN_DeleteTileset(self)
1103 | _raise_exception(ok)
1104 |
1105 |
1106 | # tilemaps management ---------------------------------------------------------
1107 | _tln.TLN_CreateTilemap.argtypes = [c_int, c_int, POINTER(Tile), c_int, c_void_p]
1108 | _tln.TLN_CreateTilemap.restype = c_void_p
1109 | _tln.TLN_LoadTilemap.argtypes = [c_char_p]
1110 | _tln.TLN_LoadTilemap.restype = c_void_p
1111 | _tln.TLN_CloneTilemap.argtypes = [c_void_p]
1112 | _tln.TLN_CloneTilemap.restype = c_void_p
1113 | _tln.TLN_GetTilemapRows.argtypes = [c_void_p]
1114 | _tln.TLN_GetTilemapRows.restype = c_int
1115 | _tln.TLN_GetTilemapCols.argtypes = [c_void_p]
1116 | _tln.TLN_GetTilemapCols.restype = c_int
1117 | _tln.TLN_SetTilemapTileset2.argtypes = [c_void_p, c_void_p, c_int]
1118 | _tln.TLN_SetTilemapTileset2.restype = c_bool
1119 | _tln.TLN_GetTilemapTileset2.argtypes = [c_void_p, c_int]
1120 | _tln.TLN_GetTilemapTileset2.restype = c_void_p
1121 | _tln.TLN_GetTilemapTile.argtypes = [c_void_p, c_int, c_int, POINTER(Tile)]
1122 | _tln.TLN_GetTilemapTile.restype = c_bool
1123 | _tln.TLN_SetTilemapTile.argtypes = [c_void_p, c_int, c_int, POINTER(Tile)]
1124 | _tln.TLN_SetTilemapTile.restype = c_bool
1125 | _tln.TLN_CopyTiles.argtypes = [c_void_p, c_int, c_int, c_int, c_int, c_void_p, c_int, c_int]
1126 | _tln.TLN_CopyTiles.restype = c_bool
1127 | _tln.TLN_DeleteTilemap.argtypes = [c_void_p]
1128 | _tln.TLN_DeleteTilemap.restype = c_bool
1129 |
1130 |
1131 | class Tilemap(object):
1132 | """
1133 | The Tilemap object holds the grid of tiles that define the background layout
1134 |
1135 | :ivar rows: number of rows (vertical cells)
1136 | :ivar cols: number of columns (horizontal cells)
1137 | :ivar tileset: Tileset object attached inside the resource file
1138 | """
1139 | def __init__(self, handle: c_void_p, owner: bool=True):
1140 | self._as_parameter_ = handle
1141 | self.owner = owner
1142 | self.library = _tln
1143 | self.rows = _tln.TLN_GetTilemapRows(handle)
1144 | self.cols = _tln.TLN_GetTilemapCols(handle)
1145 | tileset_handle = _tln.TLN_GetTilemapTileset2(handle, 0)
1146 | if tileset_handle is not None:
1147 | self.tileset = Tileset(tileset_handle, False)
1148 | else:
1149 | self.tileset = None
1150 |
1151 | @classmethod
1152 | def create(cls, rows: int, cols: int, tiles: POINTER(Tile), background_color: int=0, tileset: Optional[Tileset]=None) -> "Tilemap":
1153 | """
1154 | Static method that creates an empty tilemap
1155 |
1156 | :param rows: Number of rows (vertical dimension)
1157 | :param cols: Number of cols (horizontal dimension)
1158 | :param tiles: List of Tile objects with tile data
1159 | :param background_color: optional Color object with default background color
1160 | :param tileset: Optional reference to associated tileset
1161 | :return: instance of the created object
1162 | """
1163 | handle = _tln.TLN_CreateTilemap(rows, cols, tiles, background_color, tileset)
1164 | if handle is not None:
1165 | return Tilemap(handle)
1166 | else:
1167 | _raise_exception()
1168 |
1169 | @classmethod
1170 | def fromfile(cls, filename: str, layer_name: Optional[str]=None) -> "Tilemap":
1171 | """
1172 | Static method that loads a Tiled TMX tilemap from file
1173 |
1174 | :param filename: TMX file with the tilemap
1175 | :param layer_name: Optional name of the layer to load when the TMX file has more than one layer. \
1176 | By default it loads the first layer inside the TMX
1177 | :return: instance of the created object
1178 | """
1179 | handle = _tln.TLN_LoadTilemap(_encode_string(filename), _encode_string(layer_name))
1180 | if handle is not None:
1181 | return Tilemap(handle)
1182 | else:
1183 | _raise_exception()
1184 |
1185 | def clone(self) -> "Tilemap":
1186 | """
1187 | Creates a copy of the object
1188 |
1189 | :return: instance of the copy
1190 | """
1191 | handle = _tln.TLN_CloneTilemap(self)
1192 | if handle is not None:
1193 | return Tilemap(handle)
1194 | else:
1195 | _raise_exception()
1196 |
1197 | def get_tileset(self, index: int=0) -> Tileset:
1198 | """
1199 | Returns the nth tileset associated tileset to the specified tilemap
1200 | :param index: Tileset index (0 - 7), 0 by default
1201 | """
1202 | return Tileset(_tln.TLN_GetTilemapTileset2(self, index), False)
1203 |
1204 | def set_tileset(self, tileset: Tileset, index:int = 0):
1205 | """
1206 | Sets the nth tileset associated tileset to the specified tilemap
1207 | :param tileset: Reference to tileset object to associate
1208 | :param index: Tileset index (0 - 7), 0 by default
1209 | """
1210 | ok = _tln.TLN_SetTilemapTileset2(self, tileset, index)
1211 | _raise_exception(ok)
1212 |
1213 | def get_tile(self, row: int, col: int, tile_info: POINTER(TileInfo)):
1214 | """
1215 | Gets data about a given tile
1216 |
1217 | :param row: Vertical position of the tile (0 <= row < rows)
1218 | :param col: Horizontal position of the tile (0 <= col < cols)
1219 | :param tile_info: pointer to user-provided :class:`TileInfo` object where to get the data
1220 | """
1221 | ok = _tln.TLN_GetTilemapTile(self, row, col, tile_info)
1222 | _raise_exception(ok)
1223 |
1224 | def set_tile(self, row: int, col: int, tile_info: Optional[POINTER(TileInfo)]):
1225 | """
1226 | Sets a tile inside the tilemap
1227 |
1228 | :param row: Vertical position of the tile (0 <= row < rows)
1229 | :param col: Horizontal position of the tile (0 <= col < cols)
1230 | :param tile_info: pointer to user-provided :class:`Tile` object, or None to erase
1231 | """
1232 | if tile_info is not None:
1233 | ok = _tln.TLN_SetTilemapTile(self, row, col, tile_info)
1234 | else:
1235 | tile_info = Tile()
1236 | tile_info.index = 0
1237 | ok = _tln.TLN_SetTilemapTile(self, row, col, tile_info)
1238 | del tile_info
1239 | _raise_exception(ok)
1240 |
1241 | def copy_tiles(self, src_row: int, src_col: int, num_rows: int, num_cols: int, dst_tilemap: "Tilemap", dst_row: int, dst_col: int):
1242 | """
1243 | Copies blocks of tiles between two tilemaps
1244 |
1245 | :param src_row: Starting row (vertical position) inside the source tilemap
1246 | :param src_col: Starting column (horizontal position) inside the source tilemap
1247 | :param num_rows: Number of rows to copy
1248 | :param num_cols: Number of columns to copy
1249 | :param dst_tilemap: Target tilemap
1250 | :param dst_row: Starting row (vertical position) inside the target tilemap
1251 | :param dst_col: Starting column (horizontal position) inside the target tilemap
1252 | """
1253 | ok = _tln.TLN_CopyTiles(self, src_row, src_col, num_rows, num_cols, dst_tilemap, dst_row, dst_col)
1254 | _raise_exception(ok)
1255 |
1256 | def __del__(self):
1257 | if self.owner:
1258 | ok = self.library.TLN_DeleteTilemap(self)
1259 | _raise_exception(ok)
1260 |
1261 |
1262 | # color tables management -----------------------------------------------------
1263 | _tln.TLN_CreatePalette.argtypes = [c_int]
1264 | _tln.TLN_CreatePalette.restype = c_void_p
1265 | _tln.TLN_LoadPalette.argtypes = [c_char_p]
1266 | _tln.TLN_LoadPalette.restype = c_void_p
1267 | _tln.TLN_ClonePalette.argtypes = [c_void_p]
1268 | _tln.TLN_ClonePalette.restype = c_void_p
1269 | _tln.TLN_SetPaletteColor.argtypes = [c_void_p, c_int, c_ubyte, c_ubyte, c_ubyte]
1270 | _tln.TLN_SetPaletteColor.restype = c_bool
1271 | _tln.TLN_MixPalettes.argtypes = [c_void_p, c_void_p, c_void_p, c_ubyte]
1272 | _tln.TLN_MixPalettes.restype = c_bool
1273 | _tln.TLN_AddPaletteColor.argtypes = [c_void_p, c_ubyte, c_ubyte, c_ubyte, c_ubyte, c_ubyte]
1274 | _tln.TLN_AddPaletteColor.restype = c_bool
1275 | _tln.TLN_SubPaletteColor.argtypes = [c_void_p, c_ubyte, c_ubyte, c_ubyte, c_ubyte, c_ubyte]
1276 | _tln.TLN_SubPaletteColor.restype = c_bool
1277 | _tln.TLN_ModPaletteColor.argtypes = [c_void_p, c_ubyte, c_ubyte, c_ubyte, c_ubyte, c_ubyte]
1278 | _tln.TLN_ModPaletteColor.restype = c_bool
1279 | _tln.TLN_GetPaletteData.argtypes = [c_void_p, c_int]
1280 | _tln.TLN_GetPaletteData.restype = POINTER(c_ubyte)
1281 | _tln.TLN_DeletePalette.argtypes = [c_void_p]
1282 | _tln.TLN_DeletePalette.restype = c_bool
1283 |
1284 |
1285 | class Palette(object):
1286 | """
1287 | The Palette object holds the color tables used by tileesets and spritesets to render sprites and backgrounds
1288 | """
1289 | def __init__(self, handle: c_void_p, owner:bool = True):
1290 | self._as_parameter_ = handle
1291 | self.owner = owner
1292 | self.library = _tln
1293 |
1294 | @classmethod
1295 | def create(cls, num_entries: int=256) -> "Palette":
1296 | """
1297 | Static method that creates an empty palette
1298 |
1299 | :param num_entries: optional number of colors to hold (up to 256, default value)
1300 | :return: instance of the created object
1301 | """
1302 | handle = _tln.TLN_CreatePalette(num_entries)
1303 | if handle is not None:
1304 | return Palette(handle)
1305 | else:
1306 | _raise_exception()
1307 |
1308 | @classmethod
1309 | def fromfile(cls, filename: str) -> "Palette":
1310 | """
1311 | Static method that loads a palette from an Adobe Color Table (.act) file
1312 |
1313 | :param filename: name of the .act file to load
1314 | :return: instance of the created object
1315 | """
1316 | handle = _tln.TLN_LoadPalette(_encode_string(filename))
1317 | if handle is not None:
1318 | return Palette(handle)
1319 | else:
1320 | _raise_exception()
1321 |
1322 | def clone(self) -> "Palette":
1323 | """
1324 | Creates a copy of the object
1325 |
1326 | :return: instance of the copy
1327 | """
1328 | handle = _tln.TLN_ClonePalette(self)
1329 | if handle is not None:
1330 | return Palette(handle)
1331 | else:
1332 | _raise_exception()
1333 |
1334 | def set_color(self, entry: int, color: Color):
1335 | """
1336 | Sets the RGB color value of a palette entry
1337 |
1338 | :param entry: Index of the palette entry to modify (0-255)
1339 | :param color: Color object with the r,g,b components of the color
1340 | """
1341 | ok = _tln.TLN_SetPaletteColor(self, entry, color.r, color.g, color.b)
1342 | _raise_exception(ok)
1343 |
1344 | def mix(self, src_palette1: "Palette", src_palette2: "Palette", factor: int):
1345 | """
1346 | Mixes two palettes
1347 |
1348 | :param src_palette1: First palette to mix
1349 | :param src_palette2: Second palette to mix
1350 | :param factor: Integer value with percentage of mix (0-100)
1351 | """
1352 | ok = _tln.TLN_MixPalettes(src_palette1, src_palette2, self, factor)
1353 | _raise_exception(ok)
1354 |
1355 | def add_color(self, first: int, count: int, color: Color):
1356 | """
1357 | Modifies a range of colors by adding the provided color value to the selected range.
1358 | The result is always a brighter color.
1359 |
1360 | :param first: index of the first color entry to modify
1361 | :param count: number of colors from start to modify
1362 | :param color: Color object to add
1363 | """
1364 | ok = _tln.TLN_AddPaletteColor(self, first, count, color.r, color.g, color.b)
1365 | _raise_exception(ok)
1366 |
1367 | def sub_color(self, first: int, count: int, color: Color):
1368 | """
1369 | Modifies a range of colors by subtracting the provided color value to the selected range.
1370 | The result is always a darker color.
1371 |
1372 | :param first: index of the first color entry to modify
1373 | :param count: number of colors from start to modify
1374 | :param color: Color object to subtract
1375 | """
1376 | ok = _tln.TLN_SubPaletteColor(self, first, count, color.r, color.g, color.b)
1377 | _raise_exception(ok)
1378 |
1379 | def mod_color(self, first: int, count: int, color: Color):
1380 | """
1381 | Modifies a range of colors by modulating (normalized product) the provided color value to the selected range.
1382 | The result is always a darker color.
1383 |
1384 | :param first: index of the first color entry to modify
1385 | :param count: number of colors from start to modify
1386 | :param color: Color object to modulate
1387 | """
1388 | ok = _tln.TLN_ModPaletteColor(self, first, count, color.r, color.g, color.b)
1389 | _raise_exception(ok)
1390 |
1391 | def __del__(self):
1392 | if self.owner:
1393 | ok = self.library.TLN_DeletePalette(self)
1394 | _raise_exception(ok)
1395 |
1396 |
1397 | # bitmaps ---------------------------------------------------------------------
1398 | _tln.TLN_CreateBitmap.argtypes = [c_int, c_int, c_int]
1399 | _tln.TLN_CreateBitmap.restype = c_void_p
1400 | _tln.TLN_LoadBitmap.argtypes = [c_char_p]
1401 | _tln.TLN_LoadBitmap.restype = c_void_p
1402 | _tln.TLN_CloneBitmap.argtypes = [c_void_p]
1403 | _tln.TLN_CloneBitmap.restype = c_void_p
1404 | _tln.TLN_GetBitmapPtr.argtypes = [c_void_p, c_int, c_int]
1405 | _tln.TLN_GetBitmapPtr.restype = POINTER(c_ubyte)
1406 | _tln.TLN_GetBitmapWidth.argtypes = [c_void_p]
1407 | _tln.TLN_GetBitmapWidth.restype = c_int
1408 | _tln.TLN_GetBitmapHeight.argtypes = [c_void_p]
1409 | _tln.TLN_GetBitmapHeight.restype = c_int
1410 | _tln.TLN_GetBitmapDepth.argtypes = [c_void_p]
1411 | _tln.TLN_GetBitmapDepth.restype = c_int
1412 | _tln.TLN_GetBitmapPitch.argtypes = [c_void_p]
1413 | _tln.TLN_GetBitmapPitch.restype = c_int
1414 | _tln.TLN_GetBitmapPalette.argtypes = [c_void_p]
1415 | _tln.TLN_GetBitmapPalette.restype = c_void_p
1416 | _tln.TLN_DeleteBitmap.argtypes = [c_void_p]
1417 | _tln.TLN_DeleteBitmap.restype = c_bool
1418 |
1419 |
1420 | class Bitmap(object):
1421 | """
1422 | The Bitmap object holds graphic data used to build in backgrounds, Tileset and Spriteset objects
1423 |
1424 | :ivar width: number of horizontal pixels
1425 | :ivar height: number of vertical pixels
1426 | :ivar depth: number of bits per pixel
1427 | :ivar pitch: number of bytes per each scanline
1428 | :ivar palette: Palette object attached inside the bitmap
1429 | """
1430 | def __init__(self, handle: c_void_p, owner=True):
1431 | self._as_parameter_ = handle
1432 | self.owner = owner
1433 | self.library = _tln
1434 | self.width = _tln.TLN_GetBitmapWidth(handle)
1435 | self.height = _tln.TLN_GetBitmapHeight(handle)
1436 | self.depth = _tln.TLN_GetBitmapDepth(handle)
1437 | self.pitch = _tln.TLN_GetBitmapPitch(handle)
1438 | self.palette = Palette(_tln.TLN_GetBitmapPalette(handle), False)
1439 |
1440 | @classmethod
1441 | def create(cls, width: int, height: int, bpp: int=8) -> "Bitmap":
1442 | """
1443 | Static method that creates an empty bitmap
1444 |
1445 | :param width: Width in pixels
1446 | :param height: Height in pixels
1447 | :param bpp: Optional bits per pixel (8 by default)
1448 | :return: instance of the created object
1449 | """
1450 | handle = _tln.TLN_CreateBitmap(width, height, bpp)
1451 | if handle is not None:
1452 | return Bitmap(handle)
1453 | else:
1454 | _raise_exception()
1455 |
1456 | @classmethod
1457 | def fromfile(cls, filename: str) -> "Bitmap":
1458 | """
1459 | Static method that loads a BMP or PNG file
1460 |
1461 | :param filename: name of the file to load (.bmp or .png)
1462 | :return: instance of the created object
1463 | """
1464 | handle = _tln.TLN_LoadBitmap(_encode_string(filename))
1465 | if handle is not None:
1466 | return Bitmap(handle)
1467 | else:
1468 | _raise_exception()
1469 |
1470 | def clone(self) -> "Bitmap":
1471 | """
1472 | Creates a copy of the object
1473 |
1474 | :return: instance of the copy
1475 | """
1476 | handle = _tln.TLN_CloneBitmap(self)
1477 | if handle is not None:
1478 | return Bitmap(handle)
1479 | else:
1480 | _raise_exception()
1481 |
1482 | def get_data(self, x: int, y: int) -> c_void_p:
1483 | """
1484 | Returns a pointer to the starting memory address
1485 |
1486 | :param x: Starting x position [0, width - 1]
1487 | :param y: Starting y position [0, height - 1]
1488 | :return: pointer
1489 | """
1490 | return _tln.TLN_GetBitmapPtr(self, x, y)
1491 |
1492 | def __del__(self):
1493 | if self.owner:
1494 | ok = self.library.TLN_DeleteBitmap(self)
1495 | _raise_exception(ok)
1496 |
1497 | class ObjectList(object):
1498 | """
1499 | TODO: ObjectList reference
1500 | """
1501 |
1502 |
1503 | # sequences management --------------------------------------------------------
1504 | _tln.TLN_CreateSequence.argtypes = [c_char_p, c_int, c_int, POINTER(SequenceFrame)]
1505 | _tln.TLN_CreateSequence.restype = c_void_p
1506 | _tln.TLN_CreateCycle.argtypes = [c_char_p, c_int, POINTER(ColorStrip)]
1507 | _tln.TLN_CreateCycle.restype = c_void_p
1508 | _tln.TLN_CreateSpriteSequence.argtypes = [c_char_p, c_void_p, c_char_p, c_int]
1509 | _tln.TLN_CreateSpriteSequence.restype = c_void_p
1510 | _tln.TLN_CloneSequence.argtypes = [c_void_p]
1511 | _tln.TLN_CloneSequence.restype = c_void_p
1512 | _tln.TLN_GetSequenceInfo.argtypes = [c_void_p, POINTER(SequenceInfo)]
1513 | _tln.TLN_GetSequenceInfo.restype = c_bool
1514 | _tln.TLN_DeleteSequence.argtypes = [c_void_p]
1515 | _tln.TLN_DeleteSequence.restype = c_bool
1516 |
1517 |
1518 | class Sequence(object):
1519 | """
1520 | The Sequence object holds the sequences to feed the animation engine
1521 | """
1522 | def __init__(self, handle: c_void_p, owner: bool=True):
1523 | self._as_parameter_ = handle
1524 | self.owner = owner
1525 | self.library = _tln
1526 |
1527 | @classmethod
1528 | def create_sequence(cls, name: str, target: int, frames: List[SequenceFrame]) -> "Sequence":
1529 | """
1530 | Static method that creates an empty Sequence for Sprite and Tileset animations
1531 |
1532 | :param name: String with an unique name to identify the sequence inside a SequencePack object
1533 | :param target: For Tileset animations, the tile index to animate
1534 | :param frames: List with SequenceFrame objects, one for each frame of animation
1535 | :return: instance of the created object
1536 | """
1537 | handle = _tln.TLN_CreateSequence(_encode_string(name), target, len(frames), frames)
1538 | if handle is not None:
1539 | return Sequence(handle)
1540 | else:
1541 | _raise_exception()
1542 |
1543 | @classmethod
1544 | def create_cycle(cls, name: str, strips: List[ColorStrip]) -> "Sequence":
1545 | """
1546 | Static method that creates an empty Sequence for Palette animations
1547 |
1548 | :param name: String with an unique name to identify the sequence inside a SequencePack object
1549 | :param strips: List with ColorStrip objects, one for each frame of animation
1550 | :return: instance of the created object
1551 | """
1552 | handle = _tln.TLN_CreateCycle(_encode_string(name), len(strips), strips)
1553 | if handle is not None:
1554 | return Sequence(handle)
1555 | else:
1556 | _raise_exception()
1557 |
1558 | @classmethod
1559 | def create_sprite_sequence(cls, spriteset: Spriteset, basename: str, delay: int) -> "Sequence":
1560 | """
1561 | Static method that creates a sprite sequence based on names inside a spriteset
1562 |
1563 | :param spriteset: Reference to the spriteset with frames to animate
1564 | :param basename: Base of the sprite name for the numbered sequence
1565 | :param delay: Number of frames to hold each animation frame
1566 | :return: created Sequence object or None if error
1567 | """
1568 | handle = _tln.TLN_CreateSpriteSequence(None, spriteset, _encode_string(basename), delay)
1569 | if handle is not None:
1570 | return Sequence(handle)
1571 | else:
1572 | _raise_exception()
1573 |
1574 | def clone(self) -> "Sequence":
1575 | """
1576 | Creates a copy of the object
1577 |
1578 | :return: instance of the copy
1579 | """
1580 | handle = _tln.TLN_CloneSequence(self)
1581 | if handle is not None:
1582 | return Sequence(handle)
1583 | else:
1584 | _raise_exception()
1585 |
1586 | def get_info(self, info: POINTER(SequenceInfo)):
1587 | """
1588 | Returns runtime info about a given sequence
1589 |
1590 | :param info: user-provided SequenceInfo structure to hold the returned data
1591 | """
1592 | return _tln.TLN_GetSequenceInfo(self, info)
1593 |
1594 | def __del__(self):
1595 | if self.owner:
1596 | ok = self.library.TLN_DeleteSequence(self)
1597 | _raise_exception(ok)
1598 |
1599 |
1600 | # sequence pack management --------------------------------------------------------
1601 | _tln.TLN_CreateSequencePack.restype = c_void_p
1602 | _tln.TLN_LoadSequencePack.argtypes = [c_char_p]
1603 | _tln.TLN_LoadSequencePack.restype = c_void_p
1604 | _tln.TLN_FindSequence.argtypes = [c_void_p, c_char_p]
1605 | _tln.TLN_FindSequence.restype = c_void_p
1606 | _tln.TLN_GetSequence.argtypes = [c_void_p]
1607 | _tln.TLN_GetSequence.restype = c_void_p
1608 | _tln.TLN_GetSequencePackCount.argtypes = [c_void_p]
1609 | _tln.TLN_GetSequencePackCount.restype = c_int
1610 | _tln.TLN_AddSequenceToPack.argtypes = [c_void_p, c_void_p]
1611 | _tln.TLN_AddSequenceToPack.restype = c_bool
1612 | _tln.TLN_DeleteSequencePack.argtypes = [c_void_p]
1613 | _tln.TLN_DeleteSequencePack.restype = c_bool
1614 |
1615 |
1616 | class SequencePack(object):
1617 | """
1618 | The SequencePack object holds a collection of Sequence objects
1619 |
1620 | :ivar count: number of sequences inside the pack
1621 | :ivar sequences: dictionary of contained sequences indexed by name
1622 | """
1623 | def __init__(self, handle: c_void_p, owner: bool=True):
1624 | self._as_parameter_ = handle
1625 | self.owner = owner
1626 | self.library = _tln
1627 | self.count = _tln.TLN_GetSequencePackCount(self)
1628 | self.sequences = dict()
1629 | sequence_info = SequenceInfo()
1630 | for s in range(self.count):
1631 | sequence = self.get_sequence(s)
1632 | sequence.get_info(sequence_info)
1633 | self.sequences[_decode_string(sequence_info.name)] = sequence
1634 |
1635 | @classmethod
1636 | def create(cls) -> "SequencePack":
1637 | """
1638 | Static method that creates an empty SequencePack object
1639 |
1640 | :return: instance of the created object
1641 | """
1642 | handle = _tln.TLN_CreateSequencePack()
1643 | if handle is not None:
1644 | return SequencePack(handle)
1645 | else:
1646 | _raise_exception()
1647 |
1648 | @classmethod
1649 | def fromfile(cls, filename: str) -> "SequencePack":
1650 | """
1651 | Static method that loads a SQX file with sequence data (XML-based)
1652 |
1653 | :param filename: Name of the SQX file to load
1654 | :return: instance of the created object
1655 | """
1656 | handle = _tln.TLN_LoadSequencePack(_encode_string(filename))
1657 | if handle is not None:
1658 | return SequencePack(handle)
1659 | else:
1660 | _raise_exception()
1661 |
1662 | def get_sequence(self, index: int) -> Sequence:
1663 | """
1664 | Returns the nth sequence inside a sequence pack
1665 |
1666 | :param index: zero-based index number
1667 | :return: Sequence object
1668 | """
1669 | handle = _tln.TLN_GetSequence(self, index)
1670 | if handle is not None:
1671 | return Sequence(handle, False)
1672 | else:
1673 | _raise_exception()
1674 |
1675 | def find_sequence(self, name: str) -> Sequence:
1676 | """
1677 | Finds a Sequence by its name
1678 |
1679 | :param name: name of the Sequence to find
1680 | :return: Sequence object if found, or None if error
1681 | """
1682 | handle = _tln.TLN_FindSequence(self, _encode_string(name))
1683 | if handle is not None:
1684 | return Sequence(handle)
1685 | else:
1686 | _raise_exception()
1687 |
1688 | def add_sequence(self, sequence: Sequence):
1689 | """
1690 | Adds a Sequence to a SequencePack
1691 |
1692 | :param sequence: Sequence object to add
1693 | """
1694 | sequence_info = SequenceInfo()
1695 | sequence.get_info(sequence_info)
1696 | ok = _tln.TLN_AddSequenceToPack(self, sequence)
1697 | if ok:
1698 | self.sequences[_decode_string(sequence_info.name)] = sequence
1699 | _raise_exception(ok)
1700 |
1701 | def __del__(self):
1702 | if self.owner:
1703 | ok = self.library.TLN_DeleteSequencePack(self)
1704 | _raise_exception(ok)
1705 |
1706 |
1707 | # layer management ------------------------------------------------------------
1708 | _tln.TLN_SetLayer.argtypes = [c_int, c_void_p, c_void_p]
1709 | _tln.TLN_SetLayer.restype = c_bool
1710 |
1711 | _tln.TLN_SetLayerTilemap.argtypes = [c_int, c_void_p]
1712 | _tln.TLN_SetLayerTilemap.restype = c_bool
1713 | _tln.TLN_SetLayerBitmap.argtypes = [c_int, c_void_p]
1714 | _tln.TLN_SetLayerBitmap.restype = c_bool
1715 | _tln.TLN_SetLayerObjects.argtypes = [c_int, c_void_p, c_void_p]
1716 | _tln.TLN_SetLayerObjects.restype = c_bool
1717 | _tln.TLN_SetLayerPalette.argtypes = [c_int, c_void_p]
1718 | _tln.TLN_SetLayerPalette.restype = c_bool
1719 | _tln.TLN_SetLayerPosition.argtypes = [c_int, c_int, c_int]
1720 | _tln.TLN_SetLayerPosition.restype = c_bool
1721 | _tln.TLN_SetLayerScaling.argtypes = [c_int, c_float, c_float]
1722 | _tln.TLN_SetLayerScaling.restype = c_bool
1723 | _tln.TLN_SetLayerTransform.argtypes = [c_int, c_float, c_float, c_float, c_float, c_float]
1724 | _tln.TLN_SetLayerTransform.restype = c_bool
1725 | _tln.TLN_SetLayerPixelMapping.argtypes = [c_int, POINTER(PixelMap)]
1726 | _tln.TLN_SetLayerPixelMapping.restype = c_bool
1727 | _tln.TLN_ResetLayerMode.argtypes = [c_int]
1728 | _tln.TLN_ResetLayerMode.restype = c_bool
1729 | _tln.TLN_SetLayerBlendMode.argtypes = [c_int, c_int, c_ubyte]
1730 | _tln.TLN_SetLayerBlendMode.restype = c_bool
1731 | _tln.TLN_SetLayerColumnOffset.argtypes = [c_int, POINTER(c_int)]
1732 | _tln.TLN_SetLayerColumnOffset.restype = c_bool
1733 | _tln.TLN_SetLayerClip.argtypes = [c_int, c_int, c_int, c_int, c_int]
1734 | _tln.TLN_SetLayerClip.restype = c_bool
1735 | _tln.TLN_DisableLayerClip.argtypes = [c_int]
1736 | _tln.TLN_DisableLayerClip.restype = c_bool
1737 | _tln.TLN_SetLayerMosaic.argtypes = [c_int, c_int, c_int]
1738 | _tln.TLN_SetLayerMosaic.restype = c_bool
1739 | _tln.TLN_DisableLayerMosaic.argtypes = [c_int]
1740 | _tln.TLN_DisableLayerMosaic.restype = c_bool
1741 | _tln.TLN_DisableLayer.argtypes = [c_int]
1742 | _tln.TLN_DisableLayer.restype = c_bool
1743 | _tln.TLN_EnableLayer.argtypes = [c_int]
1744 | _tln.TLN_EnableLayer.restype = c_bool
1745 | _tln.TLN_GetLayerPalette.argtypes = [c_int]
1746 | _tln.TLN_GetLayerPalette.restype = c_void_p
1747 | _tln.TLN_GetLayerTile.argtypes = [c_int, c_int, c_int, POINTER(TileInfo)]
1748 | _tln.TLN_GetLayerTile.restype = c_bool
1749 | _tln.TLN_GetLayerWidth.argtypes = [c_int]
1750 | _tln.TLN_GetLayerWidth.restype = c_int
1751 | _tln.TLN_GetLayerHeight.argtypes = [c_int]
1752 | _tln.TLN_GetLayerHeight.restype = c_int
1753 | _tln.TLN_SetLayerPriority.argtypes = [c_int, c_bool]
1754 | _tln.TLN_SetLayerPriority.restype = c_bool
1755 |
1756 | class Layer(object):
1757 | """
1758 | The Layer object manages each tiled background plane
1759 |
1760 | :ivar index: layer index, from 0 to num_layers - 1
1761 | :ivar width: width in pixels of the assigned tilemap
1762 | :ivar height: height in pixels of the assigned tilemap
1763 | :ivar tilemap: assigned Tilemap object, for tilemap layers
1764 | :ivar bitmap: assigned Bitmap object, for bitmap layers
1765 | :ivar objectlist: assigned ObjectList object, for object list layers
1766 | """
1767 | def __init__(self, index):
1768 | self.index = index
1769 | self._as_parameter_ = index
1770 | self.width = 0
1771 | self.height = 0
1772 | self.tilemap = None
1773 | self.bitmap = None
1774 | self.objectlist = None
1775 |
1776 | def setup(self, tilemap: Tilemap, tileset: Optional[Tileset]=None):
1777 | """
1778 | Enables a background layer by setting the specified tilemap and optional tileset
1779 |
1780 | :param tilemap: Tilemap object with background layout
1781 | :param tileset: Optional Tileset object. If not set, the Tilemap's own Tileset is selected
1782 | """
1783 | ok = _tln.TLN_SetLayer(self, tileset, tilemap)
1784 | if ok is True:
1785 | self.width = _tln.TLN_GetLayerWidth(self)
1786 | self.height = _tln.TLN_GetLayerHeight(self)
1787 | self.tilemap = tilemap
1788 | if tileset is not None:
1789 | tilemap.set_tileset(tileset)
1790 | return ok
1791 | else:
1792 | _raise_exception()
1793 |
1794 | def set_tilemap(self, tilemap: Tilemap):
1795 | """
1796 | Configures a tiled background layer with the specified tilemap
1797 |
1798 | :param tilemap: Tilemap object with background layout
1799 | """
1800 | ok = _tln.TLN_SetLayerTilemap(self, tilemap)
1801 | if ok is True:
1802 | self.width = _tln.TLN_GetLayerWidth(self)
1803 | self.height = _tln.TLN_GetLayerHeight(self)
1804 | self.tilemap = tilemap
1805 | self.bitmap = None
1806 | self.objectlist = None
1807 | return ok
1808 | else:
1809 | _raise_exception()
1810 |
1811 | def set_bitmap(self, bitmap: Bitmap):
1812 | """
1813 | Configures a background layer with the specified full bitmap
1814 |
1815 | :param bitmap: Bitmap object with full bitmap background
1816 | """
1817 | ok = _tln.TLN_SetLayerBitmap(self, bitmap)
1818 | if ok is True:
1819 | self.width = _tln.TLN_GetLayerWidth(self)
1820 | self.height = _tln.TLN_GetLayerHeight(self)
1821 | self.bitmap = bitmap
1822 | self.tilemap = None
1823 | self.objectlist = None
1824 | return ok
1825 | else:
1826 | _raise_exception()
1827 |
1828 | def set_palette(self, palette: Palette):
1829 | """
1830 | Sets the color palette to the layer
1831 |
1832 | :param palette: Palette object to assign. By default the Tileset's own palette is used
1833 | """
1834 | ok = _tln.TLN_SetLayerPalette(self, palette)
1835 | _raise_exception(ok)
1836 |
1837 | def set_position(self, x: int, y: int):
1838 | """
1839 | Sets the position of the tileset that corresponds to the upper left corner of the viewport
1840 |
1841 | :param x: horizontal position
1842 | :param y: vertical position
1843 | """
1844 | ok = _tln.TLN_SetLayerPosition(self, int(x), int(y))
1845 | _raise_exception(ok)
1846 |
1847 | def set_scaling(self, sx: float, sy: float):
1848 | """
1849 | Enables layer scaling
1850 |
1851 | :param sx: floating-point value with horizontal scaling factor
1852 | :param sy: floating-point value with vertical scaling factor
1853 | """
1854 | ok = _tln.TLN_SetLayerScaling(self, sx, sy)
1855 | _raise_exception(ok)
1856 |
1857 | def set_transform(self, angle: float, x: float, y: float, sx: float, sy: float):
1858 | """
1859 | Enables layer affine transformation (rotation and scaling). All parameters are floating point values
1860 |
1861 | :param angle: rotation angle in degrees
1862 | :param x: horizontal displacement in screen space where the rotation center is located
1863 | :param y: vertical displacement in screen space where the rotation center is located
1864 | :param sx: horizontal scaling factor
1865 | :param sy: vertical scaling factor
1866 | """
1867 | ok = _tln.TLN_SetLayerTransform(self, angle, x, y, sx, sy)
1868 | _raise_exception(ok)
1869 |
1870 | def set_pixel_mapping(self, pixel_map: POINTER(PixelMap)):
1871 | """
1872 | Enables pixel mapping displacement table
1873 |
1874 | :param pixel_map: user-provided list of PixelMap objects of hres*vres size: one item per screen pixel
1875 | """
1876 | ok = _tln.TLN_SetLayerPixelMapping(self, pixel_map)
1877 | _raise_exception(ok)
1878 |
1879 | def reset_mode(self):
1880 | """
1881 | Disables all special effects: scaling, affine transform and pixel mapping, and returns to default render mode.
1882 | """
1883 | ok = _tln.TLN_ResetLayerMode(self)
1884 | _raise_exception(ok)
1885 |
1886 | def set_blend_mode(self, mode: int):
1887 | """
1888 | Enables blending mode with background objects
1889 |
1890 | :param mode: One of the :class:`Blend` defined values
1891 | """
1892 | ok = _tln.TLN_SetLayerBlendMode(self, mode, 0)
1893 | _raise_exception(ok)
1894 |
1895 | def set_column_offset(self, offsets: Optional[POINTER(c_int)]=None):
1896 | """
1897 | Enables column offset mode for tiles
1898 |
1899 | :param offsets: User-provided tuple of integers with offsets, one per column of tiles. Pass None to disable
1900 | """
1901 | ok = _tln.TLN_SetLayerColumnOffset(self, offsets)
1902 | _raise_exception(ok)
1903 |
1904 | def set_clip(self, x1: int, y1: int, x2: int, y2: int):
1905 | """
1906 | Enables clipping rectangle
1907 |
1908 | :param x1: left coordinate
1909 | :param y1: top coordinate
1910 | :param x2: right coordinate
1911 | :param y2: bottom coordinate
1912 | """
1913 | ok = _tln.TLN_SetLayerClip(self, x1, y1, x2, y2)
1914 | _raise_exception(ok)
1915 |
1916 | def disable_clip(self):
1917 | """
1918 | Disables clipping rectangle
1919 | """
1920 | ok = _tln.TLN_DisableLayerClip(self)
1921 | _raise_exception(ok)
1922 |
1923 | def set_mosaic(self, pixel_w: int, pixel_h: int):
1924 | """
1925 | Enables mosaic effect (pixelation)
1926 |
1927 | :param pixel_w: horizontal pixel size
1928 | :param pixel_h: vertical pixel size
1929 | """
1930 | ok = _tln.TLN_SetLayerMosaic(self, pixel_w, pixel_h)
1931 | _raise_exception(ok)
1932 |
1933 | def disable_mosaic(self):
1934 | """
1935 | Disables mosaic effect
1936 | """
1937 | ok = _tln.TLN_DisableLayerMosaic(self)
1938 | _raise_exception(ok)
1939 |
1940 | def disable(self):
1941 | """
1942 | Disables the layer so it is not drawn
1943 | """
1944 | ok = _tln.TLN_DisableLayer(self)
1945 | _raise_exception(ok)
1946 |
1947 | def enable(self):
1948 | """
1949 | Re-enables previously disabled layer
1950 | """
1951 | ok = _tln.TLN_EnableLayer(self)
1952 | _raise_exception(ok)
1953 |
1954 | def get_palette(self) -> Palette:
1955 | """
1956 | Gets the layer active palette
1957 |
1958 | :return: Palette object of the Layer
1959 | """
1960 | handle = _tln.TLN_GetLayerPalette(self)
1961 | if handle is not None:
1962 | return Palette(handle)
1963 | else:
1964 | _raise_exception()
1965 |
1966 | def get_tile(self, x: int, y: int, tile_info: POINTER(TileInfo)):
1967 | """
1968 | Gets detailed info about the tile located in Tilemap space
1969 |
1970 | :param x: x position inside the Tilemap
1971 | :param y: y position inside the Tilemap
1972 | :param tile_info: User-provided TileInfo object where to get the data
1973 | """
1974 | ok = _tln.TLN_GetLayerTile(self, x, y, tile_info)
1975 | _raise_exception(ok)
1976 |
1977 | def set_priority(self, enable: bool):
1978 | """
1979 | Sets full layer priority, appearing in front of regular sprites
1980 |
1981 | :param enable: True for enable, False for disable
1982 | """
1983 | ok = _tln.TLN_SetLayerPriority(self, enable)
1984 | _raise_exception(ok)
1985 |
1986 | def set_parallax_factor(self, x: int, y: int):
1987 | """
1988 | Sets layer parallax factor to use in conjunction with Engine.set_world_position()
1989 |
1990 | :param x: Horizontal parallax factor
1991 | :param y: Vertical parallax factor
1992 | """
1993 | ok = _tln.TLN_SetLayerParallaxFactor(self, x, y)
1994 | _raise_exception(ok)
1995 |
1996 |
1997 | # sprite management -----------------------------------------------------------
1998 | _tln.TLN_ConfigSprite.argtypes = [c_int, c_void_p, c_ushort]
1999 | _tln.TLN_ConfigSprite.restype = c_bool
2000 | _tln.TLN_SetSpriteSet.argtypes = [c_int, c_void_p]
2001 | _tln.TLN_SetSpriteSet.restype = c_bool
2002 | _tln.TLN_SetSpriteFlags.argtypes = [c_int, c_ushort]
2003 | _tln.TLN_SetSpriteFlags.restype = c_bool
2004 | _tln.TLN_EnableSpriteFlag.argtypes = [c_int, c_uint, c_bool]
2005 | _tln.TLN_EnableSpriteFlag.restype = c_bool
2006 | _tln.TLN_SetSpritePivot.argtypes = [c_int, c_float, c_float]
2007 | _tln.TLN_SetSpritePivot.restype = c_bool
2008 | _tln.TLN_SetSpritePosition.argtypes = [c_int, c_int, c_int]
2009 | _tln.TLN_SetSpritePosition.restype = c_bool
2010 | _tln.TLN_SetSpritePicture.argtypes = [c_int, c_int]
2011 | _tln.TLN_SetSpritePicture.restype = c_bool
2012 | _tln.TLN_SetSpritePalette.argtypes = [c_int, c_void_p]
2013 | _tln.TLN_SetSpritePalette.restype = c_bool
2014 | _tln.TLN_SetSpriteBlendMode.argtypes = [c_int, c_int, c_ubyte]
2015 | _tln.TLN_SetSpriteBlendMode.restype = c_bool
2016 | _tln.TLN_SetSpriteScaling.argtypes = [c_int, c_float, c_float]
2017 | _tln.TLN_SetSpriteScaling.restype = c_bool
2018 | _tln.TLN_ResetSpriteScaling.argtypes = [c_int]
2019 | _tln.TLN_ResetSpriteScaling.restype = c_bool
2020 | _tln.TLN_GetSpritePicture.argtypes = [c_int]
2021 | _tln.TLN_GetSpritePicture.restype = c_int
2022 | _tln.TLN_GetAvailableSprite.restype = c_int
2023 | _tln.TLN_EnableSpriteCollision.argtypes = [c_int, c_bool]
2024 | _tln.TLN_EnableSpriteCollision.restype = c_bool
2025 | _tln.TLN_GetSpriteCollision.argtypes = [c_int]
2026 | _tln.TLN_GetSpriteCollision.restype = c_bool
2027 | _tln.TLN_DisableSprite.argtypes = [c_int]
2028 | _tln.TLN_DisableSprite.restype = c_bool
2029 | _tln.TLN_GetSpritePalette.argtypes = [c_int]
2030 | _tln.TLN_GetSpritePalette.restype = c_void_p
2031 | _tln.TLN_SetSpriteAnimation.argtypes = [c_int, c_void_p, c_int]
2032 | _tln.TLN_SetSpriteAnimation.restype = c_bool
2033 | _tln.TLN_PauseSpriteAnimation.argtypes = [c_int]
2034 | _tln.TLN_PauseSpriteAnimation.restype = c_bool
2035 | _tln.TLN_ResumeSpriteAnimation.argtypes = [c_int]
2036 | _tln.TLN_ResumeSpriteAnimation.restype = c_bool
2037 | _tln.TLN_GetAnimationState.argtypes = [c_int]
2038 | _tln.TLN_GetAnimationState.restype = c_bool
2039 | _tln.TLN_DisableSpriteAnimation.argtypes = [c_int]
2040 | _tln.TLN_DisableSpriteAnimation.restype = c_bool
2041 | _tln.TLN_GetSpriteState.argtypes = [c_int, POINTER(SpriteState)]
2042 | _tln.TLN_GetSpriteState.restype = c_bool
2043 | _tln.TLN_SetFirstSprite.argtypes = [c_int]
2044 | _tln.TLN_SetFirstSprite.restype = c_bool
2045 | _tln.TLN_SetNextSprite.argtypes = [c_int, c_int]
2046 | _tln.TLN_SetNextSprite.restype = c_bool
2047 | _tln.TLN_EnableSpriteMasking.argtypes = [c_int, c_bool]
2048 | _tln.TLN_EnableSpriteMasking.restype = c_bool
2049 |
2050 | class Sprite(object):
2051 | """
2052 | The Sprite object manages each moving character onscreen
2053 |
2054 | :ivar index: sprite index, from 0 to num_sprites - 1
2055 | :ivar spriteset: assigned Spriteset object
2056 | """
2057 | def __init__(self, index: int):
2058 | self.index = index
2059 | self._as_parameter_ = index
2060 | self.spriteset = None
2061 |
2062 | def setup(self, spriteset: Spriteset, flags: int=0):
2063 | """
2064 | Enables a sprite by setting its Spriteset and optional flags
2065 |
2066 | :param spriteset: Spriteset object with the graphic data of the sprites
2067 | :param flags: Optional combination of defined :class:`Flag` values, 0 by default
2068 | """
2069 | ok = _tln.TLN_ConfigSprite(self, spriteset, flags)
2070 | self.spriteset = spriteset
2071 | _raise_exception(ok)
2072 |
2073 | def set_spriteset(self, spriteset: Spriteset):
2074 | """
2075 | Enables a sprite by setting its Spriteset
2076 |
2077 | :param spriteset: Spriteset object with the graphic data of the sprites
2078 | """
2079 | ok = _tln.TLN_SetSpriteSet(self, spriteset)
2080 | self.spriteset = spriteset
2081 | _raise_exception(ok)
2082 |
2083 | def set_flags(self, flags: int=0):
2084 | """
2085 | Sets modification flags
2086 |
2087 | :param flags: Combination of defined :class:`Flag` values
2088 | """
2089 | ok = _tln.TLN_SetSpriteFlags(self, flags)
2090 | _raise_exception(ok)
2091 |
2092 | def enable_flag(self, flag: int, value: bool=True):
2093 | """
2094 | Enables (or disables) specified flag for a sprite
2095 |
2096 | :param flag: Combination of defined :class:`Flag` values
2097 | :param value: True to enable (default) or False to disable
2098 | """
2099 | ok = _tln.TLN_EnableSpriteFlag(self, flag, value)
2100 | _raise_exception(ok)
2101 |
2102 | def set_pivot(self, u: float, v: float):
2103 | """
2104 | Sets sprite pivot point for placement and scaling source. By default is at (0,0) = top left corner. Uses normalized coordinates in [ 0.0 - 1.0] range.
2105 |
2106 | :param u: Horizontal position (0.0 = full left, 1.0 = full right)
2107 | :param v: Vertical position (0.0 = top, 1.0 = bottom)
2108 | """
2109 | ok = _tln.TLN_SetSpritePivot(self, u, v)
2110 | _raise_exception(ok)
2111 |
2112 | def set_position(self, x: int, y: int):
2113 | """
2114 | Sets the sprite position in screen coordinates
2115 |
2116 | :param x: Horizontal position
2117 | :param y: Vertical position
2118 | """
2119 | ok = _tln.TLN_SetSpritePosition(self, x, y)
2120 | _raise_exception(ok)
2121 |
2122 | def set_world_position(self, x: int, y: int):
2123 | """
2124 | Sets the sprite position in world space coordinates
2125 |
2126 | :param x: Horizontal world position of pivot (0 = left margin)
2127 | :param y: Vertical world position of pivot (0 = top margin)
2128 | """
2129 | ok = _tln.TLN_SetSpriteWorldPosition(self, x, y)
2130 | _raise_exception(ok)
2131 |
2132 | def set_picture(self, picture: Union[int,str]):
2133 | """
2134 | Sets the actual graphic contained in the Spriteset to the sprite
2135 |
2136 | :param picture: can be an integer with the index inside the Spriteset, or a string with its name
2137 | """
2138 | ok = False
2139 | param_type = type(picture)
2140 | if param_type is int:
2141 | ok = _tln.TLN_SetSpritePicture(self, picture)
2142 | elif param_type is str:
2143 | entry = _tln.TLN_FindSpritesetSprite(self.spriteset, picture)
2144 | if entry != -1:
2145 | ok = _tln.TLN_SetSpritePicture(self, entry)
2146 | else:
2147 | return
2148 | else:
2149 | return
2150 | _raise_exception(ok)
2151 |
2152 | def set_palette(self, palette: Palette):
2153 | """
2154 | Assigns a Palette object to the sprite. By default it is assigned wit the Spriteset's own palette
2155 |
2156 | :param palette: Palette object to set
2157 | """
2158 | ok = _tln.TLN_SetSpritePalette(self, palette)
2159 | _raise_exception(ok)
2160 |
2161 | def set_blend_mode(self, mode: int):
2162 | """
2163 | Enables blending mode with background objects
2164 |
2165 | :param mode: One of the :class:`Blend` defined values
2166 | """
2167 | ok = _tln.TLN_SetSpriteBlendMode(self, mode, 0)
2168 | _raise_exception(ok)
2169 |
2170 | def set_scaling(self, sx: float, sy: float):
2171 | """
2172 | Enables sprite scaling
2173 |
2174 | :param sx: floating-point value with horizontal scaling factor
2175 | :param sy: floating-point value with vertical scaling factor
2176 | """
2177 | ok = _tln.TLN_SetSpriteScaling(self, sx, sy)
2178 | _raise_exception(ok)
2179 |
2180 | def reset_mode(self):
2181 | """
2182 | Disables scaling and returns to default render mode.
2183 | """
2184 | ok = _tln.TLN_ResetSpriteScaling(self)
2185 | _raise_exception(ok)
2186 |
2187 | def get_picture(self) -> int:
2188 | """
2189 | Returns the index of the assigned picture from the Spriteset
2190 |
2191 | :return: the graphic index
2192 | """
2193 | return _tln.TLN_GetSpritePicture(self)
2194 |
2195 | def enable_collision(self, mode: bool=True):
2196 | """
2197 | Enables pixel-accurate sprite collision detection with other sprites
2198 |
2199 | :param mode: True for enabling or False for disabling
2200 | """
2201 | ok = _tln.TLN_EnableSpriteCollision(self, mode)
2202 | _raise_exception(ok)
2203 |
2204 | def check_collision(self) -> bool:
2205 | """
2206 | Gets the collision status of the sprite. Requires :meth:`Sprite.enable_collision` set to True
2207 |
2208 | :return: True if collision with another sprite detected, or False if not
2209 | """
2210 | return _tln.TLN_GetSpriteCollision(self)
2211 |
2212 | def disable(self):
2213 | """
2214 | Disables the sprite so it is not drawn
2215 | """
2216 | ok = _tln.TLN_DisableSprite(self)
2217 | _raise_exception(ok)
2218 |
2219 | def get_palette(self) -> Palette:
2220 | """
2221 | Gets the sprite active palette
2222 |
2223 | :return: Palette object of the Sprite
2224 | """
2225 | handle = _tln.TLN_GetSpritePalette(self)
2226 | if handle is not None:
2227 | return Palette(handle)
2228 | else:
2229 | _raise_exception()
2230 |
2231 | def set_animation(self, sequence: Sequence, loop: int=0):
2232 | """
2233 | Starts a Sprite animation
2234 |
2235 | :param sequence: Sequence object to play on the sprite
2236 | :param loop: number of times to repeat, 0=infinite
2237 | """
2238 | ok = _tln.TLN_SetSpriteAnimation(self, sequence, loop)
2239 | _raise_exception(ok)
2240 |
2241 | def get_animation_state(self) -> bool:
2242 | """
2243 | Gets the state of the animation
2244 |
2245 | :return: True if still running, False if it has finished
2246 | """
2247 | return _tln.TLN_GetAnimationState(self)
2248 |
2249 | def pause_animation(self):
2250 | """
2251 | Paused sprite animation
2252 | """
2253 | ok = _tln.TLN_PauseSpriteAnimation(self)
2254 | _raise_exception(ok)
2255 |
2256 | def resume_animation(self):
2257 | """
2258 | Resumes paused animation
2259 | """
2260 | ok = _tln.TLN_ResumeSpriteAnimation(self)
2261 | _raise_exception(ok)
2262 |
2263 | def disable_animation(self):
2264 | """
2265 | Disables the animation so it doesn't run
2266 | """
2267 | ok = _tln.TLN_DisableSpriteAnimation(self)
2268 | _raise_exception(ok)
2269 |
2270 | def get_state(self, state: POINTER(SpriteState)):
2271 | """
2272 | Returns runtime info about the sprite
2273 |
2274 | :param state: user-allocated SpriteState structure
2275 | """
2276 | ok = _tln.TLN_GetSpriteState(self, state)
2277 | _raise_exception(ok)
2278 |
2279 | def set_first(self):
2280 | """
2281 | Sets this to be the first sprite drawn (beginning of list)
2282 | """
2283 | ok = _tln.TLN_SetFirstSprite(self)
2284 | _raise_exception(ok)
2285 |
2286 | def set_next(self, next: int):
2287 | """
2288 | Sets the next sprite to after this one, builds list
2289 |
2290 | :param next: sprite to draw after this one
2291 | """
2292 | ok = _tln.TLN_SetNextSprite(self, next)
2293 | _raise_exception(ok)
2294 |
2295 | def enable_masking(self, enable: bool=True):
2296 | """
2297 | Enables or disables masking for this sprite, if enabled it won't be drawn inside the region set up with Engine.set_sprite_mask_region()
2298 | """
2299 | ok = _tln.TLN_EnableSpriteMasking(self, enable)
2300 | _raise_exception(ok)
2301 |
2302 |
2303 | # color cycle animation engine ------------------------------------------------------------
2304 | _tln.TLN_SetPaletteAnimation.argtypes = [c_int, c_void_p, c_void_p, c_bool]
2305 | _tln.TLN_SetPaletteAnimation.restype = c_bool
2306 | _tln.TLN_SetPaletteAnimationSource.argtypes = [c_int, c_void_p]
2307 | _tln.TLN_SetPaletteAnimationSource = c_bool
2308 | _tln.TLN_GetAvailableAnimation.restype = c_int
2309 | _tln.TLN_DisablePaletteAnimation.argtypes = [c_int]
2310 | _tln.TLN_DisablePaletteAnimation.restype = c_bool
2311 |
2312 |
2313 | class Animation(object):
2314 | """
2315 | The Animation object manages color-cycle (palette) animations
2316 |
2317 | :ivar index: animation index, from 0 to num_animations - 1
2318 | """
2319 | def __init__(self, index: int):
2320 | self.index = index
2321 | self._as_parameter_ = index
2322 |
2323 | def set_palette_animation(self, palette: Palette, sequence: Sequence, blend: bool=True):
2324 | """
2325 | Starts a color cycle animation for Palette object
2326 |
2327 | :param palette: Palette object to animate
2328 | :param sequence: Sequence object to play on the palette
2329 | :param blend: True for smooth frame interpolation, False for classic coarse mode
2330 | """
2331 | ok = _tln.TLN_SetPaletteAnimation(self, palette, sequence, blend)
2332 | _raise_exception(ok)
2333 |
2334 | def set_palette_animation_source(self, palette: Palette):
2335 | """
2336 | Sets the source palette of a color cycle animation already in motion.
2337 | Useful for combining color cycling and palette interpolation at the same time
2338 |
2339 | :param palette: Palette object to assign
2340 | """
2341 | ok = _tln.TLN_SetPaletteAnimationSource(self, palette)
2342 | _raise_exception(ok)
2343 |
2344 | def disable(self):
2345 | """
2346 | Disables the animation so it doesn't run
2347 | """
2348 | ok = _tln.TLN_DisablePaletteAnimation(self)
2349 | _raise_exception(ok)
2350 |
--------------------------------------------------------------------------------
/src/world.py:
--------------------------------------------------------------------------------
1 | """ world/play field game entity """
2 |
3 | import xml.etree.ElementTree as ET
4 | from tilengine import Tilemap, Spriteset, Sequence
5 | from effect import Effect
6 | from score import Score
7 | from eagle import Eagle
8 | from opossum import Opossum
9 | import game
10 |
11 | class Tiles:
12 | """ types of tiles for sprite-terrain collision detection """
13 | Empty, Floor, Gem, Wall, SlopeUp, SlopeDown, InnerSlopeUp, InnerSlopeDown = range(8)
14 |
15 |
16 | class Medium:
17 | """ types of environments """
18 | Floor, Air, Ladder, Water = range(4)
19 |
20 |
21 | class Item(object):
22 | """ Generic item declared in tilemap object layer awaiting to spawn """
23 | Opossum, Eagle, Frog = range(3)
24 |
25 | def __init__(self, item_type, x, y):
26 | self.type = item_type
27 | self.x = x
28 | self.y = y
29 | self.alive = False
30 |
31 | def try_spawn(self, x):
32 | """ Tries to spawn an active game object depending on screen position and item type """
33 | if self.alive is False and x < self.x < x + game.WIDTH:
34 | self.alive = True
35 | if self.type is Item.Eagle:
36 | Eagle(self, self.x, self.y - Eagle.size[1])
37 | elif self.type is Item.Opossum:
38 | Opossum(self, self.x, self.y - Opossum.size[1])
39 |
40 |
41 | def load_objects(file_name, layer_name, first_gid):
42 | """ loads tiles in object layer from a tmx file.
43 | Returns list of Item objects """
44 | tree = ET.parse(file_name)
45 | root = tree.getroot()
46 | for child in root.findall("objectgroup"):
47 | name = child.get("name")
48 | if name == layer_name:
49 | item_list = list()
50 | for item in child.findall("object"):
51 | gid = item.get("gid")
52 | if gid is not None:
53 | x = item.get("x")
54 | y = item.get("y")
55 | item_list.append(
56 | Item(int(gid) - first_gid, int(x), int(y)))
57 | return item_list
58 | return None
59 |
60 | class World(object):
61 | """ world/play field entity """
62 |
63 | def __init__(self):
64 | self.foreground = game.engine.layers[1]
65 | self.background = game.engine.layers[2]
66 | self.clouds = 0.0
67 | self.foreground.setup(Tilemap.fromfile("layer_foreground.tmx"))
68 | self.background.setup(Tilemap.fromfile("layer_background.tmx"))
69 | self.spriteset_vanish = Spriteset.fromfile("effect_vanish")
70 | self.seq_vanish = Sequence.create_sprite_sequence(self.spriteset_vanish, "vanish", 4)
71 | self.x = 0
72 | self.x_max = self.foreground.width - game.WIDTH
73 | self.objects = load_objects(game.ASSETS_PATH +
74 | "/layer_foreground.tmx", "Capa de Objetos 1", 973)
75 | game.engine.set_background_color(self.background.tilemap)
76 | game.actors.append(self)
77 |
78 | def start(self):
79 | self.time = 30
80 | self.add_timer(0)
81 | game.ui.update_time(self.time)
82 |
83 | def pick_gem(self, tiles_list):
84 | """ updates tilemap when player picks a gem """
85 | for tile_info in tiles_list:
86 | if tile_info.type is Tiles.Gem:
87 | self.foreground.tilemap.set_tile(
88 | tile_info.row, tile_info.col, None)
89 | Effect(tile_info.col*16, tile_info.row*16,
90 | self.spriteset_vanish, self.seq_vanish)
91 | game.sounds.play("pickup", 1)
92 | self.add_timer(1)
93 | Score(1, tile_info.col*16, tile_info.row*16)
94 | break
95 |
96 | def add_timer(self, amount):
97 | """ increases/decreases timer timer """
98 | self.due_time = game.window.get_ticks() + 1000
99 | if amount >= 0:
100 | self.time += amount
101 | else:
102 | amount = -amount
103 | if self.time >= amount:
104 | self.time -= amount
105 | else:
106 | self.time = 0
107 | game.ui.update_time(self.time)
108 |
109 | def update(self):
110 | """ updates world state once per frame """
111 | oldx = self.x
112 |
113 | if game.player.x < 240:
114 | self.x = 0
115 | else:
116 | self.x = int(game.player.x - 240)
117 | if self.x > self.x_max:
118 | self.x = self.x_max
119 | self.clouds += 0.1
120 |
121 | if self.x is not oldx:
122 | self.foreground.set_position(self.x, 0)
123 | self.background.set_position(self.x/8, 0)
124 |
125 | # spawn new entities from object list
126 | for item in self.objects:
127 | item.try_spawn(self.x)
128 |
129 | now = game.window.get_ticks()
130 | if now > self.due_time:
131 | self.add_timer(-1)
132 |
133 | return True
134 |
--------------------------------------------------------------------------------