Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 610 lines (445 sloc) 20.074 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
#ifndef LEVEL_HPP_INCLUDED
#define LEVEL_HPP_INCLUDED

#include <bitset>
#include <deque>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>

#include <boost/array.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/scoped_ptr.hpp>

#include "background.hpp"
#include "color_utils.hpp"
#include "decimal.hpp"
#include "entity.hpp"
#include "formula.hpp"
#include "formula_callable.hpp"
#include "formula_callable_definition_fwd.hpp"
#include "geometry.hpp"
#include "gui_formula_functions.hpp"
#include "level_object.hpp"
#include "level_solid_map.hpp"
#include "movement_script.hpp"
#include "raster.hpp"
#include "speech_dialog.hpp"
#include "tile_map.hpp"
#include "variant.hpp"
#include "water.hpp"
#include "color_utils.hpp"

class tile_corner;

class level;
class current_level_scope {
boost::intrusive_ptr<level> old_;
public:
explicit current_level_scope(level* ptr);
~current_level_scope();
};

class level : public game_logic::formula_callable
{
public:
struct summary {
std::string music, title;
};

static summary get_summary(const std::string& id);

static level& current();
static level* current_ptr();
void set_as_current_level();
static void clear_current_level();

static int tile_rebuild_state_id();

explicit level(const std::string& level_cfg, variant node=variant());
~level();

//function to do anything which loads the level and must be done
//in the main thread.
void finish_loading();

//function which sets which player we're controlling on this machine.
void set_multiplayer_slot(int slot);

const std::string& replay_data() const { return replay_data_; }
void load_save_point(const level& lvl);
void set_save_point(int x, int y) { save_point_x_ = x; save_point_y_ = y; }

const std::string& id() const { return id_; }
void set_id(const std::string& s) { id_ = s; }
const std::string& music() const { return music_; }

std::string package() const;

variant write() const;
void draw(int x, int y, int w, int h) const;
void draw_status() const;
void draw_debug_solid(int x, int y, int w, int h) const;
void draw_background(int x, int y, int rotation) const;
void process();
void set_active_chars();
void process_draw();
bool standable(const rect& r, const surface_info** info=NULL) const;
bool standable(int x, int y, const surface_info** info=NULL) const;
bool standable_tile(int x, int y, const surface_info** info=NULL) const;
bool solid(int x, int y, const surface_info** info=NULL) const;
bool solid(const entity& e, const std::vector<point>& points, const surface_info** info=NULL) const;
bool solid(const rect& r, const surface_info** info=NULL) const;
bool solid(int xbegin, int ybegin, int w, int h, const surface_info** info=NULL) const;
bool may_be_solid_in_rect(const rect& r) const;
void set_solid_area(const rect& r, bool solid);
entity_ptr board(int x, int y) const;
const rect& boundaries() const { return boundaries_; }
void set_boundaries(const rect& bounds) { boundaries_ = bounds; }
void add_tile(const level_tile& t);
void add_tile_rect(int zorder, int x1, int y1, int x2, int y2, const std::string& tile);
void add_tile_rect_vector(int zorder, int x1, int y1, int x2, int y2, const std::vector<std::string>& tiles);
void set_tile_layer_speed(int zorder, int x_speed, int y_speed);
void refresh_tile_rect(int x1, int y1, int x2, int y2);
void get_tile_rect(int zorder, int x1, int y1, int x2, int y2, std::vector<std::string>& tiles) const;
void get_all_tiles_rect(int x1, int y1, int x2, int y2, std::map<int, std::vector<std::string> >& tiles) const;
void clear_tile_rect(int x1, int y1, int x2, int y2);
void remove_tiles_at(int x, int y);

//function to do 'magic wand' selection -- given an x/y pixel position,
//will return all the solid tiles connected
std::vector<point> get_solid_contiguous_region(int xpos, int ypos) const;

const level_tile* get_tile_at(int x, int y) const;
void remove_character(entity_ptr e);
std::vector<entity_ptr> get_characters_in_rect(const rect& r, int screen_xpos, int screen_ypos) const;
std::vector<entity_ptr> get_characters_at_point(int x, int y, int screen_xpos, int screen_ypos) const;
entity_ptr get_next_character_at_point(int x, int y, int screen_xpos, int screen_ypos) const;
const player_info* player() const { return player_ ? player_->get_player_info() : NULL; }
player_info* player() { return player_ ? player_->get_player_info() : NULL; }
std::vector<entity_ptr>& players() { return players_; }
const std::vector<entity_ptr>& players() const { return players_; }
void add_multi_player(entity_ptr p);
void add_player(entity_ptr p);
void add_character(entity_ptr p);

//add a character that will be drawn on the scene. It will be removed
//from the level next time set_active_chars() is called.
void add_draw_character(entity_ptr p);

//schedule a character for removal at the end of the current cycle.
void schedule_character_removal(entity_ptr p);

//sets the last 'touched' player. This is the player found in the level when
//using WML, so it works reasonably well in multiplayer.
void set_touched_player(entity_ptr p) { last_touched_player_ = p; }

struct portal {
portal() : dest_starting_pos(false), automatic(false), saved_game(false)
{}
rect area;
std::string level_dest;
std::string dest_label;
std::string dest_str;
point dest;
bool dest_starting_pos;
bool automatic;
std::string transition;
bool saved_game;
entity_ptr new_playable;
};

//function which will make it so the next call to get_portal() will return
//a pointer to a copy of the given portal. i.e. this makes the character immediately
//enter a portal.
void force_enter_portal(const portal& p);

//the portal the character has entered (if any)
const portal* get_portal() const;

int xscale() const { return xscale_; }
int yscale() const { return yscale_; }

int group_size(int group) const;
void set_character_group(entity_ptr c, int group_num);
int add_group();

#ifndef NO_EDITOR
void set_editor(bool value=true) { editor_ = value; if(editor_) { prepare_tiles_for_drawing(); } }
#else
void set_editor(bool value=true) {}
#endif // !NO_EDITOR
void set_editor_highlight(entity_ptr c) { editor_highlight_ = c; }
entity_ptr editor_highlight() const { return editor_highlight_; }

void editor_select_object(entity_ptr c);
void editor_clear_selection();

const std::vector<entity_ptr>& editor_selection() const { return editor_selection_; }

bool show_foreground() const { return show_foreground_; }
void set_show_foreground(bool value) { show_foreground_ = value; }

bool show_background() const { return show_background_; }
void set_show_background(bool value) { show_background_ = value; }

const std::string& get_background_id() const;
void set_background_by_id(const std::string& id);

//a function to start rebuilding tiles in a background thread.
void start_rebuild_tiles_in_background(const std::vector<int>& layers);

//a function which, if rebuilding tiles has been completed, will update
//with the new tiles.
void complete_rebuild_tiles_in_background();

//stop calls to start_rebuild_tiles_in_background from proceeding
//until unfreeze_rebuild_tiles_in_background() is called.
void freeze_rebuild_tiles_in_background();

void unfreeze_rebuild_tiles_in_background();

void rebuild_tiles();

const std::string& title() const { return title_; }
void set_title(const std::string& t) { title_ = t; }

int variations(int xtile, int ytile) const;
void flip_variations(int xtile, int ytile, int delta=1);

int auto_move_camera_x() const { return auto_move_camera_.x; }
int auto_move_camera_y() const { return auto_move_camera_.y; }

int air_resistance() const { return air_resistance_; }
int water_resistance() const { return water_resistance_; }

int camera_rotation() const;

void set_end_game() { end_game_ = true; }
bool end_game() const { return end_game_; }

//Function used when the player is entering the level at a certain point.
//Will take the name of a destination within a level and return the point
//at that location. For now only takes "left" and "right".
point get_dest_from_str(const std::string& key) const;

//levels ended up at by exiting this level to the left or right.
const std::string& previous_level() const;
const std::string& next_level() const;

void set_previous_level(const std::string& name);
void set_next_level(const std::string& name);

int cycle() const { return cycle_; }
bool in_dialog() const { return in_dialog_; }
void set_in_dialog(bool value) { in_dialog_ = value; }
bool is_underwater(const rect& r, rect* res_water_area=NULL, variant* v=NULL) const;

void get_current(const entity& e, int* velocity_x, int* velocity_y) const;

water* get_water() { return water_.get(); }
const water* get_water() const { return water_.get(); }

water& get_or_create_water();

entity_ptr get_entity_by_label(const std::string& label);
const_entity_ptr get_entity_by_label(const std::string& label) const;

void get_all_labels(std::vector<std::string>& labels) const;

const std::vector<entity_ptr>& get_active_chars() const { return active_chars_; }
const std::vector<entity_ptr>& get_chars() const { return chars_; }
const std::vector<entity_ptr>& get_solid_chars() const;
void swap_chars(std::vector<entity_ptr>& v) { chars_.swap(v); solid_chars_.clear(); }
int num_active_chars() const { return active_chars_.size(); }

void begin_movement_script(const std::string& name, entity& e);
void end_movement_script();

//function which, given the rect of the player's body will return true iff
//the player can currently "interact" with a portal or object. i.e. if
//pressing up will talk to someone or enter a door etc.
bool can_interact(const rect& body) const;

int earliest_backup_cycle() const;
void replay_from_cycle(int ncycle);
void backup();
void reverse_one_cycle();
void reverse_to_cycle(int ncycle);

void transfer_state_to(level& lvl);

//gets historical 'shadows' of a given object back to the given cycle
std::vector<entity_ptr> trace_past(entity_ptr e, int ncycle);

std::vector<entity_ptr> predict_future(entity_ptr e, int ncycles);

bool is_multiplayer() const { return players_.size() > 1; }

void get_tile_layers(std::set<int>* all_layers, std::set<int>* hidden_layers=NULL);
void hide_tile_layer(int layer, bool is_hidden);

void highlight_tile_layer(int layer) { highlight_layer_ = layer; }

void hide_object_classification(const std::string& classification, bool hidden);
const std::set<std::string>& hidden_object_classifications() const { return hidden_classifications_; }

bool object_classification_hidden(const entity& e) const;

const point* lock_screen() const { return lock_screen_.get(); }

void editor_freeze_tile_updates(bool value);

decimal zoom_level() const;

void add_speech_dialog(boost::shared_ptr<speech_dialog> d);
void remove_speech_dialog();
boost::shared_ptr<const speech_dialog> current_speech_dialog() const;

const std::vector<entity_ptr>& focus_override() const { return focus_override_; }

static const game_logic::formula_callable_definition& get_formula_definition();
#ifndef NO_EDITOR
bool in_editor() const {return editor_;}
#else
bool in_editor() const {return false;}
#endif // !NO_EDITOR

void set_editor_dragging_objects() { editor_dragging_objects_ = true; }
bool is_editor_dragging_objects() const { return editor_dragging_objects_; }

void add_sub_level(const std::string& lvl, int xoffset, int yoffset, bool add_objects=true);
void remove_sub_level(const std::string& lvl);
void adjust_level_offset(int xoffset, int yoffset);

bool relocate_object(entity_ptr e, int x, int y);

int segment_width() const { return segment_width_; }
void set_segment_width(int width) { segment_width_ = width; }

int segment_height() const { return segment_height_; }
void set_segment_height(int height) { segment_height_ = height; }

bool is_arcade_level() const { return segment_height_ != 0 || segment_width_ != 0; }

variant get_var(const std::string& str) const {
return vars_[str];
}
void set_var(const std::string& str, variant value) { vars_ = vars_.add_attr(variant(str), value); }

bool set_dark(bool value) { bool res = dark_; dark_ = value; return res; }

void record_zorders();

int current_difficulty() const;

int x_resolution() const { return x_resolution_; }
int y_resolution() const { return y_resolution_; }

bool gui_event(const SDL_Event &event);

typedef std::vector<level_tile>::const_iterator TileItor;
std::pair<TileItor, TileItor> tiles_at_loc(int x, int y) const;

const std::vector<std::string>& debug_properties() const { return debug_properties_; }


private:

void read_compiled_tiles(variant node, std::vector<level_tile>::iterator& out);

void complete_tiles_refresh();
void prepare_tiles_for_drawing();

void do_processing();

void calculate_lighting(int x, int y, int w, int h) const;

bool add_tile_rect_vector_internal(int zorder, int x1, int y1, int x2, int y2, const std::vector<std::string>& tiles);

void draw_layer(int layer, int x, int y, int w, int h) const;
void draw_layer_solid(int layer, int x, int y, int w, int h) const;

void rebuild_tiles_rect(const rect& r);
void add_tile_solid(const level_tile& t);
void add_solid_rect(int x1, int y1, int x2, int y2, int friction, int traction, int damage, const std::string& info);
void add_solid(int x, int y, int friction, int traction, int damage, const std::string& info);
void add_standable(int x, int y, int friction, int traction, int damage, const std::string& info);
typedef std::pair<int,int> tile_pos;
typedef std::bitset<TileSize*TileSize> tile_bitmap;

std::string id_;
std::string music_;
std::string replay_data_;
int cycle_;

int time_freeze_;

bool in_dialog_;

//preferred screen dimensions to play the level on.
int x_resolution_, y_resolution_;

variant vars_;

level_solid_map solid_;
level_solid_map standable_;

level_solid_map solid_base_;
level_solid_map standable_base_;

bool is_solid(const level_solid_map& map, int x, int y, const surface_info** surf_info) const;
bool is_solid(const level_solid_map& map, const entity& e, const std::vector<point>& points, const surface_info** surf_info) const;

void set_solid(level_solid_map& map, int x, int y, int friction, int traction, int damage, const std::string& info, bool solid=true);

variant get_value_by_slot(int slot) const;
variant get_value(const std::string& key) const;
void set_value(const std::string& key, const variant& value);

std::string title_;

rect boundaries_;

struct solid_rect {
rect r;
int friction;
int traction;
int damage;
};
std::vector<solid_rect> solid_rects_;
mutable std::vector<level_tile> tiles_;

mutable std::vector<hex_level_tile> hex_tiles_;

//tiles sorted by position rather than zorder.
mutable std::vector<level_tile> tiles_by_position_;
std::set<int> layers_;
std::set<int> hidden_layers_; //layers hidden in the editor.
int highlight_layer_;

struct layer_blit_info {
layer_blit_info() : texture_id(0), xbase(-1), ybase(-1)
{}

GLuint texture_id;

std::vector<tile_corner> blit_vertexes;

//texture ID's for each set of corners, used if texture_id == -1
//(i.e. if there are
std::vector<GLuint> vertex_texture_ids;

int xbase, ybase;

//OpenGL ES 1.1 only supports indices of the types GL_UNSIGNED_BYTE and
//GL_UNSIGNED_SHORT in the glDrawElements call. So use shorts on ES 1.1
//platforms. Since we compile tiles on them and solid colored tiles are
//not counted, this is probably fine.
#ifdef SDL_VIDEO_OPENGL_ES
typedef GLshort IndexType;
#define TILE_INDEX_TYPE GL_UNSIGNED_SHORT
#define TILE_INDEX_TYPE_MAX SHRT_MAX
#else
typedef GLint IndexType;
#define TILE_INDEX_TYPE GL_UNSIGNED_INT
#define TILE_INDEX_TYPE_MAX INT_MAX
#endif

//a two dimensional array of indexes into vertex_texture_ids,
//representing the tiles in a layer.
std::vector<std::vector<IndexType> > indexes;

//we have two blit queues for a layer. One to draw tiles which have
//some alpha (GL_BLEND enabled) and others which are completely opaque
//and can be drawn more efficiently without alpha blending.
std::vector<IndexType> opaque_indexes, translucent_indexes;

rect tile_positions;
};

mutable std::map<int, layer_blit_info> blit_cache_;

struct solid_color_rect {
graphics::color color;
rect area;
int layer;
};

struct solid_color_rect_empty {
bool operator()(const solid_color_rect& r) const { return r.area.w() == 0; }
};

struct solid_color_rect_cmp {
bool operator()(const solid_color_rect& r, int zorder) const { return r.layer < zorder; }
bool operator()(int zorder, const solid_color_rect& r) const { return zorder < r.layer; }
bool operator()(const solid_color_rect& a, const solid_color_rect& b) const { return a.layer < b.layer; }
};

std::vector<solid_color_rect> solid_color_rects_;

std::vector<rect> opaque_rects_;

void erase_char(entity_ptr c);
std::vector<entity_ptr> chars_;
std::vector<entity_ptr> active_chars_;
mutable std::vector<entity_ptr> solid_chars_;

std::vector<entity_ptr> chars_immune_from_time_freeze_;

std::map<std::string, entity_ptr> chars_by_label_;
entity_ptr player_;
entity_ptr last_touched_player_;

std::vector<entity_ptr> players_;

//characters stored in wml format; they can't be loaded in a separate thread
//they will be loaded when complete_load_level() is called.
std::vector<variant> wml_chars_;
std::vector<variant> serialized_objects_;

std::vector<variant> wml_compiled_tiles_;
int num_compiled_tiles_;

void load_character(variant c);

typedef std::vector<entity_ptr> entity_group;
std::vector<entity_group> groups_;

portal left_portal_, right_portal_;
std::vector<portal> portals_;

mutable bool entered_portal_active_;
portal entered_portal_;

boost::shared_ptr<background> background_;
point background_offset_;
int widest_tile_, highest_tile_;

std::map<int, tile_map> tile_maps_;
std::map<int, hex_tile_map> hex_tile_maps_;
int xscale_, yscale_;

int save_point_x_, save_point_y_;
bool editor_;
entity_ptr editor_highlight_;

std::vector<entity_ptr> editor_selection_;

bool show_foreground_, show_background_;

bool dark_;
graphics::color_transform dark_color_;

point auto_move_camera_;
int air_resistance_;
int water_resistance_;

game_logic::const_formula_ptr camera_rotation_;
bool end_game_;

std::vector<std::string> preloads_; //future levels to preload

boost::shared_ptr<water> water_;

std::map<std::string, movement_script> movement_scripts_;
std::vector<active_movement_script_ptr> active_movement_scripts_;

boost::shared_ptr<point> lock_screen_;

struct backup_snapshot {
unsigned int rng_seed;
int cycle;
std::vector<entity_ptr> chars;
std::vector<entity_ptr> players;
std::vector<entity_group> groups;
entity_ptr player, last_touched_player;
};

void restore_from_backup(backup_snapshot& snapshot);

typedef boost::shared_ptr<backup_snapshot> backup_snapshot_ptr;

std::deque<backup_snapshot_ptr> backups_;

int editor_tile_updates_frozen_;
bool editor_dragging_objects_;

std::vector<std::string> gui_algo_str_;
std::vector<gui_algorithm_ptr> gui_algorithm_;

decimal zoom_level_;
std::vector<entity_ptr> focus_override_;

std::stack<boost::shared_ptr<speech_dialog> > speech_dialogs_;

std::set<std::string> hidden_classifications_;

//color palettes that the level has set.
unsigned int palettes_used_;

int background_palette_;

int segment_width_, segment_height_;

struct sub_level_data {
boost::intrusive_ptr<level> lvl;
int xbase, ybase;
int xoffset, yoffset;
bool active;
std::vector<entity_ptr> objects;
};

void build_solid_data_from_sub_levels();

std::string sub_level_str_;
std::map<std::string, sub_level_data> sub_levels_;

//A list of properties that each object in the level should display
//for debugging purposes.
std::vector<std::string> debug_properties_;
};

bool entity_in_current_level(const entity* e);

typedef boost::intrusive_ptr<level> level_ptr;

#endif
Something went wrong with that request. Please try again.