Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
689 lines (590 sloc) 19 KB
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <assert.h>
#include <map>
#include "asserts.hpp"
#include "custom_object.hpp"
#include "custom_object_callable.hpp"
#include "custom_object_functions.hpp"
#include "custom_object_type.hpp"
#include "draw_number.hpp"
#include "foreach.hpp"
#include "formula.hpp"
#include "formula_callable_definition.hpp"
#include "frame.hpp"
#include "graphical_font.hpp"
#include "gui_formula_functions.hpp"
#include "iphone_controls.hpp"
#include "json_parser.hpp"
#include "level.hpp"
#include "preferences.hpp"
#include "raster.hpp"
#include "string_utils.hpp"
#include "unit_test.hpp"
#include "variant_utils.hpp"
using namespace game_logic;
namespace {
typedef boost::function<void (const gui_algorithm* algo)> gui_command_function;
class gui_command : public formula_callable {
variant get_value(const std::string& key) const { return variant(); }
public:
virtual void execute(const gui_algorithm& algo) = 0;
};
class draw_text_command : public gui_command {
const_graphical_font_ptr font_;
std::string text_;
int x_, y_;
public:
draw_text_command(const_graphical_font_ptr font, const std::string& text, int x, int y)
: font_(font), text_(text), x_(x), y_(y)
{}
void execute(const gui_algorithm& algo) {
font_->draw(x_, y_, text_);
}
};
class draw_text_function : public function_expression {
public:
explicit draw_text_function(const args_list& args)
: function_expression("draw_text", args, 3, 4)
{}
private:
variant execute(const formula_callable& variables) const {
std::string font = "default";
int arg = 0;
if(args().size() == 4) {
font = args()[arg++]->evaluate(variables).as_string();
}
std::string text = args()[arg++]->evaluate(variables).as_string();
const int x = args()[arg++]->evaluate(variables).as_int();
const int y = args()[arg++]->evaluate(variables).as_int();
return variant(new draw_text_command(graphical_font::get(font), text, x, y));
}
};
class draw_number_command : public gui_command {
graphics::blit_queue blit_;
public:
draw_number_command(int number, int places, int x, int y)
{
queue_draw_number(blit_, number, places, x, y);
}
void execute(const gui_algorithm& algo) {
blit_.do_blit();
}
};
class draw_number_function : public function_expression {
public:
explicit draw_number_function(const args_list& args)
: function_expression("draw_number", args, 4, 4)
{}
private:
variant execute(const formula_callable& variables) const {
static cache_entry cache[4];
static int cache_loc;
const int number = args()[0]->evaluate(variables).as_int();
const int places = args()[1]->evaluate(variables).as_int();
const int x = args()[2]->evaluate(variables).as_int();
const int y = args()[3]->evaluate(variables).as_int();
for(int n = 0; n != sizeof(cache)/sizeof(*cache); ++n) {
if(x == cache[n].x && y == cache[n].y && number == cache[n].number && places == cache[n].places) {
return cache[n].result;
}
}
const int n = cache_loc++%(sizeof(cache)/sizeof(*cache));
cache[n].x = x;
cache[n].y = y;
cache[n].number = number;
cache[n].places = places;
cache[n].result = variant(new draw_number_command(number, places, x, y));
return cache[n].result;
}
struct cache_entry {
cache_entry() : x(-1), y(-1) {}
int number, places, x, y;
variant result;
};
};
class draw_animation_area_command : public gui_command {
frame_ptr frame_;
int x_, y_;
rect area_;
variant get_value(const std::string& key) const { return variant(); }
public:
draw_animation_area_command(const frame_ptr& f, int x, int y, const rect& area)
: frame_(f), x_(x), y_(y), area_(area)
{}
void execute(const gui_algorithm& algo) {
frame_->draw(x_, y_, area_);
}
};
class draw_animation_area_function : public function_expression {
public:
draw_animation_area_function(gui_algorithm* algo, const args_list& args)
: function_expression("draw_animation_area", args, 4, 7), algo_(algo)
{}
gui_algorithm* algo_;
private:
variant execute(const formula_callable& variables) const {
variant anim = args()[0]->evaluate(variables);
const frame_ptr f = algo_->get_frame(anim.as_string());
if(!f) {
return variant();
}
const int x = args()[1]->evaluate(variables).as_int();
const int y = args()[2]->evaluate(variables).as_int();
int vals[4];
for(int n = 0; n != args().size() - 3; ++n) {
vals[n] = args()[n+3]->evaluate(variables).as_int();
}
rect area;
if(args().size() == 4) {
area = rect(0, 0, vals[0], f->height()/2);
} else if(args().size() == 5) {
area = rect(0, 0, vals[0], vals[1]);
} else {
ASSERT_EQ(args().size(), 7);
area = rect(vals[0], vals[1], vals[2], vals[3]);
}
return variant(new draw_animation_area_command(f, x, y, area));
}
};
class draw_animation_command : public gui_command {
graphics::blit_queue blit_;
variant get_value(const std::string& key) const { return variant(); }
public:
draw_animation_command(const frame_ptr& f, int x, int y)
{
f->draw_into_blit_queue(blit_, x, y);
}
void execute(const gui_algorithm& algo) {
blit_.do_blit();
}
};
class draw_animation_function : public function_expression {
public:
draw_animation_function(gui_algorithm* algo, const args_list& args)
: function_expression("draw_animation", args, 3, 3), algo_(algo)
{}
gui_algorithm* algo_;
private:
variant execute(const formula_callable& variables) const {
variant anim = args()[0]->evaluate(variables);
const frame_ptr f = algo_->get_frame(anim.as_string());
if(!f) {
return variant();
}
const int x = args()[1]->evaluate(variables).as_int();
const int y = args()[2]->evaluate(variables).as_int();
return variant(new draw_animation_command(f, x, y));
}
};
class color_command : public gui_command {
unsigned char r_, g_, b_, a_;
public:
color_command(int r, int g, int b, int a) : r_(r), g_(g), b_(b), a_(a)
{}
void execute(const gui_algorithm& algo) {
glColor4ub(r_, g_, b_, a_);
}
};
class color_function : public function_expression {
public:
explicit color_function(const args_list& args)
: function_expression("color", args, 4, 4)
{}
private:
variant execute(const formula_callable& variables) const {
return variant(new color_command(
args()[0]->evaluate(variables).as_int(),
args()[1]->evaluate(variables).as_int(),
args()[2]->evaluate(variables).as_int(),
args()[3]->evaluate(variables).as_int()));
}
};
class draw_line_command : public gui_command {
decimal width_, x1_, y1_, x2_, y2_;
public:
draw_line_command(decimal width, decimal x1, decimal y1, decimal x2, decimal y2)
: width_(width), x1_(x1), y1_(y1), x2_(x2), y2_(y2)
{}
void execute(const gui_algorithm& algo) {
std::vector<GLfloat>& varray = graphics::global_vertex_array();
varray.clear();
varray.push_back(GLfloat(x1_.as_float()));
varray.push_back(GLfloat(y1_.as_float()));
varray.push_back(GLfloat(x2_.as_float()));
varray.push_back(GLfloat(y2_.as_float()));
#if defined(USE_GLES2)
glLineWidth(GLfloat(width_.as_float()));
gles2::manager gles2_manager(gles2::get_simple_shader());
gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, 0, 0, &varray.front());
glDrawArrays(GL_LINES, 0, varray.size()/2);
#else
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glLineWidth(GLfloat(width_.as_float()));
glVertexPointer(2, GL_FLOAT, 0, &varray.front());
glDrawArrays(GL_LINES, 0, varray.size()/2);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
#endif
}
};
class draw_line_function : public function_expression {
public:
explicit draw_line_function(const args_list& args)
: function_expression("draw_line", args, 5, 5)
{}
private:
variant execute(const formula_callable& variables) const {
return variant(new draw_line_command((args()[0]->evaluate(variables).as_decimal()),
(args()[1]->evaluate(variables).as_decimal()),
(args()[2]->evaluate(variables).as_decimal()),
(args()[3]->evaluate(variables).as_decimal()),
(args()[4]->evaluate(variables).as_decimal())
));
}
};
class draw_circle_command : public gui_command {
int x_, y_, r_;
public:
draw_circle_command(int x, int y, int r)
: x_(x), y_(y), r_(r)
{}
void execute(const gui_algorithm& algo) {
graphics::draw_circle(x_, y_, r_);
}
};
class draw_circle_function : public function_expression {
public:
explicit draw_circle_function(const args_list& args)
: function_expression("draw_circle", args, 3, 3)
{}
private:
variant execute(const formula_callable& variables) const {
return variant(new draw_circle_command(args()[0]->evaluate(variables).as_int(),
(args()[1]->evaluate(variables).as_int()),
(args()[2]->evaluate(variables).as_int())
));
}
};
class gui_command_function_symbol_table : public function_symbol_table
{
gui_algorithm* algo_;
public:
gui_command_function_symbol_table(gui_algorithm* algo) : algo_(algo)
{}
expression_ptr create_function(
const std::string& fn,
const std::vector<expression_ptr>& args,
const formula_callable_definition* callable_def) const
{
if(fn == "draw_animation") {
return expression_ptr(new draw_animation_function(algo_, args));
} else if(fn == "draw_animation_area") {
return expression_ptr(new draw_animation_area_function(algo_, args));
} else if(fn == "draw_number") {
return expression_ptr(new draw_number_function(args));
} else if(fn == "draw_text") {
return expression_ptr(new draw_text_function(args));
} else if(fn == "color") {
return expression_ptr(new color_function(args));
} else if(fn == "draw_line") {
return expression_ptr(new draw_line_function(args));
} else if(fn == "draw_circle") {
return expression_ptr(new draw_circle_function(args));
}
return function_symbol_table::create_function(fn, args, callable_def);
}
};
const std::string AlgorithmProperties[] = {
"level", "object", "cycle", "screen_width", "screen_height",
};
enum ALGORITHM_PROPERTY_ID {
ALGO_LEVEL, ALGO_OBJECT, ALGO_CYCLE, ALGO_SCREEN_WIDTH, ALGO_SCREEN_HEIGHT,
};
class gui_algorithm_definition : public game_logic::formula_callable_definition
{
public:
static const gui_algorithm_definition& instance() {
static const gui_algorithm_definition def;
return def;
}
gui_algorithm_definition() {
for(int n = 0; n != sizeof(AlgorithmProperties)/sizeof(*AlgorithmProperties); ++n) {
entries_.push_back(entry(AlgorithmProperties[n]));
keys_to_slots_[AlgorithmProperties[n]] = n;
}
entries_[ALGO_OBJECT].type_definition = &custom_object_type::get("dummy_gui_object")->callable_definition();
entries_[ALGO_LEVEL].type_definition = &level::get_formula_definition();
}
int get_slot(const std::string& key) const {
std::map<std::string, int>::const_iterator i = keys_to_slots_.find(key);
if(i == keys_to_slots_.end()) {
return -1;
}
return i->second;
}
const entry* get_entry(int slot) const {
if(slot < 0 || slot >= entries_.size()) {
return NULL;
}
return &entries_[slot];
}
entry* get_entry(int slot) {
if(slot < 0 || slot >= entries_.size()) {
return NULL;
}
return &entries_[slot];
}
int num_slots() const {
return entries_.size();
}
private:
std::vector<entry> entries_;
std::map<std::string, int> keys_to_slots_;
};
} // namespace
gui_algorithm::gui_algorithm(variant node)
: lvl_(NULL),
loaded_(false),
process_formula_(formula::create_optional_formula(node["on_process"], &get_custom_object_functions_symbol_table(), &gui_algorithm_definition::instance())),
cycle_(0), object_(new custom_object("dummy_gui_object", 0, 0, true))
{
gui_command_function_symbol_table symbols(this);
object_->add_ref();
std::vector<std::string> includes = util::split(node["includes"].as_string_default());
foreach(const std::string& inc, includes) {
includes_.push_back(get(inc));
}
set_object(object_);
foreach(variant frame_node, node["animation"].as_list()) {
frame_ptr f(new frame(frame_node));
frames_[frame_node["id"].as_string()] = f;
}
if(node.has_key("buttons")) {
buttons_ = node["buttons"];
}
draw_formula_ = formula::create_optional_formula(node["on_draw"], &symbols, &gui_algorithm_definition::instance());
load_formula_ = formula::create_optional_formula(node["on_load"], &get_custom_object_functions_symbol_table(), &gui_algorithm_definition::instance());
}
gui_algorithm::~gui_algorithm()
{
}
gui_algorithm_ptr gui_algorithm::get(const std::string& key) {
static std::map<std::string, gui_algorithm_ptr> algorithms;
gui_algorithm_ptr& ptr = algorithms[key];
if(!ptr) {
ptr = create(key);
}
return ptr;
}
void gui_algorithm::set_object(boost::intrusive_ptr<custom_object> obj) {
object_ = obj;
foreach(gui_algorithm_ptr inc, includes_) {
inc->set_object(obj);
}
}
void gui_algorithm::new_level() {
cycle_ = 0;
loaded_ = false;
set_object(boost::intrusive_ptr<custom_object>(new custom_object("dummy_gui_object", 0, 0, true)));
if(!buttons_.is_null()) {
object_->mutate_value("buttons", buttons_);
}
}
void gui_algorithm::load(level& lvl) {
if(load_formula_) {
object_->set_level(lvl);
variant result = load_formula_->execute(*this);
object_->execute_command(result);
if(!object_->vars()->has_key("buttons")) {
return;
}
button_formulas_.clear();
foreach(const variant v, object_->vars()->query_value("buttons").as_list()) {
std::string id = v["id"].as_string();
variant r = game_logic::formula(v["hit_rect"]).execute();
button_hit_rects_[id] = rect(r[0].as_int(), r[1].as_int(), r[2].as_int(), r[3].as_int());
//std::cerr << "ID: " << id << ", hit_rect " << button_hit_rects_[id] << std::endl;
if(v.has_key("on_mouse_down")) {
button_formulas_[id].insert(
std::make_pair(SDL_MOUSEBUTTONDOWN,
formula::create_optional_formula(v["on_mouse_down"], &get_custom_object_functions_symbol_table(), &gui_algorithm_definition::instance())));
}
if(v.has_key("on_mouse_up")) {
button_formulas_[id].insert(
std::make_pair(SDL_MOUSEBUTTONUP,
formula::create_optional_formula(v["on_mouse_up"], &get_custom_object_functions_symbol_table(), &gui_algorithm_definition::instance())));
}
if(v.has_key("on_mouse_move")) {
button_formulas_[id].insert(
std::make_pair(SDL_MOUSEMOTION,
formula::create_optional_formula(v["on_mouse_move"], &get_custom_object_functions_symbol_table(), &gui_algorithm_definition::instance())));
}
}
}
}
bool gui_algorithm::gui_event(level& lvl, const SDL_Event &event) {
if(event.type == SDL_MOUSEBUTTONDOWN
|| event.type == SDL_MOUSEBUTTONUP
|| event.type == SDL_MOUSEMOTION) {
int x = event.type == SDL_MOUSEMOTION ? event.motion.x : event.button.x;
int y = event.type == SDL_MOUSEMOTION ? event.motion.y : event.button.y;
int i = event.type == SDL_MOUSEMOTION ? event.motion.which : event.button.which;
map_formula_callable_ptr callable(new map_formula_callable(this));
callable->add("mouse_x", variant(x));
callable->add("mouse_y", variant(y));
callable->add("mouse_index", variant(i));
if(event.type != SDL_MOUSEMOTION) {
callable->add("mouse_button", variant(event.button.button));
}
const point mouse_pos(x, y);
foreach(variant b, buttons_.as_list()) {
std::string id = b["id"].as_string();
callable->add("in_hitrect", variant(point_in_rect(mouse_pos, button_hit_rects_[id])));
callable->add("button_id", b["id"]);
//std::cerr << "mouse event hit in " << b["id"].as_string() << std::endl;
std::map<const int, game_logic::formula_ptr>::iterator it = button_formulas_[id].find(event.type);
if(it != button_formulas_[id].end()) {
object_->set_level(lvl);
object_->reset_mouse_event();
variant result = it->second->execute(*callable);
object_->execute_command(result);
if(object_->mouse_event_swallowed()) {
return true;
}
}
}
}
return false;
}
void gui_algorithm::process(level& lvl) {
lvl_ = &lvl;
if(!loaded_) {
loaded_ = true;
load(lvl);
}
++cycle_;
if((cycle_%2) == 0 && process_formula_) {
object_->set_level(lvl);
variant result = process_formula_->execute(*this);
object_->execute_command(result);
}
foreach(gui_algorithm_ptr p, includes_) {
p->process(lvl);
}
}
void gui_algorithm::draw(const level& lvl) {
lvl_ = &lvl;
//fprintf(stderr, "GUI_DRAW: %p %d\n", this, cycle_);
if((cycle_%2) == 0) {
cached_draw_commands_ = variant();
}
if(cached_draw_commands_.is_null() && draw_formula_) {
cached_draw_commands_ = draw_formula_->execute(*this);
}
execute_command(cached_draw_commands_);
glColor4ub(255, 255, 255, 255);
foreach(gui_algorithm_ptr p, includes_) {
//fprintf(stderr, "DRAW CHILD: %p -> %p\n", this, p.get());
p->draw(lvl);
}
}
void gui_algorithm::execute_command(variant v) {
if(v.is_list()) {
const int num_elements = v.num_elements();
for(int n = 0; n != num_elements; ++n) {
execute_command(v[n]);
}
return;
}
gui_command* cmd = v.try_convert<gui_command>();
if(cmd) {
cmd->execute(*this);
}
}
gui_algorithm_ptr gui_algorithm::create(const std::string& key) {
static const std::string path = preferences::load_compiled() ? "data/compiled/gui/" : "data/gui/";
return gui_algorithm_ptr(new gui_algorithm(json::parse_from_file(path + key + ".cfg")));
}
void gui_algorithm::draw_animation(const std::string& object_name, const std::string& anim, int x, int y, int cycle) const {
const frame* f = NULL;
if(object_name.empty() == false) {
const_custom_object_type_ptr obj = custom_object_type::get(object_name);
if(obj) {
f = &obj->get_frame(anim);
}
}
if(!f) {
const std::map<std::string, frame_ptr>::const_iterator itor = frames_.find(anim);
if(itor != frames_.end()) {
f = itor->second.get();
}
}
if(f) {
if(cycle == -1) {
cycle = cycle_%f->duration();
}
f->draw(x, y, true, false, cycle);
}
}
void gui_algorithm::color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) const
{
glColor4ub(r, g, b, a);
}
frame_ptr gui_algorithm::get_frame(const std::string& id) const
{
const std::map<std::string, frame_ptr>::const_iterator itor = frames_.find(id);
if(itor != frames_.end()) {
return itor->second;
} else {
return frame_ptr();
}
}
variant gui_algorithm::get_value(const std::string& key) const
{
if(key == "level") {
return variant(lvl_);
} else if(key == "object") {
return variant(object_.get());
} else if(key == "cycle") {
return variant(cycle_);
} else if(key == "screen_width") {
return variant(graphics::screen_width());
} else if(key == "screen_height") {
return variant(graphics::screen_height());
} else {
return variant();
}
}
variant gui_algorithm::get_value_by_slot(int slot) const
{
switch(slot) {
case ALGO_LEVEL:
return variant(lvl_);
case ALGO_OBJECT:
return variant(object_.get());
case ALGO_CYCLE:
return variant(cycle_);
case ALGO_SCREEN_WIDTH:
return variant(graphics::screen_width());
case ALGO_SCREEN_HEIGHT:
return variant(graphics::screen_height());
}
return variant();
}
#include "level.hpp"
BENCHMARK(gui_algorithm_bench)
{
static boost::intrusive_ptr<level> lvl;
if(!lvl) {
lvl = boost::intrusive_ptr<level>(new level("titlescreen.cfg"));
lvl->finish_loading();
lvl->set_as_current_level();
}
static gui_algorithm_ptr gui(gui_algorithm::get("default"));
BENCHMARK_LOOP {
gui->draw(*lvl);
}
}
Something went wrong with that request. Please try again.