-
Notifications
You must be signed in to change notification settings - Fork 0
/
effects.py
174 lines (149 loc) · 6.02 KB
/
effects.py
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
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""This module containes visual effects (also implemented as nodes)."""
from math import pi
from random import random
import gtk
import gtk.gdk as gdk
import gobject
import cairo
from pnode import Node
class ExplosionEffect(Node):
"""This effect is showed when a bomb explode."""
def __init__(self, parent, style, fire, get_cell_size, on_die):
"""Initialize this effect.
@param fire: representing the length of four fire arms (top, right, down, left)
@type fire: a list of four integers
@param get_cell_size: the callback function that always returns the cell size
(should be MapContainer.get_cell_size())
@param on_die: the callback function called when the effect ends
"""
super(ExplosionEffect, self).__init__(parent, style)
self.fire = fire
self.get_cell_size = get_cell_size
self.on_die = on_die
def animation(self, cr, phase):
cell_size = self.get_cell_size()
left = self.fire[3] * cell_size
up = self.fire[0] * cell_size
right = self.fire[1] * cell_size
down = self.fire[2] * cell_size
cr.move_to(left, up)
cr.rel_line_to(0, -up)
cr.rel_line_to(cell_size, 0)
cr.rel_line_to(0, up)
cr.rel_line_to(right, 0)
cr.rel_line_to(0, cell_size)
cr.rel_line_to(-right, 0)
cr.rel_line_to(0, down)
cr.rel_line_to(-cell_size, 0)
cr.rel_line_to(0, -down)
cr.rel_line_to(-left, 0)
cr.rel_line_to(0, -cell_size)
cr.close_path()
cr.set_source_rgba(1, 1, 0, 0.5)
cr.fill()
self.set_animation(animation, duration=1.5, cleanup=self.on_die)
class Particle(object):
"""The individual particle for particle system."""
__slots__ = ('size', 'v_size', 'position',
'velocity', 'accel', 'color', 'v_color', 'lifetime')
class ParticleEffect(Node):
"""Display effects using particle system."""
def __init__(self, parent, style,
size, color, v_color, center,
velocity, velocity_deviation, lifetime,
v_size=0, size_deviation=0, accel=(0, 0),
max_amount=0, initial_amount=0, spawn_interval=0.0):
"""Initilize this particle system.
@param size: the size of one particle
@param color: the rgba value of the initial color for each particle
@param v_color: the vector for the change of color
@param center: the position to spawn new particles
(in relative position, eg. (0.5, 0.5) represents the center of object)
@param velocity: the velocity of each particle
@param velocity_deviation: the deviation of initial velocity
of each paricle
@param lifetime: the life of each particle (in second)
@param v_size: the change of size
@param size_deviation: the deviation of initial size of each particle
@param accel: the acceleration
@param max_amount: the max number of particles
@param initial_amount: the initial number of particles
@param spawn_interval: the period to spawn new particles
"""
super(ParticleEffect, self).__init__(parent, style)
# color
self.color = color
self.v_color = v_color
# position (change upon resize)
self.size = size
self.size_deviation = size_deviation
self.v_size = v_size
self.center = center
self.velocity = velocity
self.velocity_deviation = velocity_deviation
self.accel = accel
self.orig_width = float(self.width)
self.orig_height = float(self.height)
# other
self.lifetime = lifetime
self.max_amount = max_amount
self.spawn_interval = spawn_interval
self.time_elapsed = 0.0
self.particles = []
for i in xrange(0, initial_amount):
self.spawn()
def spawn(self):
def _deviated(e):
return e[0] + e[1] * (random() - 0.5)
p = Particle()
p.size = _deviated((self.size, self.size_deviation))
p.v_size = self.v_size
p.lifetime = self.lifetime
p.position = self.center
p.velocity = map(_deviated,
zip(self.velocity, self.velocity_deviation))
p.accel = self.accel
p.color = self.color
p.v_color = self.v_color
self.particles.append(p)
@staticmethod
def update_action(self, interval, phase):
def update_value(e):
return e[0] + e[1] * e[2]
def restrict(e):
return 0.0 if e <= 0.0 else 1.0 if e >= 1.0 else e
# Update particles
particles = list(self.particles)
interval2 = (interval, ) * 2
interval4 = (interval, ) * 4
for p in particles:
p.position = map(update_value,
zip(p.position, p.velocity, interval2))
p.velocity = map(update_value,
zip(p.velocity, p.accel, interval2))
p.color = map(update_value, zip(p.color, p.v_color, interval4))
p.color = map(restrict, p.color)
p.size += p.v_size * interval
p.lifetime -= interval
if p.lifetime <= 0.0 or p.color[3] <= 0.0 or p.size <= 0:
self.particles.remove(p)
# Spawn a new particle
self.time_elapsed += interval
if (self.time_elapsed > self.spawn_interval
and (self.max_amount == 0
or len(self.particles) <= self.max_amount)):
self.time_elapsed = 0.0
self.spawn()
def on_update(self, cr):
w, h = self.width, self.height
factor = w / self.orig_width
for p in self.particles:
x = p.position[0] * w
y = p.position[1] * h
# XXX: how to resize it?
size = p.size * factor
cr.set_source_rgba(*p.color)
cr.arc(x, y, size, 0, 2 * pi)
cr.fill()