From bb16c32bde58cba70e4877aa2d3ebd04332eb575 Mon Sep 17 00:00:00 2001 From: jacopograndi Date: Tue, 4 Jan 2022 13:35:02 +0100 Subject: linux compile and imgs --- game/ai/action.cpp | 150 +++--- game/ai/action.h | 102 ++-- game/ai/engine.h | 130 +++--- game/ai/evaluator.h | 58 +-- game/ai/generator.h | 202 ++++---- game/ai/performer.h | 96 ++-- game/ai/tactic.h | 62 +-- game/constants.h | 32 +- game/entity.cpp | 20 +- game/entity.h | 162 +++---- game/ground.cpp | 262 +++++------ game/ground.h | 72 +-- game/gst.cpp | 1208 ++++++++++++++++++++++++------------------------ game/gst.h | 248 +++++----- game/load.cpp | 298 ++++++------ game/load.h | 14 +- game/menu.cpp | 152 +++--- game/menu.h | 178 +++---- game/player.h | 90 ++-- game/playercontrol.cpp | 896 +++++++++++++++++------------------ game/playercontrol.h | 182 ++++---- game/tech.cpp | 2 +- game/tech.h | 262 +++++------ game/tile.h | 44 +- game/view.cpp | 412 ++++++++--------- game/view.h | 78 ++-- 26 files changed, 2706 insertions(+), 2706 deletions(-) (limited to 'game') diff --git a/game/ai/action.cpp b/game/ai/action.cpp index 63218ba..d85eb4d 100644 --- a/game/ai/action.cpp +++ b/game/ai/action.cpp @@ -1,76 +1,76 @@ -#include "action.h" - -bool ai::compare_action(ai::action a, ai::action b) { - return (a.heuristic < b.heuristic); -} - -std::string ai::action::to_string () { - if (type == actype::attack) { - if (abs(x-mx)+abs(y-my) != 0) { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " move " + - "[" + std::to_string(mx) + ", " + std::to_string(my) + "]" - + " attack " + - "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; - } else { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " attack " + - "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; - } - } - if (type == actype::heal) { - if (abs(x-mx)+abs(y-my) != 0) { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " move " + - "[" + std::to_string(mx) + ", " + std::to_string(my) + "]" - + " heal " + - "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; - } else { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " heal " + - "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; - } - } - if (type == actype::convert) { - if (abs(x-mx)+abs(y-my) != 0) { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " move " + - "[" + std::to_string(mx) + ", " + std::to_string(my) + "]" - + " convert " + - "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; - } else { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " convert " + - "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; - } - } - if (type == actype::move) { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" - + " move " + - "[" + std::to_string(mx) + ", " + std::to_string(my) + "]"; - } - if (type == actype::build) { - if (abs(x-mx)+abs(y-my) != 0) { - return "[" + std::to_string(x) + ", " + std::to_string(y) - + "]" + " move " + - "[" + std::to_string(mx) + ", " + std::to_string(my) - + "]" + " build " + - std::to_string(v); - } else { - return "[" + std::to_string(mx) + ", " + std::to_string(my) - + "]" + " build " + - std::to_string(v); - } - } - if (type == actype::train) { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + - " train " + std::to_string(v); - } - if (type == actype::trade) { - return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + - " trade " + std::to_string(v); - } - if (type == actype::tech) { - return "tech " + std::to_string(v); - } +#include "action.h" + +bool ai::compare_action(ai::action a, ai::action b) { + return (a.heuristic < b.heuristic); +} + +std::string ai::action::to_string () { + if (type == actype::attack) { + if (abs(x-mx)+abs(y-my) != 0) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " move " + + "[" + std::to_string(mx) + ", " + std::to_string(my) + "]" + + " attack " + + "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; + } else { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " attack " + + "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; + } + } + if (type == actype::heal) { + if (abs(x-mx)+abs(y-my) != 0) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " move " + + "[" + std::to_string(mx) + ", " + std::to_string(my) + "]" + + " heal " + + "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; + } else { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " heal " + + "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; + } + } + if (type == actype::convert) { + if (abs(x-mx)+abs(y-my) != 0) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " move " + + "[" + std::to_string(mx) + ", " + std::to_string(my) + "]" + + " convert " + + "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; + } else { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " convert " + + "[" + std::to_string(tx) + ", " + std::to_string(ty) + "]"; + } + } + if (type == actype::move) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " move " + + "[" + std::to_string(mx) + ", " + std::to_string(my) + "]"; + } + if (type == actype::build) { + if (abs(x-mx)+abs(y-my) != 0) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + + "]" + " move " + + "[" + std::to_string(mx) + ", " + std::to_string(my) + + "]" + " build " + + std::to_string(v); + } else { + return "[" + std::to_string(mx) + ", " + std::to_string(my) + + "]" + " build " + + std::to_string(v); + } + } + if (type == actype::train) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " train " + std::to_string(v); + } + if (type == actype::trade) { + return "[" + std::to_string(x) + ", " + std::to_string(y) + "]" + + " trade " + std::to_string(v); + } + if (type == actype::tech) { + return "tech " + std::to_string(v); + } } \ No newline at end of file diff --git a/game/ai/action.h b/game/ai/action.h index 12da451..6b631ca 100644 --- a/game/ai/action.h +++ b/game/ai/action.h @@ -1,52 +1,52 @@ -#ifndef ACTION_H -#define ACTION_H - -#include -#include - -namespace ai { - -enum actype { - attack, heal, convert, build, train, trade, move, tech -}; - -class action { - public: - action (actype type, int v) : - type(type), v(v) {} - action (actype type, int x, int y, int v) : - type(type), x(x), y(y), v(v) {} - action (actype type, int x, int y, int mx, int my) : - type(type), x(x), y(y), mx(mx), my(my) {} - action (actype type, int x, int y, int mx, int my, int v) : - type(type), x(x), y(y), mx(mx), my(my), v(v) {} - action (actype type, int x, int y, int mx, int my, int tx, int ty) : - type(type), x(x), y(y), mx(mx), my(my), tx(tx), ty(ty) {} - action (actype type, int x, int y, int mx, int my, int tx, int ty, int v) : - type(type), x(x), y(y), mx(mx), my(my), tx(tx), ty(ty), v(v) {} - - action (const action& rhs) { - type = rhs.type; x = rhs.x; y = rhs.y; mx = rhs.mx; my = rhs.my; - tx = rhs.tx; ty = rhs.ty; v = rhs.v; - } - action& operator=(const action& rhs) { - type = rhs.type; x = rhs.x; y = rhs.y; mx = rhs.mx; my = rhs.my; - tx = rhs.tx; ty = rhs.ty; v = rhs.v; - } - - actype type; - int x, y; // start - int mx, my; // moved - int tx, ty; // target - int v; // value (id of trainee, building or f/g trade, or tech id) - - float heuristic { 0 }; - - std::string to_string (); -}; - -bool compare_action(action a, action b); - -} - +#ifndef ACTION_H +#define ACTION_H + +#include +#include + +namespace ai { + +enum actype { + attack, heal, convert, build, train, trade, move, tech +}; + +class action { + public: + action (actype type, int v) : + type(type), v(v) {} + action (actype type, int x, int y, int v) : + type(type), x(x), y(y), v(v) {} + action (actype type, int x, int y, int mx, int my) : + type(type), x(x), y(y), mx(mx), my(my) {} + action (actype type, int x, int y, int mx, int my, int v) : + type(type), x(x), y(y), mx(mx), my(my), v(v) {} + action (actype type, int x, int y, int mx, int my, int tx, int ty) : + type(type), x(x), y(y), mx(mx), my(my), tx(tx), ty(ty) {} + action (actype type, int x, int y, int mx, int my, int tx, int ty, int v) : + type(type), x(x), y(y), mx(mx), my(my), tx(tx), ty(ty), v(v) {} + + action (const action& rhs) { + type = rhs.type; x = rhs.x; y = rhs.y; mx = rhs.mx; my = rhs.my; + tx = rhs.tx; ty = rhs.ty; v = rhs.v; + } + action& operator=(const action& rhs) { + type = rhs.type; x = rhs.x; y = rhs.y; mx = rhs.mx; my = rhs.my; + tx = rhs.tx; ty = rhs.ty; v = rhs.v; + } + + actype type; + int x, y; // start + int mx, my; // moved + int tx, ty; // target + int v; // value (id of trainee, building or f/g trade, or tech id) + + float heuristic { 0 }; + + std::string to_string (); +}; + +bool compare_action(action a, action b); + +} + #endif \ No newline at end of file diff --git a/game/ai/engine.h b/game/ai/engine.h index d3755e1..584cb11 100644 --- a/game/ai/engine.h +++ b/game/ai/engine.h @@ -1,66 +1,66 @@ -#ifndef ENGINE_H -#define ENGINE_H - -#include - -#include -#include -#include -#include - -#include "../ground.h" -#include "../gst.h" - -#include "action.h" -#include "tactic.h" -#include "generator.h" -#include "evaluator.h" -#include "performer.h" - -namespace ai { - -class engine { - public: - engine (Gst &gst) : init(gst) {} - Gst &init; - - tactic get_best () { - tactic t { search(init, 4) }; - std::cout << t.to_string(); - return t; - } - - tactic search (Gst &gst, int depth) { - generator gen { gst }; - std::vector tactics = gen.tactics(); - tactic best; best.eval = std::numeric_limits::lowest(); - for (tactic t : tactics) { - performer perf { gst }; - Gst next { perf.apply(t) }; - t.eval = negamax(next, depth, gst.turn); - std::cout << "depth " << depth << " eval " << t.eval << "\n"; - if (t.eval > best.eval) best = t; - } - return best; - } - - float negamax (Gst gst, int depth, int player) { - //for (int i=0; i<3-depth; i++) std::cout << " "; std::cout << depth << "\n"; - if (depth == 0) { - evaluator eval { gst }; - return eval.eval(player); - } - float value = std::numeric_limits::lowest(); - generator gen { gst }; - auto tactics = gen.tactics(); - for (tactic t : tactics) { - performer perf { gst }; - Gst next { perf.apply(t) }; - value = fmax(value, negamax(next, depth-1, -player)); - } - return -value; - } -}; - -} +#ifndef ENGINE_H +#define ENGINE_H + +#include + +#include +#include +#include +#include + +#include "../ground.h" +#include "../gst.h" + +#include "action.h" +#include "tactic.h" +#include "generator.h" +#include "evaluator.h" +#include "performer.h" + +namespace ai { + +class engine { + public: + engine (Gst &gst) : init(gst) {} + Gst &init; + + tactic get_best () { + tactic t { search(init, 4) }; + std::cout << t.to_string(); + return t; + } + + tactic search (Gst &gst, int depth) { + generator gen { gst }; + std::vector tactics = gen.tactics(); + tactic best; best.eval = std::numeric_limits::lowest(); + for (tactic t : tactics) { + performer perf { gst }; + Gst next { perf.apply(t) }; + t.eval = negamax(next, depth, gst.turn); + std::cout << "depth " << depth << " eval " << t.eval << "\n"; + if (t.eval > best.eval) best = t; + } + return best; + } + + float negamax (Gst gst, int depth, int player) { + //for (int i=0; i<3-depth; i++) std::cout << " "; std::cout << depth << "\n"; + if (depth == 0) { + evaluator eval { gst }; + return eval.eval(player); + } + float value = std::numeric_limits::lowest(); + generator gen { gst }; + auto tactics = gen.tactics(); + for (tactic t : tactics) { + performer perf { gst }; + Gst next { perf.apply(t) }; + value = fmax(value, negamax(next, depth-1, -player)); + } + return -value; + } +}; + +} #endif \ No newline at end of file diff --git a/game/ai/evaluator.h b/game/ai/evaluator.h index 75684de..b288076 100644 --- a/game/ai/evaluator.h +++ b/game/ai/evaluator.h @@ -1,30 +1,30 @@ -#ifndef EVALUATOR_H -#define EVALUATOR_H - -#include - -#include -#include - -#include "../ground.h" -#include "../gst.h" - -namespace ai { - -class evaluator { - public: - evaluator (Gst gst) : gst(gst) {} - Gst gst; - - float eval (int player) { - float val = 0; - for (Entity &ent : gst.entities) { - int own = (int)(ent.owner != player)*2-1; - val += own*(ent.info->cost[0] + ent.info->cost[1])*(ent.hp/100.0f); - } - return val; - } -}; - -} +#ifndef EVALUATOR_H +#define EVALUATOR_H + +#include + +#include +#include + +#include "../ground.h" +#include "../gst.h" + +namespace ai { + +class evaluator { + public: + evaluator (Gst gst) : gst(gst) {} + Gst gst; + + float eval (int player) { + float val = 0; + for (Entity &ent : gst.entities) { + int own = (int)(ent.owner != player)*2-1; + val += own*(ent.info->cost[0] + ent.info->cost[1])*(ent.hp/100.0f); + } + return val; + } +}; + +} #endif \ No newline at end of file diff --git a/game/ai/generator.h b/game/ai/generator.h index c6cb288..39bde4f 100644 --- a/game/ai/generator.h +++ b/game/ai/generator.h @@ -1,102 +1,102 @@ -#ifndef GENERATOR_H -#define GENERATOR_H - -#include - -#include -#include - -#include "../ground.h" -#include "../gst.h" -#include "action.h" -#include "tactic.h" -#include "performer.h" - -namespace ai { - -const int maxiter = 100000; - - -class generator { - public: - generator (Gst &gst) : init(gst) {} - Gst &init; - - std::vector valid_actions (Gst &gst) { - Ground &gr = gst.inv->ground; - std::vector acts; - for (Entity &ent : gst.entities) { - if (ent.done) continue; - if (ent.owner != gst.turn) continue; - - std::vector ent_acts; - - std::vector moves = gr.move_area(gst, ent); - for (auto move : moves) { - int mx = move%gr.sizex, my = move/gr.sizex; - action act { actype::move, ent.x, ent.y, mx, my }; - - performer perf { gst }; - Gst next { gst }; - next = perf.act(next, act); - Entity &matk = next.get_at(mx, my); - std::vector atks = gr.attack_targets(next, matk); - for (auto atk : atks) { - int tx = atk%gr.sizex, ty = atk/gr.sizex; - action act_atk { actype::attack, ent.x, ent.y, - mx, my, tx, ty }; - Entity &mdef = next.get_at(tx, ty); - - BattleResult res = gst.battle_res(matk, mdef); - float heur = (mdef.hp-res.def_hp); - heur *= (mdef.info->cost[0] + mdef.info->cost[1]); - act_atk.heuristic = heur; - ent_acts.push_back(act_atk); - } - - int dist = 999999; - int pos = gst.get_nearest_enemy(ent, dist); - float heur = 1.0f/dist; - act.heuristic = heur; - ent_acts.push_back(act); - } - std::sort(ent_acts.begin(), ent_acts.end(), compare_action); - for (int i=0; i<3 && i acts { valid_actions(gst) }; - if (acts.size() == 0) break; - performer perf { gst }; - int j = n % (acts.size()); - n /= acts.size(); - action &act = acts[j]; - gst = perf.act(gst, act); - t.acts.push_back(act); - //std::cout << act.to_string() << std::endl; - } - return t; - } - - std::vector tactics () { - std::vector ts; - for (int i=0; i<3; i++) { - int n = i; - tactic t = valid_tactic(n); - if (n > 0) break; - ts.push_back(t); - } - return ts; - } -}; - -} +#ifndef GENERATOR_H +#define GENERATOR_H + +#include + +#include +#include + +#include "../ground.h" +#include "../gst.h" +#include "action.h" +#include "tactic.h" +#include "performer.h" + +namespace ai { + +const int maxiter = 100000; + + +class generator { + public: + generator (Gst &gst) : init(gst) {} + Gst &init; + + std::vector valid_actions (Gst &gst) { + Ground &gr = gst.inv->ground; + std::vector acts; + for (Entity &ent : gst.entities) { + if (ent.done) continue; + if (ent.owner != gst.turn) continue; + + std::vector ent_acts; + + std::vector moves = gr.move_area(gst, ent); + for (auto move : moves) { + int mx = move%gr.sizex, my = move/gr.sizex; + action act { actype::move, ent.x, ent.y, mx, my }; + + performer perf { gst }; + Gst next { gst }; + next = perf.act(next, act); + Entity &matk = next.get_at(mx, my); + std::vector atks = gr.attack_targets(next, matk); + for (auto atk : atks) { + int tx = atk%gr.sizex, ty = atk/gr.sizex; + action act_atk { actype::attack, ent.x, ent.y, + mx, my, tx, ty }; + Entity &mdef = next.get_at(tx, ty); + + BattleResult res = gst.battle_res(matk, mdef); + float heur = (mdef.hp-res.def_hp); + heur *= (mdef.info->cost[0] + mdef.info->cost[1]); + act_atk.heuristic = heur; + ent_acts.push_back(act_atk); + } + + int dist = 999999; + int pos = gst.get_nearest_enemy(ent, dist); + float heur = 1.0f/dist; + act.heuristic = heur; + ent_acts.push_back(act); + } + std::sort(ent_acts.begin(), ent_acts.end(), compare_action); + for (int i=0; i<3 && i acts { valid_actions(gst) }; + if (acts.size() == 0) break; + performer perf { gst }; + int j = n % (acts.size()); + n /= acts.size(); + action &act = acts[j]; + gst = perf.act(gst, act); + t.acts.push_back(act); + //std::cout << act.to_string() << std::endl; + } + return t; + } + + std::vector tactics () { + std::vector ts; + for (int i=0; i<3; i++) { + int n = i; + tactic t = valid_tactic(n); + if (n > 0) break; + ts.push_back(t); + } + return ts; + } +}; + +} #endif \ No newline at end of file diff --git a/game/ai/performer.h b/game/ai/performer.h index 472773b..17be028 100644 --- a/game/ai/performer.h +++ b/game/ai/performer.h @@ -1,49 +1,49 @@ -#ifndef PERFORMER_H -#define PERFORMER_H - -#include - -#include -#include - -#include "../ground.h" -#include "../gst.h" -#include "action.h" -#include "tactic.h" - -namespace ai { - -class performer { - public: - performer (Gst &gst) : init(gst) {} - Gst &init; - - Gst apply (tactic t) { - Gst next { init }; - for (action a : t.acts) { - next = act(next, a); - } - next.end_day(); - return next; - } - - Gst& act (Gst &gst, action a) { - if (a.type == actype::move) { - Entity &ent = gst.get_at(a.x, a.y); - ent.x = a.mx; ent.y = a.my; - ent.moved = 1; ent.done = true; - } - if (a.type == actype::attack) { - Entity &ent = gst.get_at(a.x, a.y); - ent.x = a.mx; ent.y = a.my; - ent.moved = 1; - Entity &def = gst.get_at(a.tx, a.ty); - gst.battle(ent, def); - ent.done = true; - } - return gst; - } -}; - -} +#ifndef PERFORMER_H +#define PERFORMER_H + +#include + +#include +#include + +#include "../ground.h" +#include "../gst.h" +#include "action.h" +#include "tactic.h" + +namespace ai { + +class performer { + public: + performer (Gst &gst) : init(gst) {} + Gst &init; + + Gst apply (tactic t) { + Gst next { init }; + for (action a : t.acts) { + next = act(next, a); + } + next.end_day(); + return next; + } + + Gst& act (Gst &gst, action a) { + if (a.type == actype::move) { + Entity &ent = gst.get_at(a.x, a.y); + ent.x = a.mx; ent.y = a.my; + ent.moved = 1; ent.done = true; + } + if (a.type == actype::attack) { + Entity &ent = gst.get_at(a.x, a.y); + ent.x = a.mx; ent.y = a.my; + ent.moved = 1; + Entity &def = gst.get_at(a.tx, a.ty); + gst.battle(ent, def); + ent.done = true; + } + return gst; + } +}; + +} #endif \ No newline at end of file diff --git a/game/ai/tactic.h b/game/ai/tactic.h index 7eefef9..ec2e382 100644 --- a/game/ai/tactic.h +++ b/game/ai/tactic.h @@ -1,32 +1,32 @@ -#ifndef TACTIC_H -#define TACTIC_H - -#include -#include - -#include "action.h" - -namespace ai { - -class tactic { - public: - tactic () { } - - std::vector acts; - float eval = 0; - - // copy constructor - tactic (const tactic& rhs) { acts = rhs.acts; eval = rhs.eval; } - tactic& operator=(const tactic& rhs) { acts = rhs.acts; eval = rhs.eval; } - - std::string to_string () { - std::string str = "tactic eval= " + std::to_string(eval) + "\n"; - for (action act : acts) { - str += act.to_string() + "\n"; - } - return str; - } -}; - -} +#ifndef TACTIC_H +#define TACTIC_H + +#include +#include + +#include "action.h" + +namespace ai { + +class tactic { + public: + tactic () { } + + std::vector acts; + float eval = 0; + + // copy constructor + tactic (const tactic& rhs) { acts = rhs.acts; eval = rhs.eval; } + tactic& operator=(const tactic& rhs) { acts = rhs.acts; eval = rhs.eval; } + + std::string to_string () { + std::string str = "tactic eval= " + std::to_string(eval) + "\n"; + for (action act : acts) { + str += act.to_string() + "\n"; + } + return str; + } +}; + +} #endif \ No newline at end of file diff --git a/game/constants.h b/game/constants.h index 2ba343a..ef6a477 100644 --- a/game/constants.h +++ b/game/constants.h @@ -1,17 +1,17 @@ -#ifndef CONSTANTS_H -#define CONSTANTS_H - -namespace constants { - const int col_gud_r = 40; - const int col_gud_g = 120; - const int col_gud_b = 0; - const int col_bad_r = 255; - const int col_bad_g = 120; - const int col_bad_b = 0; - - const float menu_attack_margin_amt = 60; - const float menu_attack_margin_arrow = 80; - const float menu_attack_margin_mod = 93; -} - +#ifndef CONSTANTS_H +#define CONSTANTS_H + +namespace constants { + const int col_gud_r = 40; + const int col_gud_g = 120; + const int col_gud_b = 0; + const int col_bad_r = 255; + const int col_bad_g = 120; + const int col_bad_b = 0; + + const float menu_attack_margin_amt = 60; + const float menu_attack_margin_arrow = 80; + const float menu_attack_margin_mod = 93; +} + #endif \ No newline at end of file diff --git a/game/entity.cpp b/game/entity.cpp index f127150..8539045 100644 --- a/game/entity.cpp +++ b/game/entity.cpp @@ -1,11 +1,11 @@ -#include "entity.h" -#include - - -int EntityClass::from_string (std::string str) { - if (str == "inf") return EntityInfo::Class::inf; - if (str == "cav") return EntityInfo::Class::cav; - if (str == "ran") return EntityInfo::Class::ran; - if (str == "sie") return EntityInfo::Class::sie; - if (str == "bld") return EntityInfo::Class::bld; +#include "entity.h" +#include + + +int EntityClass::from_string (std::string str) { + if (str == "inf") return EntityInfo::Class::inf; + if (str == "cav") return EntityInfo::Class::cav; + if (str == "ran") return EntityInfo::Class::ran; + if (str == "sie") return EntityInfo::Class::sie; + if (str == "bld") return EntityInfo::Class::bld; } \ No newline at end of file diff --git a/game/entity.h b/game/entity.h index a15ebdb..f9f6781 100644 --- a/game/entity.h +++ b/game/entity.h @@ -1,82 +1,82 @@ -#ifndef ENTITIES_H -#define ENTITIES_H - -#include -#include - -#include - -#include "../umath/vec2.h" - - -class EntityInfo { - public: - EntityInfo() { spritebounds = vec2 { 16*6, 16 }; } - - enum Class { inf, cav, ran, sie, bld }; - Class ent_class; - - std::string name; - - int id; - int level; - - float hp; - float attack; - float defence; - int range; - float sight; - int move; - int unit; - std::vector abilities; - std::vector build; - std::vector train_id; - std::vector train_class; - - std::vector prod { 0, 0 }; - std::vector cost { 0, 0 }; - - std::vector adjacent; - std::vector diagonal; - float defence_bonus { 0 }; - int upgrade { -1 }; - - vec2 spritebounds; -}; - -namespace EntityClass { - int from_string(std::string str); -} - -class Entity { - public: - Entity (int x, int y, EntityInfo *info, int owner) - : x(x), y(y), info(info), owner(owner) { moved = 0; hp = 100; } - - // copy constructor - Entity (const Entity& rhs) { - building = rhs.building; hp = rhs.hp; x = rhs.x; y = rhs.y; - done = rhs.done; moved = rhs.moved; info = rhs.info; - fights = rhs.fights; owner = rhs.owner; - } - Entity& operator=(const Entity& rhs) { - building = rhs.building; hp = rhs.hp; x = rhs.x; y = rhs.y; - done = rhs.done; moved = rhs.moved; info = rhs.info; - fights = rhs.fights; owner = rhs.owner; - }; - - bool operator==(Entity oth) { - return x == oth.x && y == oth.y && info->unit == oth.info->unit; - } - - int building { 0 }; - float hp; - int x, y; - bool done = false; - int moved; - EntityInfo *info; - int fights { 0 }; - int owner; -}; - +#ifndef ENTITIES_H +#define ENTITIES_H + +#include +#include + +#include + +#include "../umath/vec2.h" + + +class EntityInfo { + public: + EntityInfo() { spritebounds = vec2 { 16*6, 16 }; } + + enum Class { inf, cav, ran, sie, bld }; + Class ent_class; + + std::string name; + + int id; + int level; + + float hp; + float attack; + float defence; + int range; + float sight; + int move; + int unit; + std::vector abilities; + std::vector build; + std::vector train_id; + std::vector train_class; + + std::vector prod { 0, 0 }; + std::vector cost { 0, 0 }; + + std::vector adjacent; + std::vector diagonal; + float defence_bonus { 0 }; + int upgrade { -1 }; + + vec2 spritebounds; +}; + +namespace EntityClass { + int from_string(std::string str); +} + +class Entity { + public: + Entity (int x, int y, EntityInfo *info, int owner) + : x(x), y(y), info(info), owner(owner) { moved = 0; hp = 100; } + + // copy constructor + Entity (const Entity& rhs) { + building = rhs.building; hp = rhs.hp; x = rhs.x; y = rhs.y; + done = rhs.done; moved = rhs.moved; info = rhs.info; + fights = rhs.fights; owner = rhs.owner; + } + Entity& operator=(const Entity& rhs) { + building = rhs.building; hp = rhs.hp; x = rhs.x; y = rhs.y; + done = rhs.done; moved = rhs.moved; info = rhs.info; + fights = rhs.fights; owner = rhs.owner; + }; + + bool operator==(Entity oth) { + return x == oth.x && y == oth.y && info->unit == oth.info->unit; + } + + int building { 0 }; + float hp; + int x, y; + bool done = false; + int moved; + EntityInfo *info; + int fights { 0 }; + int owner; +}; + #endif \ No newline at end of file diff --git a/game/ground.cpp b/game/ground.cpp index cd67751..610b096 100644 --- a/game/ground.cpp +++ b/game/ground.cpp @@ -1,132 +1,132 @@ -#include "ground.h" -#include "gst.h" - -#include -#include - -void Ground::build (int sx, int sy) { - sizex = sx; sizey = sy; - tiles = new int[sx*sy]; - for (int i=0; i Ground::star (int pos) { - std::vector fs; - int x = pos % sizex, y = pos / sizex; - if (x-1 >= 0) { fs.push_back(at(x-1, y)); } - if (x+1 < sizex) { fs.push_back(at(x+1, y)); } - if (y-1 >= 0) { fs.push_back(at(x, y-1)); } - if (y+1 < sizey) { fs.push_back(at(x, y+1)); } - return fs; -} - -class step { public: - step(int pos, int m) : pos(pos), m(m) {}; - bool operator==(step oth) { return pos==oth.pos && m==oth.m; } - bool operator==(int p) { return pos==p; } - int pos, m; -}; - -std::vector Ground::move_area (Gst &gst, Entity &ent) { - Player &player = gst.players[ent.owner]; - int move_num = ent.info->move; - move_num += player.tech_lookup.id(ent.info->id).move; - - std::vector moves; - std::vector visited { at(ent.x, ent.y) }; - std::vector frontier { step { at(ent.x, ent.y), move_num } }; - - int maxcost = 99; - if (gst.inv->info_has_ability(ent.info, "Scout")) maxcost = 2; - - int iter=0; - for (; iter<10000; iter++) { - - if (frontier.size() == 0) break; - step maxf {-1, -1}; - for (step t : frontier) { - if (t.m > maxf.m) { - maxf.pos = t.pos; - maxf.m = t.m; - } - } - frontier.erase(std::remove(frontier.begin(), frontier.end(), maxf), - frontier.end()); - auto forward_star = star(maxf.pos); - for (int t : forward_star) { - if (!(std::find(visited.begin(), visited.end(), t) != visited.end()) - && !(std::find(frontier.begin(), frontier.end(), t) != frontier.end())) { - int movecost = gst.inv->tiles[tiles[t]].move_cost; - if (movecost > maxcost) movecost = maxcost; - int walkedm = maxf.m - movecost; - bool obs_enemy = false, obs_friend = false; - for (Entity &e : gst.entities) { - if (at(e.x, e.y) == t) { - if (e.owner != ent.owner) obs_enemy = true; - else obs_friend = true; - break; - } - } - if (walkedm >= 0 && !obs_enemy) { - frontier.emplace_back(t, walkedm); - if (!obs_friend) { - moves.push_back(t); - } - } - } - } - visited.push_back(maxf.pos); - } - - - return moves; -} - -std::vector Ground::attack_targets (Gst &gst, Entity &ent) { - std::vector attacks; - int range = gst.get_range(ent); - bool builds = !gst.inv->info_has_ability(ent.info, "Units Only"); - bool units = !gst.inv->info_has_ability(ent.info, "Buildings Only"); - for (Entity &e : gst.entities) { - if (!units && e.info->unit == 1) continue; - if (!builds && e.info->unit == 0) continue; - int dist = abs(e.x-ent.x)+abs(e.y-ent.y); - if (dist > 0 && e.owner != ent.owner && dist <= range) { - attacks.push_back(at(e.x, e.y)); - } - } - return attacks; -} - -std::vector Ground::heal_targets (Gst &gst, Entity &ent) { - std::vector heals; - int range = gst.get_range(ent); - for (Entity &e : gst.entities) { - if (e.info->unit == 0) continue; - if (e.info->ent_class == EntityInfo::Class::sie) continue; - int dist = abs(e.x-ent.x)+abs(e.y-ent.y); - if (dist > 0 && e.owner == ent.owner && dist <= range) { - heals.push_back(at(e.x, e.y)); - } - } - return heals; -} - -std::vector Ground::convert_targets (Gst &gst, Entity &ent) { - std::vector converts; - int range = gst.get_range(ent); - for (Entity &e : gst.entities) { - if (e.info->unit == 0) continue; - int dist = abs(e.x-ent.x)+abs(e.y-ent.y); - if (dist > 0 && e.owner != ent.owner && dist <= range) { - converts.push_back(at(e.x, e.y)); - } - } - return converts; +#include "ground.h" +#include "gst.h" + +#include +#include + +void Ground::build (int sx, int sy) { + sizex = sx; sizey = sy; + tiles = new int[sx*sy]; + for (int i=0; i Ground::star (int pos) { + std::vector fs; + int x = pos % sizex, y = pos / sizex; + if (x-1 >= 0) { fs.push_back(at(x-1, y)); } + if (x+1 < sizex) { fs.push_back(at(x+1, y)); } + if (y-1 >= 0) { fs.push_back(at(x, y-1)); } + if (y+1 < sizey) { fs.push_back(at(x, y+1)); } + return fs; +} + +class step { public: + step(int pos, int m) : pos(pos), m(m) {}; + bool operator==(step oth) { return pos==oth.pos && m==oth.m; } + bool operator==(int p) { return pos==p; } + int pos, m; +}; + +std::vector Ground::move_area (Gst &gst, Entity &ent) { + Player &player = gst.players[ent.owner]; + int move_num = ent.info->move; + move_num += player.tech_lookup.id(ent.info->id).move; + + std::vector moves; + std::vector visited { at(ent.x, ent.y) }; + std::vector frontier { step { at(ent.x, ent.y), move_num } }; + + int maxcost = 99; + if (gst.inv->info_has_ability(ent.info, "Scout")) maxcost = 2; + + int iter=0; + for (; iter<10000; iter++) { + + if (frontier.size() == 0) break; + step maxf {-1, -1}; + for (step t : frontier) { + if (t.m > maxf.m) { + maxf.pos = t.pos; + maxf.m = t.m; + } + } + frontier.erase(std::remove(frontier.begin(), frontier.end(), maxf), + frontier.end()); + auto forward_star = star(maxf.pos); + for (int t : forward_star) { + if (!(std::find(visited.begin(), visited.end(), t) != visited.end()) + && !(std::find(frontier.begin(), frontier.end(), t) != frontier.end())) { + int movecost = gst.inv->tiles[tiles[t]].move_cost; + if (movecost > maxcost) movecost = maxcost; + int walkedm = maxf.m - movecost; + bool obs_enemy = false, obs_friend = false; + for (Entity &e : gst.entities) { + if (at(e.x, e.y) == t) { + if (e.owner != ent.owner) obs_enemy = true; + else obs_friend = true; + break; + } + } + if (walkedm >= 0 && !obs_enemy) { + frontier.emplace_back(t, walkedm); + if (!obs_friend) { + moves.push_back(t); + } + } + } + } + visited.push_back(maxf.pos); + } + + + return moves; +} + +std::vector Ground::attack_targets (Gst &gst, Entity &ent) { + std::vector attacks; + int range = gst.get_range(ent); + bool builds = !gst.inv->info_has_ability(ent.info, "Units Only"); + bool units = !gst.inv->info_has_ability(ent.info, "Buildings Only"); + for (Entity &e : gst.entities) { + if (!units && e.info->unit == 1) continue; + if (!builds && e.info->unit == 0) continue; + int dist = abs(e.x-ent.x)+abs(e.y-ent.y); + if (dist > 0 && e.owner != ent.owner && dist <= range) { + attacks.push_back(at(e.x, e.y)); + } + } + return attacks; +} + +std::vector Ground::heal_targets (Gst &gst, Entity &ent) { + std::vector heals; + int range = gst.get_range(ent); + for (Entity &e : gst.entities) { + if (e.info->unit == 0) continue; + if (e.info->ent_class == EntityInfo::Class::sie) continue; + int dist = abs(e.x-ent.x)+abs(e.y-ent.y); + if (dist > 0 && e.owner == ent.owner && dist <= range) { + heals.push_back(at(e.x, e.y)); + } + } + return heals; +} + +std::vector Ground::convert_targets (Gst &gst, Entity &ent) { + std::vector converts; + int range = gst.get_range(ent); + for (Entity &e : gst.entities) { + if (e.info->unit == 0) continue; + int dist = abs(e.x-ent.x)+abs(e.y-ent.y); + if (dist > 0 && e.owner != ent.owner && dist <= range) { + converts.push_back(at(e.x, e.y)); + } + } + return converts; } \ No newline at end of file diff --git a/game/ground.h b/game/ground.h index d1a4f34..fcded29 100644 --- a/game/ground.h +++ b/game/ground.h @@ -1,37 +1,37 @@ -#ifndef GROUND_H -#define GROUND_H - -#include "entity.h" - -class Gst; - -class Resource { - public: - Resource (int pos, int kind) : pos(pos), kind(kind) {} - - enum Type { gold, food }; - - int pos, kind; -}; - -class Ground { - public: - Ground () {} - - int *tiles; - - std::vector resources; - - void build (int sx, int sy); - - int sizex; - int sizey; - int at (int x, int y); - std::vector star (int pos); - std::vector move_area (Gst &gst, Entity &ent); - std::vector attack_targets (Gst &gst, Entity &ent); - std::vector heal_targets (Gst &gst, Entity &ent); - std::vector convert_targets (Gst &gst, Entity &ent); -}; - +#ifndef GROUND_H +#define GROUND_H + +#include "entity.h" + +class Gst; + +class Resource { + public: + Resource (int pos, int kind) : pos(pos), kind(kind) {} + + enum Type { gold, food }; + + int pos, kind; +}; + +class Ground { + public: + Ground () {} + + int *tiles; + + std::vector resources; + + void build (int sx, int sy); + + int sizex; + int sizey; + int at (int x, int y); + std::vector star (int pos); + std::vector move_area (Gst &gst, Entity &ent); + std::vector attack_targets (Gst &gst, Entity &ent); + std::vector heal_targets (Gst &gst, Entity &ent); + std::vector convert_targets (Gst &gst, Entity &ent); +}; + #endif \ No newline at end of file diff --git a/game/gst.cpp b/game/gst.cpp index 334844a..35f2b44 100644 --- a/game/gst.cpp +++ b/game/gst.cpp @@ -1,605 +1,605 @@ -#include "gst.h" - -#include -#include - - -Tech* Inv::get_tech (int id) { - for (auto &tech : techs) { - if (id == tech.id) return &tech; - } -} - -EntityInfo* Inv::get_info (std::string name) { - for (EntityInfo &info : infos) { - if (name == info.name) return &info; - } -} - -EntityInfo* Inv::get_info (int id) { - for (EntityInfo &info : infos) { - if (id == info.id) return &info; - } -} - -bool Inv::info_has_ability (EntityInfo* info, std::string name) { - for (int ab : info->abilities) { - if (abilities[ab].name == name) return true; - } - return false; -} - - - -Player& Gst::get_player (int id) { - for (auto &player : players) { - if (id == player.id) return player; - } -} - -Entity& Gst::get_at (int x, int y) { - for (Entity &e : entities) { - if (e.x ==x && e.y == y) return e; - } -} - -std::vector Gst::get_cost (EntityInfo *info, Player &player) { - std::vector cost = info->cost; - - std::cout << "cost : " << player.tech_lookup.id(info->id).cost[0] << " " << player.tech_lookup.id(info->id).cost[1] << "\n"; - for (int i=0; icost.size(); i++) { - cost[i] *= 1+player.tech_lookup.id(info->id).cost[i]; - cost[i] += player.tech_lookup.id(info->id).cost_abs[i]; - } - return cost; -} - -float Gst::get_trade_rate (Player &player) { - float rate = 250; - rate -= player.tech_lookup.id(0).trade * 25; - return rate; -} - -float Gst::get_type_bonus (Entity &atk, Entity &def) { - float b = 0; - switch(atk.info->ent_class) { - case EntityInfo::Class::inf: - if (def.info->ent_class == EntityInfo::Class::bld) b += 1.0f/3.0f; - if (def.info->ent_class == EntityInfo::Class::sie) b += 1.0f/3.0f; - break; - - case EntityInfo::Class::cav: - if (def.info->ent_class == EntityInfo::Class::bld) b += -0.5; - if (def.info->ent_class == EntityInfo::Class::inf) b += 1.0f/3.0f; - if (def.info->ent_class == EntityInfo::Class::ran) b += 1.0f/3.0f; - break; - - case EntityInfo::Class::ran: - if (def.info->ent_class == EntityInfo::Class::bld) b += -0.5f; - break; - - case EntityInfo::Class::sie: - if (def.info->ent_class == EntityInfo::Class::bld) b += +0.5f; - break; - } - return b; -} - -int Gst::get_vet_level (Entity &ent) { return std::min(3, ent.fights/3); } - -std::vector Gst::get_bonuses (Entity &atk, Entity &def) { - auto &tiles = inv->tiles; - auto &ground = inv->ground; - std::vector bs; - if (tiles[ground.tiles[ground.at(atk.x, atk.y)]].attack_bonus != 0) { - - bs.emplace_back( - tiles[ground.tiles[ground.at(atk.x, atk.y)]].attack_bonus, - Bonus::Id::ground, true); - } - if (tiles[ground.tiles[ground.at(def.x, def.y)]].defence_bonus != 0) { - bs.emplace_back( - tiles[ground.tiles[ground.at(def.x, def.y)]].defence_bonus, - Bonus::Id::ground, false); - } - - if (check_obstructed(def)) { - for (Entity &e : entities) { - if (&def != &e && e.x == def.x && def.y == e.y) { - bs.emplace_back(e.info->defence_bonus, - Bonus::Id::on_bld, false); - } - } - } - - if (get_type_bonus(atk, def) != 0) { - bs.emplace_back(get_type_bonus(atk, def), Bonus::Id::type, true); - } - /* only attack bonuses - if (get_type_bonus(def, atk) != 0) { - bs.emplace_back(get_type_bonus(def, atk), Bonus::Id::type, false); - }*/ - - if (inv->info_has_ability(atk.info, "Causes Fear")) - bs.emplace_back(-1.0f/3, Bonus::Id::ability, false); - if (inv->info_has_ability(def.info, "Causes Fear")) - bs.emplace_back(-1.0f/3, Bonus::Id::ability, true); - - if (inv->info_has_ability(atk.info, "Anti-Cavalry")) - bs.emplace_back(4.0f/3, Bonus::Id::ability, true); - if (inv->info_has_ability(def.info, "Anti-Cavalry")) - bs.emplace_back(4.0f/3, Bonus::Id::ability, false); - - if (inv->info_has_ability(atk.info, "Desert Charge") - && !inv->info_has_ability(def.info, "Desert Charge") - && tiles[ground.tiles[ground.at(def.x, def.y)]].name == "Desert") - bs.emplace_back(1.0f/3, Bonus::Id::ability, true); - - if (inv->info_has_ability(atk.info, "Plains Charge") - && !inv->info_has_ability(def.info, "Plains Charge") - && tiles[ground.tiles[ground.at(def.x, def.y)]].name == "Plains") - bs.emplace_back(1.0f/3, Bonus::Id::ability, true); - - if (inv->info_has_ability(atk.info, "Woodsman") - && !inv->info_has_ability(def.info, "Woodsman") - && tiles[ground.tiles[ground.at(def.x, def.y)]].name == "Forest") - bs.emplace_back(1.0f/3, Bonus::Id::ability, true); - - if (inv->info_has_ability(atk.info, "Volley") && atk.hp >= 50) - bs.emplace_back(1.0f/3, Bonus::Id::ability, true); - - if (inv->info_has_ability(atk.info, "Frenzy")) - bs.emplace_back(1/atk.hp, Bonus::Id::ability, true); - - Player &player_atk = players[atk.owner]; - Player &player_def = players[def.owner]; - float tech_attack = player_atk.tech_lookup.id(atk.info->id).attack; - if (tech_attack != 0) - bs.emplace_back(tech_attack, Bonus::Id::tech, true); - float tech_defence = player_def.tech_lookup.id(def.info->id).defence; - if (tech_defence != 0) - bs.emplace_back(tech_defence, Bonus::Id::tech, false); - - float atk_vet = get_vet_level(atk) * 0.15f; - if (atk_vet > 0) - bs.emplace_back(atk_vet, Bonus::Id::veteran, true); - float def_vet = get_vet_level(def) * 0.15f; - if (def_vet > 0) - bs.emplace_back(def_vet, Bonus::Id::veteran, false); - - return bs; -} - -float Gst::get_damage (Entity &atk, Entity &def, float atk_hp) { - float atkmul = 1; - float defmul = 1; - - auto bonuses = get_bonuses(atk, def); - for (auto bonus : bonuses) { - if (bonus.atk) { atkmul += bonus.amt; } - else { defmul += bonus.amt; } - } - - float dam = (atk.info->attack * atk_hp * atkmul) - / (2.0f*def.info->defence * defmul); - - return dam; -} - -float Gst::get_damage (Entity &atk, Entity &def) { - return get_damage(atk, def, atk.hp); -} - -bool Gst::get_first_strike (Entity &atk, Entity &def) { - bool fs { false }; - fs = inv->info_has_ability(atk.info, "First Strike"); - return fs; -} - -float clamp_hp (float hp) { - if (hp > 100) hp = 100; - if (hp < 0) hp = 0; - return hp; -} - -BattleResult Gst::battle_res (Entity &atk, Entity &def) { - BattleResult result { atk.hp, def.hp }; - bool first_strike_atk = inv->info_has_ability(atk.info, "First Strike"); - bool first_strike_def = inv->info_has_ability(def.info, "First Strike"); - bool skirmish_atk = inv->info_has_ability(atk.info, "Skirmish"); - bool skirmish_def = inv->info_has_ability(def.info, "Skirmish"); - bool anticav_atk = inv->info_has_ability(atk.info, "Anti-Cavalry"); - bool anticav_def = inv->info_has_ability(def.info, "Anti-Cavalry"); - first_strike_atk = first_strike_atk - || (skirmish_atk && def.info->range == 1); - first_strike_def = first_strike_def - || (skirmish_def && atk.info->range == 1); - first_strike_atk = first_strike_atk - || (anticav_atk && def.info->ent_class == EntityInfo::Class::cav); - first_strike_def = first_strike_def - || (anticav_def && atk.info->ent_class == EntityInfo::Class::cav); - - int dist = abs(atk.x-def.x) + abs(atk.y-def.y); - bool def_inrange = (dist <= get_range(def)) ? true : false; - - bool swap = false; - if (first_strike_def && !first_strike_atk) swap = true; - if (swap) { - if (def_inrange) { - result.atk_hp = clamp_hp( - result.atk_hp - get_damage(def, atk, result.def_hp)); - } - if (!inv->info_has_ability(atk.info, "No Counter")) - if (result.atk_hp > 0) - result.def_hp = clamp_hp( - result.def_hp - get_damage(atk, def, result.atk_hp)); - } else { - result.def_hp = clamp_hp( - result.def_hp - get_damage(atk, def, result.atk_hp)); - if (!inv->info_has_ability(def.info, "No Counter") && def_inrange) - if (result.def_hp > 0) - result.atk_hp = clamp_hp( - result.atk_hp - get_damage(def, atk, result.def_hp)); - } - - if (inv->info_has_ability(atk.info, "Rapid Fire")) - if (result.def_hp > 0) - result.def_hp = clamp_hp( - result.def_hp - get_damage(atk, def, result.def_hp)); - - if (inv->info_has_ability(def.info, "Rapid Fire") && def_inrange) - if (result.atk_hp > 0) - result.atk_hp = clamp_hp( - result.atk_hp - get_damage(def, atk, result.def_hp)); - - if (result.atk_hp > 0 && inv->info_has_ability(atk.info, "Zeal")) - result.atk_hp = clamp_hp(result.atk_hp + 20); - if (result.def_hp > 0 && inv->info_has_ability(def.info, "Zeal")) - result.def_hp = clamp_hp(result.def_hp + 20); - - return result; -} - -void Gst::battle (Entity &atk, Entity &def) { - /*std::cout << "! attack " << atk.info->name << "(hp:" << atk.hp << "), " - << def.info->name << "(hp:" << def.hp << ") \n";*/ - - auto result = battle_res(atk, def); - atk.hp = result.atk_hp; - def.hp = result.def_hp; - - if (atk.info->unit == 1) atk.fights += 1; - if (def.info->unit == 1) def.fights += 1; - - /*std::cout << "! result " << atk.info->name << "(hp:" << atk.hp << "), " - << def.info->name << "(hp:" << def.hp << ") \n";*/ - clear_dead(); -} - -void Gst::clear_dead() { - auto i = std::begin(entities); - while (i != std::end(entities)) { - if (i->hp <= 0) { - entities.erase(i); - } - else i++; - } -} - -int Gst::get_range (Entity &ent) { - int range = ent.info->range; - if (range > 1) { - auto &tiles = inv->tiles; - auto &ground = inv->ground; - range += tiles[ground.tiles[ground.at(ent.x, ent.y)]].range_bonus; - } - if (range < 1) range = 1; - return range; -} - - -void Gst::heal (Entity &atk, Entity &def) { - Player &player = get_player(atk.owner); - float amt = 20; - if (atk.info->level == 3) { amt += 10; } // improved heal - if (player.has_tech(52)) { amt += 10; } // tech illumination - def.hp = clamp_hp(def.hp + amt); -} - -void Gst::convert (Entity &atk, Entity &def) { - Player &player = get_player(atk.owner); - float amt = 0.20f; - if (player.has_tech(53)) { amt += 0.10f; } // tech faith - // caution, randomness - std::uniform_real_distribution odds(0, 1); - float value = odds(inv->engine); - std::cout << value << " / " << amt << " odds\n"; - if (value < amt) { - def.owner = atk.owner; - } -} - - -int Gst::get_nearest_enemy (Entity &ent, int &mindist) { - auto &ground = inv->ground; - int pos = -1; mindist = 9999999; - for (Entity &oth : entities) { - if (oth.owner == ent.owner) continue; - int dist = abs(oth.x-ent.x) + abs(oth.y-ent.y); - if (dist < mindist) { - mindist = dist; - pos = ground.at(oth.x, oth.y); - } - } -} - - -std::vector Gst::get_possible_trains (Entity &ent) { - Player &player = get_player(ent.owner); - auto &cls = ent.info->train_class; - std::vector trains; - if (ent.info->id == 107) { // market special case - std::vector candidates; - for (EntityInfo &info : inv->infos) { - if (info.id == 0) continue; // villager only in train_id - if (info.level > player.level) continue; - if (info.level < player.level && info.upgrade != -1) continue; - if (std::find(cls.begin(), cls.end(), info.ent_class) != cls.end()) - { - candidates.push_back(info.id); - } - } - std::shuffle(candidates.begin(), candidates.end(), inv->engine); - // pick 3 cands at random - for (int i=0; i<3; i++) trains.push_back(candidates[i]); - return trains; - } - - for (int id : ent.info->train_id) { - auto info = inv->get_info(id); - if (info->level > player.level) continue; - if (info->level < player.level && info->upgrade != -1) continue; - trains.push_back(id); - } - /* - // get all train class ids, highest upgrade level - for (EntityInfo &info : infos) { - if (info.id == 0) continue; // villager only in train_id - if (info.level > player.level) continue; - if (info.level < player.level && info.upgrade != -1) continue; - if (std::find(cls.begin(), cls.end(), info.ent_class) != cls.end()) { - if (std::find(trains.begin(), trains.end(), info.id) - != trains.end()) - { - trains.push_back(info.id); - } - } - }*/ - return trains; -} - -std::vector Gst::get_possible_builds (Entity &ent) { - std::vector builds; - for (int id : ent.info->build) { - if (check_req_build(ent, inv->get_info(id))) { - builds.push_back(id); - } - } - return builds; -} - - -bool Gst::check_req_build(Entity &ent, EntityInfo *info) { - auto &ground = inv->ground; - Player &player = players[ent.owner]; - if (player.level < info->level) return false; - for (int id : info->adjacent) { - bool adj = false; - for (Entity &e : entities) { - if (e.info->id == id && ent.owner == e.owner) { - int dist = abs(e.x-ent.x) + abs(e.y-ent.y); - if (dist == 1) { - adj = true; - } - } - } - if (!adj) return false; - } - for (int id : info->diagonal) { - bool diag = false; - for (Entity &e : entities) { - if (e.info->id == id && ent.owner == e.owner) { - int dx = abs(e.x-ent.x), dy = abs(e.y-ent.y); - int dist = dx + dy; - if (dx == 1 && dy == 1) { - diag = true; - } - } - } - if (!diag) return false; - } - if (info->id == 100) { - for (Resource &r : ground.resources) { - if (r.pos == ground.at(ent.x, ent.y)) { - return false; - } - } - int mindist = 9999; - for (Entity &e : entities) { - if (e.info->id == 100 && ent.owner == e.owner) { - int dist = abs(e.x-ent.x) + abs(ent.y-e.y); - if (dist < mindist) { - mindist = dist; - } - } - } - if (mindist < 5) { - return false; - } - return true; - } - if (info->id == 101) { - for (Resource &r : ground.resources) { - if (r.kind == Resource::Type::food - && r.pos == ground.at(ent.x, ent.y)) { - return true; - } - } - return false; - } - if (info->id == 102) { - for (Resource &r : ground.resources) { - if (r.kind == Resource::Type::gold - && r.pos == ground.at(ent.x, ent.y)) { - return true; - } - } - return false; - } - return true; -} - - -bool Gst::check_req_tech (Tech *tech, Player &player) { - if (player.leveling_up == 1) return false; - if (tech->level > player.level) { - return false; - } - if (tech->cost[0] > player.res[0] - || tech->cost[1] > player.res[1] ) - { - return false; - } - if (player.has_tech(tech->id)) { - return false; - } - bool req_id = false; - for (auto &ent : entities) { - if (ent.owner == turn // WARNING: turn is not player.id - && ent.info->id == tech->req_id - && ent.building == 0) - { - req_id = true; - break; - } - } - if (!req_id) { - return false; - } - return true; -} - -bool Gst::check_req_level (Player &player) { - if (player.leveling_up == 1) return false; - - for (float v : player.res) { - if (v <= (player.level+1)*500) return false; - } - - std::map lv_techs; - for (int id : player.techs) lv_techs[inv->get_tech(id)->level] ++; - if (player.level == 0) { - if (lv_techs[0] >= 3) return true; - } - if (player.level == 1) { - if (lv_techs[1] >= 7) return true; - } - if (player.level == 2) { - if (lv_techs[2] >= 11) return true; - } - return false; -} - -bool Gst::check_obstructed (Entity &ent) { - for (Entity &e : entities) { - if (&ent != &e && e.x == ent.x && ent.y == e.y) return true; - } - return false; -} - -void Gst::end_day () { - turn++; - if (turn >= players.size()) { - turn = 0; - day++; - } - Player &player = players[turn]; - if (player.leveling_up != -1) { - level_upgrade(player); - player.level ++; - player.leveling_up = -1; - } - for (Entity &e : entities) { - e.done = false; - e.moved = 0; - if (get_player(e.owner) == player) { - for (int i=0; iprod[i] * - (1+player.tech_lookup.id(e.info->id).prod[i]); - } - if (e.building < 0) { - e.building++; - if (e.building == 0) { - e.hp = clamp_hp(e.hp + 50); - } - } - if (e.info->unit == 1 && check_obstructed(e)) { - e.hp = clamp_hp(e.hp + 20); - } - } - } - if (player.researching != -1) { - player.techs.push_back(player.researching); - update_tech_lookup(player); - player.researching = -1; - } -} - -void Gst::level_upgrade (Player &player) { - for (Entity &e : entities) { - if (get_player(e.owner) == player) { - if (e.info->upgrade != -1 && e.info->level == player.level) { - e.info = inv->get_info(e.info->upgrade); - } - } - } -} - - -void Gst::update_tech_lookup (Player &player) { - player.tech_lookup.map_id.clear(); - for (int i : player.techs) { - Tech *tech = inv->get_tech(i); - std::vector ids { }; - bool noaff = true; - if (tech->bonus.aff_id.size() > 0) { - ids = tech->bonus.aff_id; - noaff = false; - } else { - if (tech->bonus.aff_level != -1) { - for (EntityInfo info : inv->infos) { - if (info.level == tech->bonus.aff_level) { - ids.push_back(info.id); - } - } - noaff = false; - } - if (tech->bonus.aff_class.size() > 0) { - for (EntityInfo info : inv->infos) { - auto &cls = tech->bonus.aff_class; - if (std::find(cls.begin(), cls.end(), - info.ent_class) != cls.end()) - { - ids.push_back(info.id); - } - } - noaff = false; - } - } - if (noaff) { for (auto info : inv->infos) ids.push_back(info.id); } - for (int id : ids) { - player.tech_lookup.map_id[id] = - player.tech_lookup.map_id[id] + tech->bonus; - } - } +#include "gst.h" + +#include +#include + + +Tech* Inv::get_tech (int id) { + for (auto &tech : techs) { + if (id == tech.id) return &tech; + } +} + +EntityInfo* Inv::get_info (std::string name) { + for (EntityInfo &info : infos) { + if (name == info.name) return &info; + } +} + +EntityInfo* Inv::get_info (int id) { + for (EntityInfo &info : infos) { + if (id == info.id) return &info; + } +} + +bool Inv::info_has_ability (EntityInfo* info, std::string name) { + for (int ab : info->abilities) { + if (abilities[ab].name == name) return true; + } + return false; +} + + + +Player& Gst::get_player (int id) { + for (auto &player : players) { + if (id == player.id) return player; + } +} + +Entity& Gst::get_at (int x, int y) { + for (Entity &e : entities) { + if (e.x ==x && e.y == y) return e; + } +} + +std::vector Gst::get_cost (EntityInfo *info, Player &player) { + std::vector cost = info->cost; + + std::cout << "cost : " << player.tech_lookup.id(info->id).cost[0] << " " << player.tech_lookup.id(info->id).cost[1] << "\n"; + for (int i=0; icost.size(); i++) { + cost[i] *= 1+player.tech_lookup.id(info->id).cost[i]; + cost[i] += player.tech_lookup.id(info->id).cost_abs[i]; + } + return cost; +} + +float Gst::get_trade_rate (Player &player) { + float rate = 250; + rate -= player.tech_lookup.id(0).trade * 25; + return rate; +} + +float Gst::get_type_bonus (Entity &atk, Entity &def) { + float b = 0; + switch(atk.info->ent_class) { + case EntityInfo::Class::inf: + if (def.info->ent_class == EntityInfo::Class::bld) b += 1.0f/3.0f; + if (def.info->ent_class == EntityInfo::Class::sie) b += 1.0f/3.0f; + break; + + case EntityInfo::Class::cav: + if (def.info->ent_class == EntityInfo::Class::bld) b += -0.5; + if (def.info->ent_class == EntityInfo::Class::inf) b += 1.0f/3.0f; + if (def.info->ent_class == EntityInfo::Class::ran) b += 1.0f/3.0f; + break; + + case EntityInfo::Class::ran: + if (def.info->ent_class == EntityInfo::Class::bld) b += -0.5f; + break; + + case EntityInfo::Class::sie: + if (def.info->ent_class == EntityInfo::Class::bld) b += +0.5f; + break; + } + return b; +} + +int Gst::get_vet_level (Entity &ent) { return std::min(3, ent.fights/3); } + +std::vector Gst::get_bonuses (Entity &atk, Entity &def) { + auto &tiles = inv->tiles; + auto &ground = inv->ground; + std::vector bs; + if (tiles[ground.tiles[ground.at(atk.x, atk.y)]].attack_bonus != 0) { + + bs.emplace_back( + tiles[ground.tiles[ground.at(atk.x, atk.y)]].attack_bonus, + Bonus::Id::ground, true); + } + if (tiles[ground.tiles[ground.at(def.x, def.y)]].defence_bonus != 0) { + bs.emplace_back( + tiles[ground.tiles[ground.at(def.x, def.y)]].defence_bonus, + Bonus::Id::ground, false); + } + + if (check_obstructed(def)) { + for (Entity &e : entities) { + if (&def != &e && e.x == def.x && def.y == e.y) { + bs.emplace_back(e.info->defence_bonus, + Bonus::Id::on_bld, false); + } + } + } + + if (get_type_bonus(atk, def) != 0) { + bs.emplace_back(get_type_bonus(atk, def), Bonus::Id::type, true); + } + /* only attack bonuses + if (get_type_bonus(def, atk) != 0) { + bs.emplace_back(get_type_bonus(def, atk), Bonus::Id::type, false); + }*/ + + if (inv->info_has_ability(atk.info, "Causes Fear")) + bs.emplace_back(-1.0f/3, Bonus::Id::ability, false); + if (inv->info_has_ability(def.info, "Causes Fear")) + bs.emplace_back(-1.0f/3, Bonus::Id::ability, true); + + if (inv->info_has_ability(atk.info, "Anti-Cavalry")) + bs.emplace_back(4.0f/3, Bonus::Id::ability, true); + if (inv->info_has_ability(def.info, "Anti-Cavalry")) + bs.emplace_back(4.0f/3, Bonus::Id::ability, false); + + if (inv->info_has_ability(atk.info, "Desert Charge") + && !inv->info_has_ability(def.info, "Desert Charge") + && tiles[ground.tiles[ground.at(def.x, def.y)]].name == "Desert") + bs.emplace_back(1.0f/3, Bonus::Id::ability, true); + + if (inv->info_has_ability(atk.info, "Plains Charge") + && !inv->info_has_ability(def.info, "Plains Charge") + && tiles[ground.tiles[ground.at(def.x, def.y)]].name == "Plains") + bs.emplace_back(1.0f/3, Bonus::Id::ability, true); + + if (inv->info_has_ability(atk.info, "Woodsman") + && !inv->info_has_ability(def.info, "Woodsman") + && tiles[ground.tiles[ground.at(def.x, def.y)]].name == "Forest") + bs.emplace_back(1.0f/3, Bonus::Id::ability, true); + + if (inv->info_has_ability(atk.info, "Volley") && atk.hp >= 50) + bs.emplace_back(1.0f/3, Bonus::Id::ability, true); + + if (inv->info_has_ability(atk.info, "Frenzy")) + bs.emplace_back(1/atk.hp, Bonus::Id::ability, true); + + Player &player_atk = players[atk.owner]; + Player &player_def = players[def.owner]; + float tech_attack = player_atk.tech_lookup.id(atk.info->id).attack; + if (tech_attack != 0) + bs.emplace_back(tech_attack, Bonus::Id::tech, true); + float tech_defence = player_def.tech_lookup.id(def.info->id).defence; + if (tech_defence != 0) + bs.emplace_back(tech_defence, Bonus::Id::tech, false); + + float atk_vet = get_vet_level(atk) * 0.15f; + if (atk_vet > 0) + bs.emplace_back(atk_vet, Bonus::Id::veteran, true); + float def_vet = get_vet_level(def) * 0.15f; + if (def_vet > 0) + bs.emplace_back(def_vet, Bonus::Id::veteran, false); + + return bs; +} + +float Gst::get_damage (Entity &atk, Entity &def, float atk_hp) { + float atkmul = 1; + float defmul = 1; + + auto bonuses = get_bonuses(atk, def); + for (auto bonus : bonuses) { + if (bonus.atk) { atkmul += bonus.amt; } + else { defmul += bonus.amt; } + } + + float dam = (atk.info->attack * atk_hp * atkmul) + / (2.0f*def.info->defence * defmul); + + return dam; +} + +float Gst::get_damage (Entity &atk, Entity &def) { + return get_damage(atk, def, atk.hp); +} + +bool Gst::get_first_strike (Entity &atk, Entity &def) { + bool fs { false }; + fs = inv->info_has_ability(atk.info, "First Strike"); + return fs; +} + +float clamp_hp (float hp) { + if (hp > 100) hp = 100; + if (hp < 0) hp = 0; + return hp; +} + +BattleResult Gst::battle_res (Entity &atk, Entity &def) { + BattleResult result { atk.hp, def.hp }; + bool first_strike_atk = inv->info_has_ability(atk.info, "First Strike"); + bool first_strike_def = inv->info_has_ability(def.info, "First Strike"); + bool skirmish_atk = inv->info_has_ability(atk.info, "Skirmish"); + bool skirmish_def = inv->info_has_ability(def.info, "Skirmish"); + bool anticav_atk = inv->info_has_ability(atk.info, "Anti-Cavalry"); + bool anticav_def = inv->info_has_ability(def.info, "Anti-Cavalry"); + first_strike_atk = first_strike_atk + || (skirmish_atk && def.info->range == 1); + first_strike_def = first_strike_def + || (skirmish_def && atk.info->range == 1); + first_strike_atk = first_strike_atk + || (anticav_atk && def.info->ent_class == EntityInfo::Class::cav); + first_strike_def = first_strike_def + || (anticav_def && atk.info->ent_class == EntityInfo::Class::cav); + + int dist = abs(atk.x-def.x) + abs(atk.y-def.y); + bool def_inrange = (dist <= get_range(def)) ? true : false; + + bool swap = false; + if (first_strike_def && !first_strike_atk) swap = true; + if (swap) { + if (def_inrange) { + result.atk_hp = clamp_hp( + result.atk_hp - get_damage(def, atk, result.def_hp)); + } + if (!inv->info_has_ability(atk.info, "No Counter")) + if (result.atk_hp > 0) + result.def_hp = clamp_hp( + result.def_hp - get_damage(atk, def, result.atk_hp)); + } else { + result.def_hp = clamp_hp( + result.def_hp - get_damage(atk, def, result.atk_hp)); + if (!inv->info_has_ability(def.info, "No Counter") && def_inrange) + if (result.def_hp > 0) + result.atk_hp = clamp_hp( + result.atk_hp - get_damage(def, atk, result.def_hp)); + } + + if (inv->info_has_ability(atk.info, "Rapid Fire")) + if (result.def_hp > 0) + result.def_hp = clamp_hp( + result.def_hp - get_damage(atk, def, result.def_hp)); + + if (inv->info_has_ability(def.info, "Rapid Fire") && def_inrange) + if (result.atk_hp > 0) + result.atk_hp = clamp_hp( + result.atk_hp - get_damage(def, atk, result.def_hp)); + + if (result.atk_hp > 0 && inv->info_has_ability(atk.info, "Zeal")) + result.atk_hp = clamp_hp(result.atk_hp + 20); + if (result.def_hp > 0 && inv->info_has_ability(def.info, "Zeal")) + result.def_hp = clamp_hp(result.def_hp + 20); + + return result; +} + +void Gst::battle (Entity &atk, Entity &def) { + /*std::cout << "! attack " << atk.info->name << "(hp:" << atk.hp << "), " + << def.info->name << "(hp:" << def.hp << ") \n";*/ + + auto result = battle_res(atk, def); + atk.hp = result.atk_hp; + def.hp = result.def_hp; + + if (atk.info->unit == 1) atk.fights += 1; + if (def.info->unit == 1) def.fights += 1; + + /*std::cout << "! result " << atk.info->name << "(hp:" << atk.hp << "), " + << def.info->name << "(hp:" << def.hp << ") \n";*/ + clear_dead(); +} + +void Gst::clear_dead() { + auto i = std::begin(entities); + while (i != std::end(entities)) { + if (i->hp <= 0) { + entities.erase(i); + } + else i++; + } +} + +int Gst::get_range (Entity &ent) { + int range = ent.info->range; + if (range > 1) { + auto &tiles = inv->tiles; + auto &ground = inv->ground; + range += tiles[ground.tiles[ground.at(ent.x, ent.y)]].range_bonus; + } + if (range < 1) range = 1; + return range; +} + + +void Gst::heal (Entity &atk, Entity &def) { + Player &player = get_player(atk.owner); + float amt = 20; + if (atk.info->level == 3) { amt += 10; } // improved heal + if (player.has_tech(52)) { amt += 10; } // tech illumination + def.hp = clamp_hp(def.hp + amt); +} + +void Gst::convert (Entity &atk, Entity &def) { + Player &player = get_player(atk.owner); + float amt = 0.20f; + if (player.has_tech(53)) { amt += 0.10f; } // tech faith + // caution, randomness + std::uniform_real_distribution odds(0, 1); + float value = odds(inv->engine); + std::cout << value << " / " << amt << " odds\n"; + if (value < amt) { + def.owner = atk.owner; + } +} + + +int Gst::get_nearest_enemy (Entity &ent, int &mindist) { + auto &ground = inv->ground; + int pos = -1; mindist = 9999999; + for (Entity &oth : entities) { + if (oth.owner == ent.owner) continue; + int dist = abs(oth.x-ent.x) + abs(oth.y-ent.y); + if (dist < mindist) { + mindist = dist; + pos = ground.at(oth.x, oth.y); + } + } +} + + +std::vector Gst::get_possible_trains (Entity &ent) { + Player &player = get_player(ent.owner); + auto &cls = ent.info->train_class; + std::vector trains; + if (ent.info->id == 107) { // market special case + std::vector candidates; + for (EntityInfo &info : inv->infos) { + if (info.id == 0) continue; // villager only in train_id + if (info.level > player.level) continue; + if (info.level < player.level && info.upgrade != -1) continue; + if (std::find(cls.begin(), cls.end(), info.ent_class) != cls.end()) + { + candidates.push_back(info.id); + } + } + std::shuffle(candidates.begin(), candidates.end(), inv->engine); + // pick 3 cands at random + for (int i=0; i<3; i++) trains.push_back(candidates[i]); + return trains; + } + + for (int id : ent.info->train_id) { + auto info = inv->get_info(id); + if (info->level > player.level) continue; + if (info->level < player.level && info->upgrade != -1) continue; + trains.push_back(id); + } + /* + // get all train class ids, highest upgrade level + for (EntityInfo &info : infos) { + if (info.id == 0) continue; // villager only in train_id + if (info.level > player.level) continue; + if (info.level < player.level && info.upgrade != -1) continue; + if (std::find(cls.begin(), cls.end(), info.ent_class) != cls.end()) { + if (std::find(trains.begin(), trains.end(), info.id) + != trains.end()) + { + trains.push_back(info.id); + } + } + }*/ + return trains; +} + +std::vector Gst::get_possible_builds (Entity &ent) { + std::vector builds; + for (int id : ent.info->build) { + if (check_req_build(ent, inv->get_info(id))) { + builds.push_back(id); + } + } + return builds; +} + + +bool Gst::check_req_build(Entity &ent, EntityInfo *info) { + auto &ground = inv->ground; + Player &player = players[ent.owner]; + if (player.level < info->level) return false; + for (int id : info->adjacent) { + bool adj = false; + for (Entity &e : entities) { + if (e.info->id == id && ent.owner == e.owner) { + int dist = abs(e.x-ent.x) + abs(e.y-ent.y); + if (dist == 1) { + adj = true; + } + } + } + if (!adj) return false; + } + for (int id : info->diagonal) { + bool diag = false; + for (Entity &e : entities) { + if (e.info->id == id && ent.owner == e.owner) { + int dx = abs(e.x-ent.x), dy = abs(e.y-ent.y); + int dist = dx + dy; + if (dx == 1 && dy == 1) { + diag = true; + } + } + } + if (!diag) return false; + } + if (info->id == 100) { + for (Resource &r : ground.resources) { + if (r.pos == ground.at(ent.x, ent.y)) { + return false; + } + } + int mindist = 9999; + for (Entity &e : entities) { + if (e.info->id == 100 && ent.owner == e.owner) { + int dist = abs(e.x-ent.x) + abs(ent.y-e.y); + if (dist < mindist) { + mindist = dist; + } + } + } + if (mindist < 5) { + return false; + } + return true; + } + if (info->id == 101) { + for (Resource &r : ground.resources) { + if (r.kind == Resource::Type::food + && r.pos == ground.at(ent.x, ent.y)) { + return true; + } + } + return false; + } + if (info->id == 102) { + for (Resource &r : ground.resources) { + if (r.kind == Resource::Type::gold + && r.pos == ground.at(ent.x, ent.y)) { + return true; + } + } + return false; + } + return true; +} + + +bool Gst::check_req_tech (Tech *tech, Player &player) { + if (player.leveling_up == 1) return false; + if (tech->level > player.level) { + return false; + } + if (tech->cost[0] > player.res[0] + || tech->cost[1] > player.res[1] ) + { + return false; + } + if (player.has_tech(tech->id)) { + return false; + } + bool req_id = false; + for (auto &ent : entities) { + if (ent.owner == turn // WARNING: turn is not player.id + && ent.info->id == tech->req_id + && ent.building == 0) + { + req_id = true; + break; + } + } + if (!req_id) { + return false; + } + return true; +} + +bool Gst::check_req_level (Player &player) { + if (player.leveling_up == 1) return false; + + for (float v : player.res) { + if (v <= (player.level+1)*500) return false; + } + + std::map lv_techs; + for (int id : player.techs) lv_techs[inv->get_tech(id)->level] ++; + if (player.level == 0) { + if (lv_techs[0] >= 3) return true; + } + if (player.level == 1) { + if (lv_techs[1] >= 7) return true; + } + if (player.level == 2) { + if (lv_techs[2] >= 11) return true; + } + return false; +} + +bool Gst::check_obstructed (Entity &ent) { + for (Entity &e : entities) { + if (&ent != &e && e.x == ent.x && ent.y == e.y) return true; + } + return false; +} + +void Gst::end_day () { + turn++; + if (turn >= players.size()) { + turn = 0; + day++; + } + Player &player = players[turn]; + if (player.leveling_up != -1) { + level_upgrade(player); + player.level ++; + player.leveling_up = -1; + } + for (Entity &e : entities) { + e.done = false; + e.moved = 0; + if (get_player(e.owner) == player) { + for (int i=0; iprod[i] * + (1+player.tech_lookup.id(e.info->id).prod[i]); + } + if (e.building < 0) { + e.building++; + if (e.building == 0) { + e.hp = clamp_hp(e.hp + 50); + } + } + if (e.info->unit == 1 && check_obstructed(e)) { + e.hp = clamp_hp(e.hp + 20); + } + } + } + if (player.researching != -1) { + player.techs.push_back(player.researching); + update_tech_lookup(player); + player.researching = -1; + } +} + +void Gst::level_upgrade (Player &player) { + for (Entity &e : entities) { + if (get_player(e.owner) == player) { + if (e.info->upgrade != -1 && e.info->level == player.level) { + e.info = inv->get_info(e.info->upgrade); + } + } + } +} + + +void Gst::update_tech_lookup (Player &player) { + player.tech_lookup.map_id.clear(); + for (int i : player.techs) { + Tech *tech = inv->get_tech(i); + std::vector ids { }; + bool noaff = true; + if (tech->bonus.aff_id.size() > 0) { + ids = tech->bonus.aff_id; + noaff = false; + } else { + if (tech->bonus.aff_level != -1) { + for (EntityInfo info : inv->infos) { + if (info.level == tech->bonus.aff_level) { + ids.push_back(info.id); + } + } + noaff = false; + } + if (tech->bonus.aff_class.size() > 0) { + for (EntityInfo info : inv->infos) { + auto &cls = tech->bonus.aff_class; + if (std::find(cls.begin(), cls.end(), + info.ent_class) != cls.end()) + { + ids.push_back(info.id); + } + } + noaff = false; + } + } + if (noaff) { for (auto info : inv->infos) ids.push_back(info.id); } + for (int id : ids) { + player.tech_lookup.map_id[id] = + player.tech_lookup.map_id[id] + tech->bonus; + } + } } \ No newline at end of file diff --git a/game/gst.h b/game/gst.h index 376026e..8ade237 100644 --- a/game/gst.h +++ b/game/gst.h @@ -1,125 +1,125 @@ -#ifndef GST_H -#define GST_H - -#include -#include -#include -#include -#include - -#include // just for picking market trains - -#include "ground.h" -#include "entity.h" -#include "tile.h" -#include "player.h" -#include "tech.h" - -class Ability { - public: - Ability(std::string name) : name(name) {} - std::string name; -}; - -class Bonus { - public: - Bonus(float amt, int id, bool atk) : amt(amt), id(id), atk(atk) {} - float amt; int id; bool atk; - enum Id { ground, type, ability, tech, veteran, on_bld, adjacency }; - std::string id_string () { - switch (id) { - case ground: return "Ground"; - case type: return "Class"; - case ability: return "Ability"; - case tech: return "Tech"; - case veteran: return "Veteran"; - case on_bld: return "On Building"; - case adjacency: return "Adjacency"; - } - } -}; - -class BattleResult { - public: - BattleResult(float atk_hp, float def_hp) - : atk_hp(atk_hp), def_hp(def_hp) {} - float atk_hp, def_hp; -}; - -class Inv { - public: - Inv () {} - - std::vector techs; - std::vector abilities; - std::vector infos; - std::vector tiles; - Ground ground; - - Tech* get_tech (int id); - EntityInfo* get_info (std::string name); - EntityInfo* get_info (int id); - bool info_has_ability (EntityInfo* info, std::string name); - - std::default_random_engine engine = std::default_random_engine{}; -}; - -class Gst { - public: - Gst(Inv *inv) : inv(inv) { } - - // copy constructor - Gst (const Gst& rhs) { - inv = rhs.inv; entities = rhs.entities; players = rhs.players; - turn = rhs.turn; day = rhs.day; - } - Gst& operator=(const Gst& rhs) { - inv = rhs.inv; entities = rhs.entities; players = rhs.players; - turn = rhs.turn; day = rhs.day; - }; - - Inv *inv; - - std::vector entities; - std::vector players; - int turn { 0 }; - int day { 0 }; - - Player& get_player (int id); - - Entity& get_at (int x, int y); - std::vector get_cost (EntityInfo *info, Player &player); - float get_trade_rate (Player &player); - - int get_vet_level (Entity &ent); - float get_type_bonus (Entity &atk, Entity &def); - std::vector get_bonuses (Entity &atk, Entity &def); - float get_damage (Entity &atk, Entity &def); - float get_damage (Entity &atk, Entity &def, float atk_hp); - bool get_first_strike (Entity &atk, Entity &def); - BattleResult battle_res (Entity &atk, Entity &def); - void clear_dead(); - int get_range(Entity &ent); - - void battle (Entity &atk, Entity &def); - - void heal (Entity &atk, Entity &def); - void convert (Entity &atk, Entity &def); - - int get_nearest_enemy (Entity &ent, int &mindist); - - std::vector get_possible_trains (Entity &ent); - std::vector get_possible_builds (Entity &ent); - - bool check_req_build (Entity &ent, EntityInfo *info); - bool check_req_tech (Tech *tech, Player &player); - bool check_req_level (Player &player); - bool check_obstructed (Entity &ent); - - void end_day (); - void level_upgrade (Player &player); - - void update_tech_lookup (Player &player); -}; - +#ifndef GST_H +#define GST_H + +#include +#include +#include +#include +#include + +#include // just for picking market trains + +#include "ground.h" +#include "entity.h" +#include "tile.h" +#include "player.h" +#include "tech.h" + +class Ability { + public: + Ability(std::string name) : name(name) {} + std::string name; +}; + +class Bonus { + public: + Bonus(float amt, int id, bool atk) : amt(amt), id(id), atk(atk) {} + float amt; int id; bool atk; + enum Id { ground, type, ability, tech, veteran, on_bld, adjacency }; + std::string id_string () { + switch (id) { + case ground: return "Ground"; + case type: return "Class"; + case ability: return "Ability"; + case tech: return "Tech"; + case veteran: return "Veteran"; + case on_bld: return "On Building"; + case adjacency: return "Adjacency"; + } + } +}; + +class BattleResult { + public: + BattleResult(float atk_hp, float def_hp) + : atk_hp(atk_hp), def_hp(def_hp) {} + float atk_hp, def_hp; +}; + +class Inv { + public: + Inv () {} + + std::vector techs; + std::vector abilities; + std::vector infos; + std::vector tiles; + Ground ground; + + Tech* get_tech (int id); + EntityInfo* get_info (std::string name); + EntityInfo* get_info (int id); + bool info_has_ability (EntityInfo* info, std::string name); + + std::default_random_engine engine = std::default_random_engine{}; +}; + +class Gst { + public: + Gst(Inv *inv) : inv(inv) { } + + // copy constructor + Gst (const Gst& rhs) { + inv = rhs.inv; entities = rhs.entities; players = rhs.players; + turn = rhs.turn; day = rhs.day; + } + Gst& operator=(const Gst& rhs) { + inv = rhs.inv; entities = rhs.entities; players = rhs.players; + turn = rhs.turn; day = rhs.day; + }; + + Inv *inv; + + std::vector entities; + std::vector players; + int turn { 0 }; + int day { 0 }; + + Player& get_player (int id); + + Entity& get_at (int x, int y); + std::vector get_cost (EntityInfo *info, Player &player); + float get_trade_rate (Player &player); + + int get_vet_level (Entity &ent); + float get_type_bonus (Entity &atk, Entity &def); + std::vector get_bonuses (Entity &atk, Entity &def); + float get_damage (Entity &atk, Entity &def); + float get_damage (Entity &atk, Entity &def, float atk_hp); + bool get_first_strike (Entity &atk, Entity &def); + BattleResult battle_res (Entity &atk, Entity &def); + void clear_dead(); + int get_range(Entity &ent); + + void battle (Entity &atk, Entity &def); + + void heal (Entity &atk, Entity &def); + void convert (Entity &atk, Entity &def); + + int get_nearest_enemy (Entity &ent, int &mindist); + + std::vector get_possible_trains (Entity &ent); + std::vector get_possible_builds (Entity &ent); + + bool check_req_build (Entity &ent, EntityInfo *info); + bool check_req_tech (Tech *tech, Player &player); + bool check_req_level (Player &player); + bool check_obstructed (Entity &ent); + + void end_day (); + void level_upgrade (Player &player); + + void update_tech_lookup (Player &player); +}; + #endif \ No newline at end of file diff --git a/game/load.cpp b/game/load.cpp index ea4e9e7..f9b0135 100644 --- a/game/load.cpp +++ b/game/load.cpp @@ -1,150 +1,150 @@ -#include -#include -#include - -#include "load.h" - -#include "nlohmann/json.hpp" -using json = nlohmann::json; - -std::vector load_abilities () { - std::vector abs; - abs.emplace_back("Anti-Cavalry"); - abs.emplace_back("Skirmish"); - abs.emplace_back("First Strike"); - abs.emplace_back("Rapid Fire"); - abs.emplace_back("Units Only"); - abs.emplace_back("Buildings Only"); - abs.emplace_back("No Counter"); - abs.emplace_back("No Move & Attack"); - abs.emplace_back("Causes Fear"); - abs.emplace_back("Desert Charge"); - abs.emplace_back("Plains Charge"); - abs.emplace_back("Scares Horses"); - abs.emplace_back("Woodsman"); - abs.emplace_back("Volley"); - abs.emplace_back("Frenzy"); - abs.emplace_back("Zeal"); - abs.emplace_back("Scout"); - abs.emplace_back("Convert"); - abs.emplace_back("Heal"); - abs.emplace_back("Seasoned Veteran"); - return abs; -} - -void load_json (Inv &inv) { - inv.abilities = load_abilities(); - - std::ifstream file_tiles("content/tiles.json"); - json j_tiles; file_tiles >> j_tiles; - for (auto it : j_tiles) { - Tile tile; - tile.name = it["name"]; - tile.move_cost = it["move_cost"]; - tile.defence_bonus = it["defence_bonus"]; - tile.range_bonus = it["range_bonus"]; - tile.spritebounds = vec2 { it["spritebounds"][0], it["spritebounds"][1] }; - inv.tiles.push_back(tile); - } - - std::ifstream file_ents("content/entities.json"); - json j_ents; file_ents >> j_ents; - for (auto it : j_ents) { - EntityInfo ent; - ent.id = it["id"]; - ent.name = it["name"]; - ent.level = it["level"]; - ent.range = it["range"]; - ent.move = it["move"]; - ent.attack = it["attack"]; - ent.defence = it["defence"]; - ent.sight = it["sight"]; - ent.unit = it["unit"]; - for (int i=0; i())); - } - for (auto ad : it["adjacent"]) { - ent.adjacent.push_back(ad); - } - for (auto ad : it["diagonal"]) { - ent.diagonal.push_back(ad); - } - ent.ent_class = (EntityInfo::Class) EntityClass::from_string( - it["class"].get()); - for (auto ab : it["abilities"]) { - int index = 0; - for (int i=0; i> j_techs; - for (auto it : j_techs) { - Tech tech; - tech.name = it["name"]; - tech.id = it["id"]; - tech.level = it["level"]; - tech.req_id = it["req_id"]; - for (int i=0; i())); - } - if (b.contains("aff_level")) { - tech.bonus.aff_level = b["aff_level"]; - } - if (b.contains("aff_all")) { - tech.bonus.aff_all = b["aff_all"]; - } - } - inv.techs.push_back(tech); - } +#include +#include +#include + +#include "load.h" + +#include "nlohmann/json.hpp" +using json = nlohmann::json; + +std::vector load_abilities () { + std::vector abs; + abs.emplace_back("Anti-Cavalry"); + abs.emplace_back("Skirmish"); + abs.emplace_back("First Strike"); + abs.emplace_back("Rapid Fire"); + abs.emplace_back("Units Only"); + abs.emplace_back("Buildings Only"); + abs.emplace_back("No Counter"); + abs.emplace_back("No Move & Attack"); + abs.emplace_back("Causes Fear"); + abs.emplace_back("Desert Charge"); + abs.emplace_back("Plains Charge"); + abs.emplace_back("Scares Horses"); + abs.emplace_back("Woodsman"); + abs.emplace_back("Volley"); + abs.emplace_back("Frenzy"); + abs.emplace_back("Zeal"); + abs.emplace_back("Scout"); + abs.emplace_back("Convert"); + abs.emplace_back("Heal"); + abs.emplace_back("Seasoned Veteran"); + return abs; +} + +void load_json (Inv &inv) { + inv.abilities = load_abilities(); + + std::ifstream file_tiles("content/tiles.json"); + json j_tiles; file_tiles >> j_tiles; + for (auto it : j_tiles) { + Tile tile; + tile.name = it["name"]; + tile.move_cost = it["move_cost"]; + tile.defence_bonus = it["defence_bonus"]; + tile.range_bonus = it["range_bonus"]; + tile.spritebounds = vec2 { it["spritebounds"][0], it["spritebounds"][1] }; + inv.tiles.push_back(tile); + } + + std::ifstream file_ents("content/entities.json"); + json j_ents; file_ents >> j_ents; + for (auto it : j_ents) { + EntityInfo ent; + ent.id = it["id"]; + ent.name = it["name"]; + ent.level = it["level"]; + ent.range = it["range"]; + ent.move = it["move"]; + ent.attack = it["attack"]; + ent.defence = it["defence"]; + ent.sight = it["sight"]; + ent.unit = it["unit"]; + for (int i=0; i())); + } + for (auto ad : it["adjacent"]) { + ent.adjacent.push_back(ad); + } + for (auto ad : it["diagonal"]) { + ent.diagonal.push_back(ad); + } + ent.ent_class = (EntityInfo::Class) EntityClass::from_string( + it["class"].get()); + for (auto ab : it["abilities"]) { + int index = 0; + for (int i=0; i> j_techs; + for (auto it : j_techs) { + Tech tech; + tech.name = it["name"]; + tech.id = it["id"]; + tech.level = it["level"]; + tech.req_id = it["req_id"]; + for (int i=0; i())); + } + if (b.contains("aff_level")) { + tech.bonus.aff_level = b["aff_level"]; + } + if (b.contains("aff_all")) { + tech.bonus.aff_all = b["aff_all"]; + } + } + inv.techs.push_back(tech); + } } \ No newline at end of file diff --git a/game/load.h b/game/load.h index d9da659..7dce21e 100644 --- a/game/load.h +++ b/game/load.h @@ -1,8 +1,8 @@ -#ifndef LOAD_H -#define LOAD_H - -#include "gst.h" - -void load_json (Inv &inv); - +#ifndef LOAD_H +#define LOAD_H + +#include "gst.h" + +void load_json (Inv &inv); + #endif \ No newline at end of file diff --git a/game/menu.cpp b/game/menu.cpp index b5a91b9..6349f29 100644 --- a/game/menu.cpp +++ b/game/menu.cpp @@ -1,77 +1,77 @@ -#include - -#include "menu.h" - -void Menu::close () { - active = false; -} - -void Menu::open (vec2 res) { - over = -1; - active = true; - pos = vec2 { (float)res.x, (float)res.y }; - float height = options.size() * 20; - size = vec2 { 150, height+10 }; - pos *= 0.5f; - pos -= size/2; -} - -int Menu::mouse_option (vec2 mouse) { - int i=0; - for (Option opt : options) { - vec2 off { 0, 5.0f + i*20 }; - vec2 sizeopt { 150, 20 }; - off += pos; - if (off.x < mouse.x && mouse.x < off.x+sizeopt.x - && off.y < mouse.y && mouse.y < off.y+sizeopt.y ) { - return opt.id; - } - i++; - } - return -1; -} - - -void Menu_tech::open (vec2 res) { - over = -1; - active = true; - pos = vec2 { (float)res.x, (float)res.y }; - - tech_opt_ordered.clear(); - tech_opt_ordered.emplace_back(); - tech_opt_ordered.emplace_back(); - tech_opt_ordered.emplace_back(); - tech_opt_ordered.emplace_back(); - - for (OptionTech opt : tech_options) { - tech_opt_ordered[opt.tech->level].emplace_back(opt); - } - int maxsize = 0; - for (auto v : tech_opt_ordered) { - maxsize = maxsize < v.size() ? v.size() : maxsize; - } - - float width = 150 * tech_opt_ordered.size(); - float height = maxsize * 10; - size = vec2 { width, height+20 }; - pos *= 0.5f; - pos -= size/2; -} - -int Menu_tech::mouse_option (vec2 mouse) { - float x = 0, y = 0; - for (auto v : tech_opt_ordered) { - for (auto opt : v) { - vec2 off { x*150, 10.0f + y*10 }; - vec2 sizeopt { 150, 10 }; - off += pos; - if (off.x < mouse.x && mouse.x < off.x+sizeopt.x - && off.y < mouse.y && mouse.y < off.y+sizeopt.y ) { - return opt.tech->id; - } - y++; - } - x++; y=0; - } - return -1; +#include + +#include "menu.h" + +void Menu::close () { + active = false; +} + +void Menu::open (vec2 res) { + over = -1; + active = true; + pos = vec2 { (float)res.x, (float)res.y }; + float height = options.size() * 20; + size = vec2 { 150, height+10 }; + pos *= 0.5f; + pos -= size/2; +} + +int Menu::mouse_option (vec2 mouse) { + int i=0; + for (Option opt : options) { + vec2 off { 0, 5.0f + i*20 }; + vec2 sizeopt { 150, 20 }; + off += pos; + if (off.x < mouse.x && mouse.x < off.x+sizeopt.x + && off.y < mouse.y && mouse.y < off.y+sizeopt.y ) { + return opt.id; + } + i++; + } + return -1; +} + + +void Menu_tech::open (vec2 res) { + over = -1; + active = true; + pos = vec2 { (float)res.x, (float)res.y }; + + tech_opt_ordered.clear(); + tech_opt_ordered.emplace_back(); + tech_opt_ordered.emplace_back(); + tech_opt_ordered.emplace_back(); + tech_opt_ordered.emplace_back(); + + for (OptionTech opt : tech_options) { + tech_opt_ordered[opt.tech->level].emplace_back(opt); + } + int maxsize = 0; + for (auto v : tech_opt_ordered) { + maxsize = maxsize < v.size() ? v.size() : maxsize; + } + + float width = 150 * tech_opt_ordered.size(); + float height = maxsize * 10; + size = vec2 { width, height+20 }; + pos *= 0.5f; + pos -= size/2; +} + +int Menu_tech::mouse_option (vec2 mouse) { + float x = 0, y = 0; + for (auto v : tech_opt_ordered) { + for (auto opt : v) { + vec2 off { x*150, 10.0f + y*10 }; + vec2 sizeopt { 150, 10 }; + off += pos; + if (off.x < mouse.x && mouse.x < off.x+sizeopt.x + && off.y < mouse.y && mouse.y < off.y+sizeopt.y ) { + return opt.tech->id; + } + y++; + } + x++; y=0; + } + return -1; } \ No newline at end of file diff --git a/game/menu.h b/game/menu.h index 41348a7..471bfd7 100644 --- a/game/menu.h +++ b/game/menu.h @@ -1,90 +1,90 @@ -#ifndef MENU_H -#define MENU_H - -#include -#include - -#include "../umath/vec2.h" -#include "tech.h" - -class Option { - public: - Option(std::string name, int id) : name(name), id(id) {} - - std::string name; - int id; - - std::vector cost; -}; - - -class Menu { - public: - bool active { false }; - std::vector