1 """ In the night, the mould attacks. """
4 import pygame.locals as pgl
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, Tooltip
21 from ..constants import (
22 NIGHT_LENGTH, NIGHT_HOURS_PER_TICK, DEBUG, FONTS,
26 class NightScene(BaseScene):
28 DARKNESS = Overlay(colour=(0, 0, 0, 150))
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
37 self._infobar = InfoBar("day", battery=self._battery, scene=self)
38 self._countdownbar = CountDownBar("h")
39 self._mould = Boyd(gamestate, self._space)
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)
50 self._eaten_tonight = 0
51 self._night_over_text = []
54 def create_tools(self, gamestate):
56 y = SCREEN_SIZE[1] - 40
57 tools.append(ImageButton(
58 '32', 'day.png', name='day', pos=(SCREEN_SIZE[0] - 200, y), tooltip=Tooltip(["Skip to day"])))
59 tools.append(ImageButton(
60 '32', 'pause.png', name='pause play',
61 pos=(SCREEN_SIZE[0] - 150, y), tooltip=Tooltip(["Pause"])))
62 tools.append(ImageButton(
63 '32', 'exit.png', name='exit', pos=(SCREEN_SIZE[0] - 50, y), tooltip=Tooltip(["Exit"])))
68 def turnip_count(self):
69 return len(self._turnips)
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))
77 def remaining_hours(self):
79 (NIGHT_LENGTH - self._total_ticks) * NIGHT_HOURS_PER_TICK))
81 @debug_timer("night.render")
82 def render(self, surface, gamestate):
83 surface.blit(self._soil, (0, 0))
85 self._mould.render(surface)
87 for turnip in self._turnips[:]:
89 self._turnips.remove(turnip)
92 self._eaten_tonight += 1
94 turnip.render(surface)
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())
102 for tool in self._tools:
105 for text, text_pos in self._night_over_text:
106 surface.blit(text, text_pos, None)
108 def event(self, ev, gamestate):
111 if ev.type == pgl.KEYDOWN:
112 if not self._do_ticks:
114 self._to_day(gamestate)
115 if ev.key in (pgl.K_q, pgl.K_ESCAPE):
117 from .menu import MenuScene
118 SceneChangeEvent.post(scene=MenuScene())
119 elif ev.key == pgl.K_e and DEBUG:
121 elif ev.key == pgl.K_SPACE:
123 elif ev.type == pgl.MOUSEBUTTONDOWN:
124 if not self._do_ticks:
125 # Any mouse press exits
126 self._to_day(gamestate)
128 self._lights.toggle_nearest(ev.pos, surfpos=True)
131 for tool in self._tools:
133 if tool.name == 'pause play':
135 elif tool.name == 'exit':
137 from .menu import MenuScene
138 SceneChangeEvent.post(scene=MenuScene())
139 elif tool.name == 'day':
140 self._to_day(gamestate)
142 def toggle_pause(self):
143 self._paused = not self._paused
144 pause_img = "play.png" if self._paused else "pause.png"
145 pause_text = "Play" if self._paused else "Pause"
146 for tool in self._tools:
147 if tool.name == 'pause play':
148 tool.update_image("32", pause_img)
149 tool.tooltip = Tooltip([pause_text])
151 def _to_day(self, gamestate):
155 gamestate.update_lights(self._lights)
157 self.grow_turnips(gamestate)
158 turnip_data = [turnip.serialize() for turnip in self._turnips]
159 gamestate.turnips = turnip_data
161 self._mould.update_resistances(gamestate)
163 from .day import DayScene
164 SceneChangeEvent.post(scene=DayScene())
166 def _end_night(self):
167 self._do_ticks = False
168 self._night_over_text = []
169 overlay = pygame.surface.Surface(
170 (SCREEN_SIZE[0], 240), pgl.SWSURFACE).convert_alpha()
171 overlay.fill((0, 0, 0, 172))
172 self._night_over_text.append((overlay, (0, 40)))
173 self._night_over_text.append(
174 (shadowed_text("The Night is Over", FONTS["bold"], 48), (300, 50)))
175 self._night_over_text.append(
176 (shadowed_text("Turnips eaten tonight: %d" % self._eaten_tonight,
177 FONTS["sans"], 32), (300, 130)))
178 self._night_over_text.append(
179 (shadowed_text("Surviving turnips: %d" % len(self._turnips),
180 FONTS["sans"], 32), (300, 170)))
181 self._night_over_text.append(
182 (shadowed_text("Press any key to continue", FONTS["sans"], 24),
185 def check_battery(self):
186 if self._battery.current == 0 and not self._battery_dead:
187 self._battery_dead = True
188 sound.play_sound("battery_dying.ogg")
189 self._lights.battery_dead()
191 @debug_timer("night.tick")
192 def tick(self, gamestate):
193 if self._do_ticks and not self._paused:
194 if self._total_ticks < NIGHT_LENGTH:
195 self._mould.tick(gamestate, self._space, self._lights)
197 if self._total_ticks % FPS == 0:
198 self._battery.current -= int(
199 self._lights.total_power_usage())
201 self._total_ticks += 1
204 if not self._mould.alive():
206 if not self.turnip_count:
207 self._tools[0].enable()
208 if not self.turnip_count and not self._battery.current:
211 def grow_turnips(self, gamestate):
212 """ Turnips grow at the end of the night """
213 for turnip in self._turnips[:]:
214 # Turnips grow at dawn
215 seeds = turnip.grow()
217 gamestate.seeds += seeds
218 gamestate.harvested += 1
219 self._turnips.remove(turnip)
220 # We ignore the body cleanup, since the space is going away