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