257 lines
6.8 KiB
C++
257 lines
6.8 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/nuvie/core/nuvie_defs.h"
|
|
#include "ultima/nuvie/conf/configuration.h"
|
|
#include "ultima/nuvie/misc/call_back.h"
|
|
#include "ultima/nuvie/files/nuvie_io.h"
|
|
#include "ultima/nuvie/misc/u6_misc.h"
|
|
#include "ultima/nuvie/misc/u6_llist.h"
|
|
#include "ultima/nuvie/core/weather.h"
|
|
#include "ultima/nuvie/core/game.h"
|
|
#include "ultima/nuvie/core/game_clock.h"
|
|
#include "ultima/nuvie/save/obj_list.h"
|
|
#include "ultima/nuvie/core/timed_event.h"
|
|
#include "ultima/nuvie/views/view_manager.h"
|
|
#include "ultima/nuvie/gui/widgets/map_window.h"
|
|
#include "ultima/nuvie/core/map.h"
|
|
#include "ultima/nuvie/script/script.h"
|
|
|
|
namespace Ultima {
|
|
namespace Nuvie {
|
|
|
|
//the longest we will go before having a change in wind direction
|
|
#define WEATHER_MAX_WIND 30
|
|
|
|
Weather::Weather(const Configuration *cfg, GameClock *c, nuvie_game_t type)
|
|
: config(cfg), _clock(c), gametype(type), wind_dir(NUVIE_DIR_NONE),
|
|
wind_timer(nullptr) {
|
|
string s;
|
|
config->value(config_get_game_key(config) + "/displayed_wind_dir", s, "from");
|
|
if (s == "to")
|
|
display_from_wind_dir = false;
|
|
else
|
|
display_from_wind_dir = true;
|
|
}
|
|
|
|
Weather::~Weather() {
|
|
}
|
|
|
|
bool Weather::load(NuvieIO *objlist) {
|
|
clear_wind();
|
|
|
|
if (gametype == NUVIE_GAME_U6) {
|
|
wind_dir = load_wind(objlist);
|
|
set_wind_change_callback(); //set a timer to change the wind direction in the future.
|
|
send_wind_change_notification_callback();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
MapCoord Weather::get_moonstone(uint8 moonstone) {
|
|
if (moonstone < 8) // FIXME: hardcoded constant
|
|
return Game::get_game()->get_script()->call_moonstone_get_loc(moonstone + 1);
|
|
|
|
DEBUG(0, LEVEL_ERROR, "get_moonstone(%d): Moonstone out of range\n", moonstone);
|
|
return MapCoord(0, 0, 0);
|
|
}
|
|
bool Weather::set_moonstone(uint8 moonstone, MapCoord where) {
|
|
if (moonstone < 8) { // FIXME: hardcoded constant
|
|
Game::get_game()->get_script()->call_moonstone_set_loc(moonstone + 1, where); //phase starts at 1 in script.
|
|
return true;
|
|
}
|
|
DEBUG(0, LEVEL_ERROR, "set_moonstone(%d): Moonstone out of range\n", moonstone);
|
|
return false;
|
|
}
|
|
|
|
void Weather::update_moongates() {
|
|
Game::get_game()->get_script()->call_update_moongates(is_moon_visible());
|
|
}
|
|
|
|
NuvieDir Weather::load_wind(NuvieIO *objlist) {
|
|
const NuvieDir wind_tbl[8] = {
|
|
NUVIE_DIR_N,
|
|
NUVIE_DIR_NE,
|
|
NUVIE_DIR_E,
|
|
NUVIE_DIR_SE,
|
|
NUVIE_DIR_S,
|
|
NUVIE_DIR_SW,
|
|
NUVIE_DIR_W,
|
|
NUVIE_DIR_NW
|
|
};
|
|
|
|
objlist->seek(OBJLIST_OFFSET_U6_WIND_DIR);
|
|
uint8 objlist_wind = objlist->read1();
|
|
|
|
if (objlist_wind > 7) //objlist 0xff = Calm 'C'
|
|
return NUVIE_DIR_NONE;
|
|
|
|
return wind_tbl[objlist_wind];
|
|
}
|
|
|
|
void Weather::clear_wind() {
|
|
if (wind_timer) {
|
|
wind_timer->stop_timer();
|
|
wind_timer = nullptr;
|
|
}
|
|
|
|
|
|
wind_dir = NUVIE_DIR_NONE;
|
|
|
|
return;
|
|
}
|
|
|
|
bool Weather::save(NuvieIO *objlist) {
|
|
if (gametype == NUVIE_GAME_U6) {
|
|
save_wind(objlist);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Weather::save_wind(NuvieIO *objlist) {
|
|
const uint8 wind_tbl[] = {
|
|
OBJLIST_U6_WIND_DIR_N,
|
|
OBJLIST_U6_WIND_DIR_S,
|
|
OBJLIST_U6_WIND_DIR_E,
|
|
OBJLIST_U6_WIND_DIR_W,
|
|
OBJLIST_U6_WIND_DIR_NE,
|
|
OBJLIST_U6_WIND_DIR_SE,
|
|
OBJLIST_U6_WIND_DIR_SW,
|
|
OBJLIST_U6_WIND_DIR_NW,
|
|
OBJLIST_U6_WIND_DIR_C
|
|
};
|
|
|
|
objlist->seek(OBJLIST_OFFSET_U6_WIND_DIR);
|
|
objlist->write1(wind_tbl[wind_dir]);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Weather::is_eclipse() const {
|
|
if (gametype != NUVIE_GAME_U6 || _clock->get_timer(GAMECLOCK_TIMER_U6_ECLIPSE) == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Weather::is_moon_visible() const {
|
|
//FIXME this is duplicated logic. Maybe we should look at how the original works out moon locations
|
|
|
|
uint8 day = _clock->get_day();
|
|
uint8 hour = _clock->get_hour();
|
|
// trammel (starts 1 hour ahead of sun)
|
|
uint8 phase = uint8(nearbyint((day - 1) / TRAMMEL_PHASE)) % 8;
|
|
uint8 posA = ((hour + 1) + 3 * phase) % 24; // advance 3 positions each phase-change
|
|
if (posA >= 5 && posA <= 19)
|
|
return true;
|
|
|
|
// felucca (starts 1 hour behind sun)
|
|
// ...my FELUCCA_PHASE may be wrong but this method works with it...
|
|
sint8 phaseb = (day - 1) % uint8(nearbyint(FELUCCA_PHASE * 8)) - 1;
|
|
phase = (phaseb >= 0) ? phaseb : 0;
|
|
uint8 posB = ((hour - 1) + 3 * phase) % 24; // advance 3 positions per phase-change
|
|
|
|
if (posB >= 5 && posB <= 19)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
string Weather::get_wind_dir_str() const {
|
|
if (display_from_wind_dir) {
|
|
static const char from_names[9][3] = {"N", "E", "S", "W", "NE", "SE", "SW", "NW", "C"};
|
|
return from_names[wind_dir];
|
|
} else {
|
|
static const char to_names[9][3] = {"S", "W", "N", "E", "SW", "NW", "NE", "SE", "C"};
|
|
return to_names[wind_dir];
|
|
}
|
|
}
|
|
|
|
void Weather::change_wind_dir() {
|
|
NuvieDir new_wind_dir = static_cast<NuvieDir>(NUVIE_RAND() % 9);
|
|
|
|
set_wind_dir(new_wind_dir);
|
|
return;
|
|
}
|
|
|
|
bool Weather::set_wind_dir(NuvieDir new_wind_dir) {
|
|
NuvieDir old_wind_dir = wind_dir;
|
|
|
|
if (new_wind_dir >= 9)
|
|
return false;
|
|
|
|
clear_wind();
|
|
if (Game::get_game()->get_map_window()->in_dungeon_level())
|
|
wind_dir = NUVIE_DIR_NONE;
|
|
else
|
|
wind_dir = new_wind_dir;
|
|
|
|
if (wind_dir != old_wind_dir)
|
|
send_wind_change_notification_callback();
|
|
|
|
set_wind_change_callback();
|
|
|
|
return true;
|
|
}
|
|
|
|
inline void Weather::set_wind_change_callback() {
|
|
uint16 length = (NUVIE_RAND() % WEATHER_MAX_WIND) + 1;
|
|
uint8 *cb_msgid = new uint8;
|
|
*cb_msgid = WEATHER_CB_CHANGE_WIND_DIR;
|
|
wind_timer = new GameTimedCallback((CallBack *)this, cb_msgid, length);
|
|
DEBUG(0, LEVEL_DEBUGGING, "Adding wind change timer. Length = %d\n", length);
|
|
}
|
|
|
|
inline void Weather::send_wind_change_notification_callback() {
|
|
for (CallBack *cb : wind_change_notification_list)
|
|
cb->callback(WEATHER_CB_CHANGE_WIND_DIR, (CallBack *)this, nullptr);
|
|
}
|
|
|
|
bool Weather::add_wind_change_notification_callback(CallBack *caller) {
|
|
wind_change_notification_list.push_back(caller);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
uint16 Weather::callback(uint16 msg, CallBack *caller, void *data) {
|
|
uint8 *cb_msgid = (uint8 *)callback_user_data;
|
|
|
|
switch (*cb_msgid) {
|
|
case WEATHER_CB_CHANGE_WIND_DIR :
|
|
wind_timer = nullptr;
|
|
change_wind_dir();
|
|
break;
|
|
default :
|
|
DEBUG(0, LEVEL_ERROR, "Weather: Unknown callback!\n");
|
|
break;
|
|
}
|
|
|
|
delete cb_msgid;
|
|
|
|
return 1;
|
|
}
|
|
|
|
} // End of namespace Nuvie
|
|
} // End of namespace Ultima
|