1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file map.cpp
12 *** \author Tyler Olsen, roots@allacrost.org
13 *** \brief Source file for map mode interface.
14 *** ***************************************************************************/
15
16 // Allacrost engines
17 #include "audio.h"
18 #include "script.h"
19 #include "input.h"
20 #include "system.h"
21
22 // Allacrost globals
23 #include "global.h"
24
25 // Other mode headers
26 #include "menu.h"
27 #include "pause.h"
28
29 // Local map mode headers
30 #include "map.h"
31 #include "map_dialogue.h"
32 #include "map_events.h"
33 #include "map_objects.h"
34 #include "map_sprites.h"
35 #include "map_tiles.h"
36 #include "map_treasure.h"
37 #include "map_zones.h"
38
39 using namespace std;
40 using namespace hoa_utils;
41 using namespace hoa_audio;
42 using namespace hoa_input;
43 using namespace hoa_mode_manager;
44 using namespace hoa_script;
45 using namespace hoa_system;
46 using namespace hoa_video;
47 using namespace hoa_global;
48 using namespace hoa_menu;
49 using namespace hoa_pause;
50 using namespace hoa_map::private_map;
51
52 namespace hoa_map {
53
54 // Initialize static class variables
55 MapMode* MapMode::_current_instance = NULL;
56
57 // ****************************************************************************
58 // ********** MapMode Public Class Methods
59 // ****************************************************************************
60
MapMode(string filename)61 MapMode::MapMode(string filename) :
62 GameMode(),
63 _map_filename(filename),
64 _map_tablespace(""),
65 _map_event_group(NULL),
66 _tile_supervisor(NULL),
67 _object_supervisor(NULL),
68 _event_supervisor(NULL),
69 _dialogue_supervisor(NULL),
70 _treasure_supervisor(NULL),
71 _camera(NULL),
72 _num_map_contexts(0),
73 _current_context(MAP_CONTEXT_01),
74 _ignore_input(false),
75 _run_disabled(false),
76 _run_forever(false),
77 _run_stamina(10000),
78 _show_dialogue_icons(true),
79 _current_track(0)
80 {
81 mode_type = MODE_MANAGER_MAP_MODE;
82 _current_instance = this;
83
84 ResetState();
85 PushState(STATE_EXPLORE);
86
87 // Create the event group name by modifying the filename to consists only of alphanumeric characters and underscores
88 // This will make it a valid identifier name in Lua syntax
89 string event_group_name = _map_filename;
90 std::replace(event_group_name.begin(), event_group_name.end(), '/', '_');
91 std::replace(event_group_name.begin(), event_group_name.end(), '.', '_');
92
93 if (GlobalManager->DoesEventGroupExist(event_group_name) == false) {
94 GlobalManager->AddNewEventGroup(event_group_name);
95 }
96 _map_event_group = GlobalManager->GetEventGroup(event_group_name);
97
98 _tile_supervisor = new TileSupervisor();
99 _object_supervisor = new ObjectSupervisor();
100 _event_supervisor = new EventSupervisor();
101 _dialogue_supervisor = new DialogueSupervisor();
102 _treasure_supervisor = new TreasureSupervisor();
103
104 _intro_timer.Initialize(7000, 0);
105 _intro_timer.EnableAutoUpdate(this);
106
107 // TODO: Load the map data in a seperate thread
108 _Load();
109
110 // Load miscellaneous map graphics
111 vector<uint32> timings(16, 100); // holds the timing data for the new dialogue animation; 16 frames at 100ms each
112 _dialogue_icon.SetDimensions(2, 2);
113 if (_dialogue_icon.LoadFromFrameSize("img/misc/dialogue_icon.png", timings, 32, 32) == false)
114 IF_PRINT_WARNING(MAP_DEBUG) << "failed to load the new dialogue icon image" << endl;
115
116 if (_stamina_bar_background.Load("img/misc/stamina_bar_background.png", 227, 24) == false)
117 IF_PRINT_WARNING(MAP_DEBUG) << "failed to load the the stamina bar background image" << endl;
118
119 if (_stamina_bar_infinite_overlay.Load("img/misc/stamina_bar_infinite_overlay.png", 227, 24) == false)
120 IF_PRINT_WARNING(MAP_DEBUG) << "failed to load the the stamina bar infinite overlay image" << endl;
121 }
122
123
124
~MapMode()125 MapMode::~MapMode() {
126 for (uint32 i = 0; i < _music.size(); i++)
127 _music[i].FreeAudio();
128 _music.clear();
129
130 for (uint32 i = 0; i < _sounds.size(); i++)
131 _sounds[i].FreeAudio();
132 _sounds.clear();
133
134 for (uint32 i = 0; i < _enemies.size(); i++)
135 delete(_enemies[i]);
136 _enemies.clear();
137
138 delete(_tile_supervisor);
139 delete(_object_supervisor);
140 delete(_event_supervisor);
141 delete(_dialogue_supervisor);
142 delete(_treasure_supervisor);
143
144 _map_script.CloseFile();
145 }
146
147
148
Reset()149 void MapMode::Reset() {
150 // Reset video engine context properties
151 VideoManager->SetCoordSys(0.0f, SCREEN_COLS, SCREEN_ROWS, 0.0f);
152 VideoManager->SetDrawFlags(VIDEO_X_CENTER, VIDEO_Y_BOTTOM, 0);
153
154 // Set the active instance pointer to this map
155 MapMode::_current_instance = this;
156
157 // Make the map location known globally to other code that may need to know this information
158 GlobalManager->SetLocation(MakeUnicodeString(_map_filename), _location_graphic.GetFilename());
159
160 if (_music.size() > _current_track && _music[_current_track].GetState() != AUDIO_STATE_PLAYING) {
161 _music[_current_track].Play();
162 }
163
164 _intro_timer.Run();
165 }
166
167
168
Update()169 void MapMode::Update() {
170 // TODO: we need to detect if a battle is about to occur and if so, fade the screen gradually from
171 // map mode into the battle
172 _dialogue_icon.Update();
173
174 // TODO: instead of doing this every frame, see if it can be done only when the _camera pointer is modified
175 _current_context = _camera->GetContext();
176
177 // Process quit and pause events unconditional to the state of map mode
178 if (InputManager->QuitPress() == true) {
179 ModeManager->Push(new PauseMode(true));
180 return;
181 }
182 else if (InputManager->PausePress() == true) {
183 ModeManager->Push(new PauseMode(false));
184 return;
185 }
186
187 // ---------- (1) Call the map script's update function
188 if (_update_function)
189 ScriptCallFunction<void>(_update_function);
190
191 // ---------- (2) Update all animated tile images
192 _tile_supervisor->Update();
193 _object_supervisor->Update();
194 _object_supervisor->SortObjects();
195
196 // ---------- (3) Update the active state of the map
197 switch (CurrentState()) {
198 case STATE_EXPLORE:
199 _UpdateExplore();
200 break;
201 case STATE_SCENE:
202 break;
203 case STATE_DIALOGUE:
204 _UpdateDialogue();
205 _dialogue_supervisor->Update();
206 break;
207 case STATE_TREASURE:
208 _camera->moving = false;
209 _treasure_supervisor->Update();
210 break;
211 default:
212 IF_PRINT_WARNING(MAP_DEBUG) << "map was set in an unknown state: " << CurrentState() << endl;
213 ResetState();
214 break;
215 }
216
217 // ---------- (4) Update all active map events
218 _event_supervisor->Update();
219 } // void MapMode::Update()
220
221
222
Draw()223 void MapMode::Draw() {
224 _CalculateMapFrame();
225
226 if (_draw_function)
227 ScriptCallFunction<void>(_draw_function);
228 else
229 _DrawMapLayers();
230
231 _DrawGUI();
232 if (CurrentState() == STATE_DIALOGUE) {
233 _dialogue_supervisor->Draw();
234 }
235 } // void MapMode::_Draw()
236
237
238
ResetState()239 void MapMode::ResetState() {
240 _state_stack.clear();
241 _state_stack.push_back(STATE_INVALID);
242 }
243
244
245
PushState(MAP_STATE state)246 void MapMode::PushState(MAP_STATE state) {
247 _state_stack.push_back(state);
248 }
249
250
251
PopState()252 void MapMode::PopState() {
253 _state_stack.pop_back();
254 if (_state_stack.empty() == true) {
255 IF_PRINT_WARNING(MAP_DEBUG) << "stack was empty after operation, reseting state stack" << endl;
256 _state_stack.push_back(STATE_INVALID);
257 }
258 }
259
260
261
CurrentState()262 MAP_STATE MapMode::CurrentState() {
263 if (_state_stack.empty() == true) {
264 IF_PRINT_WARNING(MAP_DEBUG) << "stack was empty, reseting state stack" << endl;
265 _state_stack.push_back(STATE_INVALID);
266 }
267 return _state_stack.back();
268 }
269
270
271
AddGroundObject(MapObject * obj)272 void MapMode::AddGroundObject(MapObject *obj) {
273 _object_supervisor->_ground_objects.push_back(obj);
274 _object_supervisor->_all_objects.insert(make_pair(obj->object_id, obj));
275 }
276
277
278
AddPassObject(MapObject * obj)279 void MapMode::AddPassObject(MapObject *obj) {
280 _object_supervisor->_pass_objects.push_back(obj);
281 _object_supervisor->_all_objects.insert(make_pair(obj->object_id, obj));
282 }
283
284
285
AddSkyObject(MapObject * obj)286 void MapMode::AddSkyObject(MapObject *obj) {
287 _object_supervisor->_sky_objects.push_back(obj);
288 _object_supervisor->_all_objects.insert(make_pair(obj->object_id, obj));
289 }
290
291
292
AddZone(MapZone * zone)293 void MapMode::AddZone(MapZone *zone) {
294 _object_supervisor->_zones.push_back(zone);
295 }
296
297
298
IsEnemyLoaded(uint32 id) const299 bool MapMode::IsEnemyLoaded(uint32 id) const {
300 for (uint32 i = 0; i < _enemies.size(); i++) {
301 if (_enemies[i]->GetID() == id) {
302 return true;
303 }
304 }
305 return false;
306 }
307
PlayMusic(uint32 track_num)308 void MapMode::PlayMusic(uint32 track_num) {
309 _music[_current_track].Stop();
310 _current_track = track_num;
311 _music[_current_track].Play();
312 }
313
314 // ****************************************************************************
315 // ********** MapMode Private Class Methods
316 // ****************************************************************************
317
_Load()318 void MapMode::_Load() {
319 // ---------- (1) Open map script file and read in the basic map properties and tile definitions
320 if (_map_script.OpenFile(_map_filename) == false) {
321 return;
322 }
323
324 // Determine the map's tablespacename and then open it. The tablespace is the name of the map file without
325 // file extension or path information (for example, 'dat/maps/demo.lua' has a tablespace name of 'demo').
326 int32 period = _map_filename.find(".");
327 int32 last_slash = _map_filename.find_last_of("/");
328 _map_tablespace = _map_filename.substr(last_slash + 1, period - (last_slash + 1));
329 _map_script.OpenTable(_map_tablespace);
330
331 // Read the number of map contexts, the name of the map, and load the location graphic image
332 _num_map_contexts = _map_script.ReadUInt("num_map_contexts");
333 _map_name = MakeUnicodeString(_map_script.ReadString("map_name"));
334 if (_location_graphic.Load("img/menus/locations/" + _map_script.ReadString("location_filename")) == false) {
335 PRINT_ERROR << "failed to load location graphic image: " << _location_graphic.GetFilename() << endl;
336 }
337
338 // ---------- (2) Instruct the supervisor classes to perform their portion of the load operation
339 _tile_supervisor->Load(_map_script, this);
340 _object_supervisor->Load(_map_script);
341
342 // ---------- (3) Load map sounds and music
343 vector<string> sound_filenames;
344 _map_script.ReadStringVector("sound_filenames", sound_filenames);
345
346 for (uint32 i = 0; i < sound_filenames.size(); i++) {
347 _sounds.push_back(SoundDescriptor());
348 if (_sounds.back().LoadAudio(sound_filenames[i]) == false) {
349 PRINT_ERROR << "failed to load map sound: " << sound_filenames[i] << endl;
350 return;
351 }
352 }
353
354 vector<string> music_filenames;
355 _map_script.ReadStringVector("music_filenames", music_filenames);
356 _music.resize(music_filenames.size(), MusicDescriptor());
357 for (uint32 i = 0; i < music_filenames.size(); i++) {
358 if (_music[i].LoadAudio(music_filenames[i]) == false) {
359 PRINT_ERROR << "failed to load map music: " << music_filenames[i] << endl;
360 return;
361 }
362 }
363
364 // ---------- (4) Create and store all enemies that may appear on this map
365 vector<int32> enemy_ids;
366 _map_script.ReadIntVector("enemy_ids", enemy_ids);
367 for (uint32 i = 0; i < enemy_ids.size(); i++) {
368 _enemies.push_back(new GlobalEnemy(enemy_ids[i]));
369 }
370
371 // ---------- (5) Call the map script's custom load function and get a reference to all other script function pointers
372 ScriptObject map_table(luabind::from_stack(_map_script.GetLuaState(), hoa_script::private_script::STACK_TOP));
373 ScriptObject function = map_table["Load"];
374 ScriptCallFunction<void>(function, this);
375
376 _update_function = _map_script.ReadFunctionPointer("Update");
377 _draw_function = _map_script.ReadFunctionPointer("Draw");
378
379 // ---------- (6) Prepare all sprites with dialogue
380 // This is done at this stage because the map script's load function creates the sprite and dialogue objects. Only after
381 // both sets are created can we determine which sprites have active dialogue.
382
383 // TODO: Need to figure out a new function appropriate for this code?
384 // TEMP: The line below is very bad to do, but is necessary for the UpdateDialogueStatus function to work correctly
385 _current_instance = this;
386 for (map<uint16, MapObject*>::iterator i = _object_supervisor->_all_objects.begin(); i != _object_supervisor->_all_objects.end(); i++) {
387 if (i->second->GetType() == SPRITE_TYPE) {
388 MapSprite* sprite = dynamic_cast<MapSprite*>(i->second);
389 sprite->UpdateDialogueStatus();
390 }
391 }
392
393 _map_script.CloseAllTables();
394 } // void MapMode::_Load()
395
396
397
_UpdateExplore()398 void MapMode::_UpdateExplore() {
399 // First go to menu mode if the user requested it
400 if (InputManager->MenuPress()) {
401 MenuMode *MM = new MenuMode(_map_name, _location_graphic.GetFilename());
402 ModeManager->Push(MM);
403 return;
404 }
405
406 // Update the running state of the camera object. Check if the player wishes to continue running and if so,
407 // update the stamina value if the operation is permitted
408 _camera->is_running = false;
409 if (_run_disabled == false && InputManager->CancelState() == true &&
410 (InputManager->UpState() || InputManager->DownState() || InputManager->LeftState() || InputManager->RightState()))
411 {
412 if (_run_forever) {
413 _camera->is_running = true;
414 }
415 else if (_run_stamina > SystemManager->GetUpdateTime() * 2) {
416 _run_stamina -= (SystemManager->GetUpdateTime() * 2);
417 _camera->is_running = true;
418 }
419 else {
420 _run_stamina = 0;
421 }
422 }
423 // Regenerate the stamina at 1/2 the consumption rate
424 else if (_run_stamina < 10000) {
425 _run_stamina += SystemManager->GetUpdateTime();
426 if (_run_stamina > 10000)
427 _run_stamina = 10000;
428 }
429
430 // If the user requested a confirm event, check if there is a nearby object that the player may interact with
431 // Interactions are currently limited to dialogue with sprites and opening of treasures
432 if (InputManager->ConfirmPress()) {
433 MapObject* obj = _object_supervisor->FindNearestObject(_camera);
434
435 if (obj && (obj->GetType() == SPRITE_TYPE)) {
436 MapSprite *sp = reinterpret_cast<MapSprite*>(obj);
437
438 if (sp->HasAvailableDialogue()) {
439 _dialogue_supervisor->BeginDialogue(sp);
440 return;
441 }
442 }
443 else if (obj && obj->GetType() == TREASURE_TYPE) {
444 MapTreasure* treasure = reinterpret_cast<MapTreasure*>(obj);
445
446 if (treasure->IsEmpty() == false) {
447 _camera->moving = false;
448 treasure->Open();
449 }
450 }
451 }
452
453 // Detect movement input from the user
454 if (InputManager->UpState() || InputManager->DownState() || InputManager->LeftState() || InputManager->RightState()) {
455 _camera->moving = true;
456 }
457 else {
458 _camera->moving = false;
459 }
460
461 // Determine the direction of movement. Priority of movement is given to: up, down, left, right.
462 // In the case of diagonal movement, the direction that the sprite should face also needs to be deduced.
463 if (_camera->moving == true) {
464 if (InputManager->UpState())
465 {
466 if (InputManager->LeftState())
467 _camera->SetDirection(MOVING_NORTHWEST);
468 else if (InputManager->RightState())
469 _camera->SetDirection(MOVING_NORTHEAST);
470 else
471 _camera->SetDirection(NORTH);
472 }
473 else if (InputManager->DownState())
474 {
475 if (InputManager->LeftState())
476 _camera->SetDirection(MOVING_SOUTHWEST);
477 else if (InputManager->RightState())
478 _camera->SetDirection(MOVING_SOUTHEAST);
479 else
480 _camera->SetDirection(SOUTH);
481 }
482 else if (InputManager->LeftState()) {
483 _camera->SetDirection(WEST);
484 }
485 else if (InputManager->RightState()) {
486 _camera->SetDirection(EAST);
487 }
488 } // if (_camera->moving == true)
489 } // void MapMode::_UpdateExplore()
490
491
492
_UpdateDialogue()493 void MapMode::_UpdateDialogue() {
494 // Update the running state of the camera object. Check if the player wishes to continue running and if so,
495 // update the stamina value if the operation is permitted
496 _camera->is_running = false;
497 if (_run_disabled == false && InputManager->CancelState() == true &&
498 (InputManager->UpState() || InputManager->DownState() || InputManager->LeftState() || InputManager->RightState()))
499 {
500 if (_run_forever) {
501 _camera->is_running = true;
502 }
503 else if (_run_stamina > SystemManager->GetUpdateTime() * 2) {
504 _run_stamina -= (SystemManager->GetUpdateTime() * 2);
505 _camera->is_running = true;
506 }
507 else {
508 _run_stamina = 0;
509 }
510 }
511 // Regenerate the stamina at 1/2 the consumption rate
512 else if (_run_stamina < 10000) {
513 _run_stamina += SystemManager->GetUpdateTime();
514 if (_run_stamina > 10000)
515 _run_stamina = 10000;
516 }
517
518 // TODO: If we want to allow moving during dialogue, this is the place to do it
519 // Detect movement input from the user
520 // if (InputManager->UpState() || InputManager->DownState() || InputManager->LeftState() || InputManager->RightState()) {
521 // _camera->moving = true;
522 // }
523 // else {
524 _camera->moving = false;
525 // }
526
527 // Determine the direction of movement. Priority of movement is given to: up, down, left, right.
528 // In the case of diagonal movement, the direction that the sprite should face also needs to be deduced.
529 if (_camera->moving == true) {
530 if (InputManager->UpState())
531 {
532 if (InputManager->LeftState())
533 _camera->SetDirection(MOVING_NORTHWEST);
534 else if (InputManager->RightState())
535 _camera->SetDirection(MOVING_NORTHEAST);
536 else
537 _camera->SetDirection(NORTH);
538 }
539 else if (InputManager->DownState())
540 {
541 if (InputManager->LeftState())
542 _camera->SetDirection(MOVING_SOUTHWEST);
543 else if (InputManager->RightState())
544 _camera->SetDirection(MOVING_SOUTHEAST);
545 else
546 _camera->SetDirection(SOUTH);
547 }
548 else if (InputManager->LeftState()) {
549 _camera->SetDirection(WEST);
550 }
551 else if (InputManager->RightState()) {
552 _camera->SetDirection(EAST);
553 }
554 } // if (_camera->moving == true)
555 } // void MapMode::_UpdateDialogue()
556
557
558
_CalculateMapFrame()559 void MapMode::_CalculateMapFrame() {
560 // ---------- (1) Determine the center position coordinates for the camera
561 float camera_x, camera_y; // Holds the final X, Y coordinates of the camera
562 float x_pixel_length, y_pixel_length; // The X and Y length values that coorespond to a single pixel in the current coodinate system
563 float rounded_x_offset, rounded_y_offset; // The X and Y position offsets of the camera, rounded to perfectly align on a pixel boundary
564
565 // TODO: the call to GetPixelSize() will return the same result every time so long as the coordinate system did not change. If we never
566 // change the coordinate system in map mode, then this should be done only once and the calculated values should be saved for re-use.
567 // However, we've discussed the possiblity of adding a zoom feature to maps, in which case we need to continually re-calculate the pixel size
568 VideoManager->GetPixelSize(x_pixel_length, y_pixel_length);
569 rounded_x_offset = FloorToFloatMultiple(_camera->x_offset, x_pixel_length);
570 rounded_y_offset = FloorToFloatMultiple(_camera->y_offset, y_pixel_length);
571 camera_x = static_cast<float>(_camera->x_position) + rounded_x_offset;
572 camera_y = static_cast<float>(_camera->y_position) + rounded_y_offset;
573
574 // ---------- (2) Calculate all four screen edges and determine
575 // Determine the draw coordinates of the top left corner using the camera's current position
576 _map_frame.tile_x_start = 1.0f - rounded_x_offset;
577 if (IsOddNumber(_camera->x_position))
578 _map_frame.tile_x_start -= 1.0f;
579
580 _map_frame.tile_y_start = 2.0f - rounded_y_offset;
581 if (IsOddNumber(_camera->y_position))
582 _map_frame.tile_y_start -= 1.0f;
583
584 // The starting row and column of tiles to draw is determined by the map camera's position
585 _map_frame.starting_col = (_camera->x_position / 2) - HALF_TILE_COLS;
586 _map_frame.starting_row = (_camera->y_position / 2) - HALF_TILE_ROWS;
587
588 _map_frame.screen_edges.top = camera_y - HALF_SCREEN_ROWS;
589 _map_frame.screen_edges.bottom = camera_y + HALF_SCREEN_ROWS;
590 _map_frame.screen_edges.left = camera_x - HALF_SCREEN_COLS;
591 _map_frame.screen_edges.right = camera_x + HALF_SCREEN_COLS;
592
593 // ---------- (3) Check for boundary conditions and re-adjust as necessary so we don't draw outside the map area
594
595 // Usually the map centers on the camera's position, but when the camera becomes too close to
596 // the edges of the map, we need to modify the drawing properties of the frame.
597
598 // Camera exceeds the left boundary of the map
599 if (_map_frame.starting_col < 0) {
600 _map_frame.starting_col = 0;
601 _map_frame.tile_x_start = 1.0f;
602 _map_frame.screen_edges.left = 0.0f;
603 _map_frame.screen_edges.right = SCREEN_COLS;
604 }
605 // Camera exceeds the right boundary of the map
606 else if (_map_frame.starting_col + TILE_COLS >= _tile_supervisor->_num_tile_cols) {
607 _map_frame.starting_col = static_cast<int16>(_tile_supervisor->_num_tile_cols - TILE_COLS);
608 _map_frame.tile_x_start = 1.0f;
609 _map_frame.screen_edges.right = static_cast<float>(_object_supervisor->_num_grid_cols);
610 _map_frame.screen_edges.left = _map_frame.screen_edges.right - SCREEN_COLS;
611 }
612
613 // Camera exceeds the top boundary of the map
614 if (_map_frame.starting_row < 0) {
615 _map_frame.starting_row = 0;
616 _map_frame.tile_y_start = 2.0f;
617 _map_frame.screen_edges.top = 0.0f;
618 _map_frame.screen_edges.bottom = SCREEN_ROWS;
619 }
620 // Camera exceeds the bottom boundary of the map
621 else if (_map_frame.starting_row + TILE_ROWS >= _tile_supervisor->_num_tile_rows) {
622 _map_frame.starting_row = static_cast<int16>(_tile_supervisor->_num_tile_rows - TILE_ROWS);
623 _map_frame.tile_y_start = 2.0f;
624 _map_frame.screen_edges.bottom = static_cast<float>(_object_supervisor->_num_grid_rows);
625 _map_frame.screen_edges.top = _map_frame.screen_edges.bottom - SCREEN_ROWS;
626 }
627
628 // ---------- (4) Determine the number of rows and columns of tiles that need to be drawn
629
630 // When the tile images align perfectly with the screen, we can afford to draw one less row or column of tiles
631 if (IsFloatInRange(_map_frame.tile_x_start, 0.999f, 1.001f)) {
632 _map_frame.num_draw_cols = TILE_COLS;
633 }
634 else {
635 _map_frame.num_draw_cols = TILE_COLS + 1;
636 }
637 if (IsFloatInRange(_map_frame.tile_y_start, 1.999f, 2.001f)) {
638 _map_frame.num_draw_rows = TILE_ROWS;
639 }
640 else {
641 _map_frame.num_draw_rows = TILE_ROWS + 1;
642 }
643
644 // Comment this out to print out debugging info about each map frame that is drawn
645 // printf("--- MAP DRAW INFO ---\n");
646 // printf("Starting row, col: [%d, %d]\n", _map_frame.starting_row, _map_frame.starting_col);
647 // printf("# draw rows, cols: [%d, %d]\n", _map_frame.num_draw_rows, _map_frame.num_draw_cols);
648 // printf("Camera position: [%f, %f]\n", camera_x, camera_y);
649 // printf("Tile draw start: [%f, %f]\n", _map_frame.tile_x_start, _map_frame.tile_y_start);
650 // printf("Edges (T,B,L,R): [%f, %f, %f, %f]\n", _map_frame.screen_edges.top, _map_frame.screen_edges.bottom,
651 // _map_frame.screen_edges.left, _map_frame.screen_edges.right);
652 } // void MapMode::_CalculateMapFrame()
653
654
655
_DrawMapLayers()656 void MapMode::_DrawMapLayers() {
657 VideoManager->SetCoordSys(0.0f, SCREEN_COLS, SCREEN_ROWS, 0.0f);
658
659 _tile_supervisor->DrawLowerLayer(&_map_frame);
660 _tile_supervisor->DrawMiddleLayer(&_map_frame);
661
662 _object_supervisor->DrawGroundObjects(&_map_frame, false); // First draw pass of ground objects
663 _object_supervisor->DrawPassObjects(&_map_frame);
664 _object_supervisor->DrawGroundObjects(&_map_frame, true); // Second draw pass of ground objects
665
666 _tile_supervisor->DrawUpperLayer(&_map_frame);
667
668 _object_supervisor->DrawSkyObjects(&_map_frame);
669 } // void MapMode::_DrawMapLayers()
670
671
672
_DrawGUI()673 void MapMode::_DrawGUI() {
674 // TODO: figure out what this color represents and create an approximate name for it
675 const Color unknown(0.0196f, 0.207f, 0.0196f, 1.0f);
676 const Color lighter_green(0.419f, 0.894f, 0.0f, 1.0f);
677 const Color light_green(0.0196f, 0.207f, 0.0196f, 1.0f);
678 const Color medium_green(0.0509f, 0.556f, 0.0509f, 1.0f);
679 const Color darkish_green(0.352f, 0.4f, 0.352f, 1.0f);
680 const Color dark_green(0.0196f, 0.207f, 0.0196f, 1.0f);
681 const Color bright_yellow(0.937f, 1.0f, 0.725f, 1.0f);
682
683 // ---------- (1) Draw the introductory location name and graphic if necessary
684 if (_intro_timer.IsFinished() == false) {
685 uint32 time = _intro_timer.GetTimeExpired();
686
687 Color blend(1.0f, 1.0f, 1.0f, 1.0f);
688 if (time < 2000) { // Fade in
689 blend.SetAlpha((static_cast<float>(time) / 2000.0f));
690 }
691 else if (time > 5000) { // Fade out
692 blend.SetAlpha(1.0f - static_cast<float>(time - 5000) / 2000.0f);
693 }
694
695 VideoManager->PushState();
696 VideoManager->SetCoordSys(0.0f, 1024.0f, 768.0f, 0.0f);
697 VideoManager->SetDrawFlags(VIDEO_X_CENTER, VIDEO_Y_CENTER, 0);
698 VideoManager->Move(512.0f, 100.0f);
699 _location_graphic.Draw(blend);
700 VideoManager->MoveRelative(0.0f, -80.0f);
701 VideoManager->Text()->Draw(_map_name, TextStyle("title24", blend, VIDEO_TEXT_SHADOW_DARK));
702 VideoManager->PopState();
703 }
704
705 // ---------- (2) Draw the stamina bar in the lower right corner
706 // TODO: the code in this section needs better comments to explain what each coloring step is doing
707 float fill_size = static_cast<float>(_run_stamina) / 10000.0f;
708
709 VideoManager->PushState();
710 VideoManager->SetCoordSys(0.0f, 1024.0f, 768.0f, 0.0f);
711 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
712
713 // Draw the background image
714 VideoManager->Move(780, 747);
715 _stamina_bar_background.Draw();
716 VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, VIDEO_NO_BLEND, 0);
717
718 // Draw the base color of the bar
719 VideoManager->Move(800, 740);
720 VideoManager->DrawRectangle(200 * fill_size, 10, unknown);
721
722 // Shade the bar with a faux lighting effect
723 VideoManager->Move(800,739);
724 VideoManager->DrawRectangle(200 * fill_size, 2, dark_green);
725 VideoManager->Move(800, 737);
726 VideoManager->DrawRectangle(200 * fill_size, 7, darkish_green);
727
728 // Only do this if the bar is at least 4 pixels long
729 if ((200 * fill_size) >= 4) {
730 VideoManager->Move(801, 739);
731 VideoManager->DrawRectangle((200 * fill_size) -2, 1, darkish_green);
732
733 VideoManager->Move(801, 738);
734 VideoManager->DrawRectangle(1, 2, medium_green);
735 VideoManager->Move(800 + (fill_size * 200 - 2), 738); // Automatically reposition to be at moving endcap
736 VideoManager->DrawRectangle(1, 2, medium_green);
737 }
738
739 VideoManager->Move(800, 736);
740 VideoManager->DrawRectangle(200 * fill_size, 5, medium_green);
741
742 // Only do this if the bar is at least 4 pixels long
743 if ((200 * fill_size) >= 4) {
744 VideoManager->Move(801, 735);
745 VideoManager->DrawRectangle(1, 1, lighter_green);
746 VideoManager->Move(800 + (fill_size * 200 - 2), 735); // automatically reposition to be at moving endcap
747 VideoManager->DrawRectangle(1, 1, lighter_green);
748 VideoManager->Move(800, 734);
749 VideoManager->DrawRectangle(200 * fill_size, 2, lighter_green);
750 }
751
752 // Only do this if the bar is at least 6 pixels long
753 if ((200 * fill_size) >= 6) {
754 VideoManager->Move(802, 733);
755 VideoManager->DrawRectangle((200 * fill_size) - 4, 1, bright_yellow);
756 }
757
758 if (_run_forever) { // Draw the infinity symbol over the stamina bar
759 VideoManager->SetDrawFlags(VIDEO_BLEND, 0);
760 VideoManager->Move(780, 747);
761 _stamina_bar_infinite_overlay.Draw();
762 }
763
764 VideoManager->PopState();
765
766 // ---------- (3) Draw the treasure menu if necessary
767 if (_treasure_supervisor->IsActive() == true)
768 _treasure_supervisor->Draw();
769 } // void MapMode::_DrawGUI()
770
771 } // namespace hoa_map
772
773