593a03f5bed6292238349ec5851da9a01a07e411
[tabakrolletjie.git] / tabakrolletjie / scenes / night.py
1 """ In the night, the mould attacks. """
2
3 import pygame.surface
4 import pygame.locals as pgl
5
6 import pymunk
7
8 from .base import BaseScene
9 from ..battery import BatteryManager
10 from ..lights import LightManager
11 from ..infobar import InfoBar, CountDownBar
12 from ..obstacles import ObstacleManager
13 from ..enemies import Boyd
14 from ..events import SceneChangeEvent
15 from ..utils import debug_timer, shadowed_text
16 from ..loader import loader
17 from ..sound import sound
18 from ..transforms import Overlay
19 from ..turnip import Turnip
20 from ..widgets import ImageButton
21 from ..constants import (
22     NIGHT_LENGTH, NIGHT_HOURS_PER_TICK, DEBUG, FONTS,
23     SCREEN_SIZE, FPS)
24
25
26 class NightScene(BaseScene):
27
28     DARKNESS = Overlay(colour=(0, 0, 0, 150))
29
30     def enter(self, gamestate):
31         self._space = pymunk.Space()
32         self._obstacles = ObstacleManager(self._space, gamestate)
33         self._lights = LightManager(self._space, gamestate)
34         self._battery = BatteryManager(gamestate)
35         self._battery_dead = False
36         self.check_battery()
37         self._infobar = InfoBar("day", battery=self._battery, scene=self)
38         self._countdownbar = CountDownBar("h")
39         self._mould = Boyd(gamestate, self._space)
40         self._turnips = []
41         for turnip_data in gamestate.turnips:
42             turnip = Turnip(space=self._space, **turnip_data)
43             self._turnips.append(turnip)
44         self._soil = loader.load_image(
45             "textures", "soil.png", transform=self.DARKNESS)
46         self._tools = self.create_tools(gamestate)
47         self._total_ticks = 0
48         self._do_ticks = True
49         self._paused = False
50         self._eaten_tonight = 0
51         self._night_over_text = []
52         self._ending = False
53
54     def create_tools(self, gamestate):
55         tools = []
56         y = SCREEN_SIZE[1] - 40
57         tools.append(ImageButton(
58             '32', 'pause.png', name='pause play',
59             pos=(SCREEN_SIZE[0] - 150, y)))
60         tools.append(ImageButton(
61             '32', 'exit.png', name='exit', pos=(SCREEN_SIZE[0] - 50, y)))
62         return tools
63
64     def add_day_button(self):
65         y = SCREEN_SIZE[1] - 40
66         self._tools.append(ImageButton(
67             '32', 'day.png', name='day', pos=(SCREEN_SIZE[0] - 200, y)))
68
69     @property
70     def turnip_count(self):
71         return len(self._turnips)
72
73     @property
74     def power_usage(self):
75         power = self._lights.total_power_usage()
76         power = power / (FPS * NIGHT_HOURS_PER_TICK)
77         return int(round(power))
78
79     def remaining_hours(self):
80         return int(round(
81             (NIGHT_LENGTH - self._total_ticks) * NIGHT_HOURS_PER_TICK))
82
83     @debug_timer("night.render")
84     def render(self, surface, gamestate):
85         surface.blit(self._soil, (0, 0))
86
87         self._mould.render(surface)
88
89         for turnip in self._turnips[:]:
90             if turnip.eaten:
91                 self._turnips.remove(turnip)
92                 turnip.remove()
93                 gamestate.eaten += 1
94                 self._eaten_tonight += 1
95             else:
96                 turnip.render(surface)
97
98         self._lights.render_light(surface)
99         self._obstacles.render(surface)
100         self._lights.render_fittings(surface)
101         self._infobar.render(surface, gamestate)
102         self._countdownbar.render(surface, self.remaining_hours())
103
104         for tool in self._tools:
105             tool.render(surface)
106
107         for text, text_pos in self._night_over_text:
108             surface.blit(text, text_pos, None)
109
110     def event(self, ev, gamestate):
111         if self._ending:
112             return
113         if ev.type == pgl.KEYDOWN:
114             if not self._do_ticks:
115                 # Any keypress exits
116                 self._to_day(gamestate)
117             if ev.key in (pgl.K_q, pgl.K_ESCAPE):
118                 self._ending = True
119                 from .menu import MenuScene
120                 SceneChangeEvent.post(scene=MenuScene())
121             elif ev.key == pgl.K_e and DEBUG:
122                 self._end_night()
123             elif ev.key == pgl.K_SPACE:
124                 self.toggle_pause()
125         elif ev.type == pgl.MOUSEBUTTONDOWN:
126             if not self._do_ticks:
127                 # Any mouse press exits
128                 self._to_day(gamestate)
129             if ev.button == 1:
130                 self._lights.toggle_nearest(ev.pos, surfpos=True)
131
132                 # Check tools
133                 for tool in self._tools:
134                     if tool.pressed(ev):
135                         if tool.name == 'pause play':
136                             self.toggle_pause()
137                         elif tool.name == 'exit':
138                             self._ending = True
139                             from .menu import MenuScene
140                             SceneChangeEvent.post(scene=MenuScene())
141                         elif tool.name == 'day':
142                             self._to_day(gamestate)
143
144     def toggle_pause(self):
145         self._paused = not self._paused
146         pause_img = "play.png" if self._paused else "pause.png"
147         for tool in self._tools:
148             if tool.name == 'pause play':
149                 tool.update_image("32", pause_img)
150
151     def _to_day(self, gamestate):
152         # End the night
153         if self._ending:
154             return
155         gamestate.update_lights(self._lights)
156         self._ending = True
157         from .day import DayScene
158         SceneChangeEvent.post(scene=DayScene())
159
160     def _end_night(self):
161         self._do_ticks = False
162         self._night_over_text = []
163         overlay = pygame.surface.Surface(
164             (SCREEN_SIZE[0], 240), pgl.SWSURFACE).convert_alpha()
165         overlay.fill((0, 0, 0, 172))
166         self._night_over_text.append((overlay, (0, 40)))
167         self._night_over_text.append(
168             (shadowed_text("The Night is Over", FONTS["bold"], 48), (300, 50)))
169         self._night_over_text.append(
170             (shadowed_text("Turnips eaten tonight: %d" % self._eaten_tonight,
171                            FONTS["sans"], 32), (300, 130)))
172         self._night_over_text.append(
173             (shadowed_text("Surviving turnips: %d" % len(self._turnips),
174                            FONTS["sans"], 32), (300, 170)))
175         self._night_over_text.append(
176             (shadowed_text("Press any key to continue", FONTS["sans"], 24),
177              (350, 240)))
178
179     def check_battery(self):
180         if self._battery.current == 0 and not self._battery_dead:
181             self._battery_dead = True
182             sound.play_sound("battery_dying.ogg")
183             self._lights.battery_dead()
184
185     @debug_timer("night.tick")
186     def tick(self, gamestate):
187         if self._do_ticks and not self._paused:
188             if self._total_ticks < NIGHT_LENGTH:
189                 self._mould.tick(gamestate, self._space, self._lights)
190                 self._lights.tick()
191                 if self._total_ticks % FPS == 0:
192                     self._battery.current -= int(
193                         self._lights.total_power_usage())
194                     self.check_battery()
195                 self._total_ticks += 1
196             else:
197                 self._end_night()
198             if not self._mould.alive():
199                 self._end_night()
200             if not self.turnip_count:
201                 self.add_day_button()
202             if not self.turnip_count and not self._battery.current:
203                 self._end_night()
204
205     def exit(self, gamestate):
206         turnip_data = [turnip.serialize() for turnip in self._turnips]
207         gamestate.turnips = turnip_data
208         # TODO: Move this into the end_night function
209         gamestate.days += 1
210         self._mould.update_resistances(gamestate)