260 lines
7.5 KiB
C++
260 lines
7.5 KiB
C++
/*
|
|
Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
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 2 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.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "formula/function_gamestate.hpp"
|
|
#include "formula/callable_objects.hpp"
|
|
|
|
#include "resources.hpp"
|
|
#include "game_board.hpp"
|
|
#include "map/map.hpp"
|
|
#include "pathutils.hpp"
|
|
#include "units/types.hpp"
|
|
#include "units/unit.hpp"
|
|
|
|
namespace wfl {
|
|
|
|
namespace gamestate {
|
|
|
|
DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
|
|
{
|
|
const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).convert_to<location_callable>()->loc();
|
|
adjacent_loc_array_t adj;
|
|
get_adjacent_tiles(loc, adj.data());
|
|
|
|
std::vector<variant> v;
|
|
for(unsigned n = 0; n < adj.size(); ++n) {
|
|
if(resources::gameboard->map().on_board(adj[n])) {
|
|
v.emplace_back(std::make_shared<location_callable>(adj[n]));
|
|
}
|
|
}
|
|
|
|
return variant(v);
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
|
|
{
|
|
const map_location loc = args()[0]->evaluate(variables, fdb).convert_to<location_callable>()->loc();
|
|
|
|
int range = args()[1]->evaluate(variables, fdb).as_int();
|
|
|
|
if(range < 0) {
|
|
return variant();
|
|
}
|
|
|
|
if(!range) {
|
|
return variant(std::make_shared<location_callable>(loc));
|
|
}
|
|
|
|
std::vector<map_location> res;
|
|
|
|
get_tiles_in_radius(loc, range, res);
|
|
|
|
std::vector<variant> v;
|
|
v.reserve(res.size() + 1);
|
|
v.emplace_back(std::make_shared<location_callable>(loc));
|
|
|
|
for(std::size_t n = 0; n != res.size(); ++n) {
|
|
if(resources::gameboard->map().on_board(res[n])) {
|
|
v.emplace_back(std::make_shared<location_callable>(res[n]));
|
|
}
|
|
}
|
|
|
|
return variant(v);
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(get_unit_type, 1, 1)
|
|
{
|
|
const std::string type = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "get_unit_type:name")).as_string();
|
|
|
|
const unit_type *ut = unit_types.find(type);
|
|
if(ut) {
|
|
return variant(std::make_shared<unit_type_callable>(*ut));
|
|
}
|
|
|
|
return variant();
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(unit_at, 1, 1)
|
|
{
|
|
variant loc_var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_at:location"));
|
|
if(loc_var.is_null()) {
|
|
return variant();
|
|
}
|
|
auto loc = loc_var.convert_to<location_callable>();
|
|
const unit_map::const_iterator i = resources::gameboard->units().find(loc->loc());
|
|
if(i != resources::gameboard->units().end()) {
|
|
return variant(std::make_shared<unit_callable>(*i));
|
|
} else {
|
|
return variant();
|
|
}
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(defense_on, 2, 2)
|
|
{
|
|
variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "defense_on:unit"));
|
|
variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "defense_on:location"));
|
|
if(u.is_null() || loc_var.is_null()) {
|
|
return variant();
|
|
}
|
|
|
|
auto u_call = u.try_convert<unit_callable>();
|
|
auto u_type = u.try_convert<unit_type_callable>();
|
|
const map_location& loc = loc_var.convert_to<location_callable>()->loc();
|
|
|
|
if(u_call) {
|
|
const unit& un = u_call->get_unit();
|
|
|
|
if(un.total_movement() < un.movement_cost((resources::gameboard->map())[loc]))
|
|
return variant();
|
|
|
|
if(!resources::gameboard->map().on_board(loc)) {
|
|
return variant();
|
|
}
|
|
|
|
return variant(100 - un.defense_modifier((resources::gameboard->map())[loc]));
|
|
}
|
|
|
|
if(u_type) {
|
|
const unit_type& un = u_type->get_unit_type();
|
|
|
|
if(un.movement() < un.movement_type().movement_cost((resources::gameboard->map())[loc]))
|
|
return variant();
|
|
|
|
if(!resources::gameboard->map().on_board(loc)) {
|
|
return variant();
|
|
}
|
|
|
|
return variant(100 - un.movement_type().defense_modifier((resources::gameboard->map())[loc]));
|
|
}
|
|
|
|
return variant();
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(chance_to_hit, 2, 2)
|
|
{
|
|
variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "chance_to_hit:unit"));
|
|
variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "chance_to_hit:location"));
|
|
if(u.is_null() || loc_var.is_null()) {
|
|
return variant();
|
|
}
|
|
|
|
auto u_call = u.try_convert<unit_callable>();
|
|
auto u_type = u.try_convert<unit_type_callable>();
|
|
const map_location& loc = loc_var.convert_to<location_callable>()->loc();
|
|
|
|
if(u_call) {
|
|
const unit& un = u_call->get_unit();
|
|
|
|
if(!resources::gameboard->map().on_board(loc)) {
|
|
return variant();
|
|
}
|
|
|
|
return variant(un.defense_modifier((resources::gameboard->map())[loc]));
|
|
}
|
|
|
|
if(u_type) {
|
|
const unit_type& un = u_type->get_unit_type();
|
|
|
|
if(!resources::gameboard->map().on_board(loc)) {
|
|
return variant();
|
|
}
|
|
|
|
return variant(un.movement_type().defense_modifier((resources::gameboard->map())[loc]));
|
|
}
|
|
|
|
return variant();
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(movement_cost, 2, 2)
|
|
{
|
|
variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:unit"));
|
|
variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:location"));
|
|
if(u.is_null() || loc_var.is_null()) {
|
|
return variant();
|
|
}
|
|
//we can pass to this function either unit_callable or unit_type callable
|
|
auto u_call = u.try_convert<unit_callable>();
|
|
auto u_type = u.try_convert<unit_type_callable>();
|
|
const map_location& loc = loc_var.convert_to<location_callable>()->loc();
|
|
|
|
if(u_call) {
|
|
const unit& un = u_call->get_unit();
|
|
|
|
if(!resources::gameboard->map().on_board(loc)) {
|
|
return variant();
|
|
}
|
|
|
|
return variant(un.movement_cost((resources::gameboard->map())[loc]));
|
|
}
|
|
|
|
if(u_type) {
|
|
const unit_type& un = u_type->get_unit_type();
|
|
|
|
if(!resources::gameboard->map().on_board(loc)) {
|
|
return variant();
|
|
}
|
|
|
|
return variant(un.movement_type().movement_cost((resources::gameboard->map())[loc]));
|
|
}
|
|
|
|
return variant();
|
|
}
|
|
|
|
DEFINE_WFL_FUNCTION(enemy_of, 2, 2)
|
|
{
|
|
variant self_v = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "enemy_of:self"));
|
|
variant other_v = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "enemy_of:other"));
|
|
int self, other;
|
|
|
|
if(auto uc = self_v.try_convert<unit_callable>()) {
|
|
// For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :|
|
|
self = uc->get_value("side").as_int() + 1;
|
|
} else if(auto tc = self_v.try_convert<team_callable>()) {
|
|
self = tc->get_value("side").as_int();
|
|
} else {
|
|
self = self_v.as_int();
|
|
}
|
|
|
|
if(auto uc = other_v.try_convert<unit_callable>()) {
|
|
// For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :|
|
|
other = uc->get_value("side").as_int() + 1;
|
|
} else if(auto tc = other_v.try_convert<team_callable>()) {
|
|
other = tc->get_value("side").as_int();
|
|
} else {
|
|
other = other_v.as_int();
|
|
}
|
|
|
|
int num_teams = resources::gameboard->teams().size();
|
|
if(self < 1 || self > num_teams || other < 1 || other > num_teams) {
|
|
return variant(0);
|
|
}
|
|
return variant(resources::gameboard->get_team(self).is_enemy(other) ? 1 : 0);
|
|
}
|
|
|
|
} // namespace gamestate
|
|
|
|
gamestate_function_symbol_table::gamestate_function_symbol_table(std::shared_ptr<function_symbol_table> parent) : function_symbol_table(parent) {
|
|
using namespace gamestate;
|
|
function_symbol_table& functions_table = *this;
|
|
DECLARE_WFL_FUNCTION(get_unit_type);
|
|
DECLARE_WFL_FUNCTION(unit_at);
|
|
DECLARE_WFL_FUNCTION(defense_on);
|
|
DECLARE_WFL_FUNCTION(chance_to_hit);
|
|
DECLARE_WFL_FUNCTION(movement_cost);
|
|
DECLARE_WFL_FUNCTION(adjacent_locs); // This is deliberately duplicated here; this form excludes off-map locations, while the core form does not
|
|
DECLARE_WFL_FUNCTION(locations_in_radius);
|
|
DECLARE_WFL_FUNCTION(enemy_of);
|
|
}
|
|
|
|
}
|