Skip to content

Commit

Permalink
Combo System: First Pass
Browse files Browse the repository at this point in the history
This is the first pass at a combo system. The player and enemies can
use this system to chain together a series of moves and keep the
opponent from moving taking a turn.

As far as proofs of concept go, this is very exciting!
  • Loading branch information
Kestrel Gregorich-Trevor committed Jan 18, 2024
1 parent b3427ba commit ba68393
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 121 deletions.
84 changes: 73 additions & 11 deletions data/creature/creatures.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,50 @@
"chr": "@",
"color": "white",
"hp": 40,
"speed": 100,
"speed": 40,
"unique": 1,
"attacks": [
{ "damage": 6, "accuracy": 70, "types": ["Low"] },
{ "damage": 8, "accuracy": 95, "types": ["Mid"] },
{ "damage": 12, "accuracy": 50, "kb": 1, "types": ["High"] },
{ "damage": 6, "kb": 2, "accuracy": 70, "types": ["Grab"] }
{ "damage": 6, "stun": 40, "accuracy": 70, "types": ["Low"] },
{ "damage": 8, "stun": 80, "accuracy": 95, "types": ["Mid"] },
{ "damage": 12, "stun": 160, "recovery": 80, "accuracy": 50, "kb": 1, "types": ["High"] },
{ "damage": 6, "stun": 80, "kb": 2, "accuracy": 70, "types": ["Grab"] }
],
"equip" : {},
"description": "A standarad fighter. Easy to use."
"equip" : {}
},
"sandbag": {
"name": "sandbag",
"appearance": "punching bag",
"chr": "S",
"color": "blue",
"hp": 100,
"speed": 40,
"attacks": [
{"damage": 6, "accuracy": 0, "types": ["Mid"]}
],
"ai": { "seekdef": 10 }
},
"sdrone": {
"name": "spy drone",
"appearance": "robot",
"chr": "r",
"color": "black",
"hp": 1,
"speed": 50,
"speed": 20,
"attacks": [
{ "damage": 4, "accuracy": 75, "types": ["High"] }
],
"ai": {
"seekdef": 10
},
"grow_up": "assault drone",
"description": "This aerial drone has the logo of Polaris Industries stamped on it."
"grow_up": "assault drone"
},
"flunky": {
"name": "flunky",
"appearance": "business man",
"chr": "@",
"color": "green",
"hp": 12,
"speed": 100,
"speed": 40,
"attacks": [
{ "damage": 6, "kb": 1, "accuracy": 70, "types": ["Mid"] },
{ "damage": 8, "accuracy": 30, "types": ["Low"] }
Expand All @@ -46,5 +56,57 @@
"seekdef": 5
},
"description": "One of Polaris's goons. It's just a day job."
},
"flyweight": {
"name": "flyweight",
"appearance": "boxer",
"chr": "B",
"color": "yellow",
"hp": 12,
"speed": 40,
"attacks": [
{"damage": 6, "accuracy": 80, "types": ["Mid"]}
],
"ai": { "seekdef": 3 }
},
"featherweight": {
"name": "featherweight",
"appearance": "boxer",
"chr": "B",
"color": "white",
"hp": 20,
"speed": 40,
"attacks": [
{"damage": 6, "accuracy": 80, "types": ["Mid"]},
{"damage": 8, "accuracy": 60, "types": ["Mid"]}
],
"ai": { "seekdef": 3 }
},
"middleweight": {
"name": "middleweight",
"appearance": "boxer",
"chr": "B",
"color": "blue",
"hp": 40,
"speed": 40,
"attacks": [
{"damage": 8, "accuracy": 80, "types": ["Mid"]},
{"damage": 10, "kb":1, "accuracy": 70, "types": ["Mid"]}
],
"ai": { "seekdef": 3 }
},
"heavyweight": {
"name": "heavyweight",
"appearance": "boxer",
"chr": "B",
"color": "magenta",
"hp": 80,
"speed": 40,
"attacks": [
{"damage": 8, "kb": 1, "accuracy": 80, "types": ["Mid"]},
{"damage": 10, "kb": 1, "accuracy": 80, "types": ["Mid"]},
{"damage": 12, "accuracy": 60, "types": ["High"]}
],
"ai": { "seekdef": 3 }
}
}
5 changes: 2 additions & 3 deletions include/actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ struct attack {
unsigned char dam;
unsigned char kb;
unsigned char accuracy;
#if 0
unsigned char stun;
unsigned char cost;
#endif
unsigned char recovery;
/* bitfields */
unsigned short hitdescs;
};
Expand All @@ -70,6 +68,7 @@ struct actor {
int speed; /* For creatures, denotes move speed. For items, attack speed. */
signed char evasion, accuracy;
signed char temp_evasion, temp_accuracy;
unsigned char combo_counter;
/* Attack list */
struct attack attacks[MAX_ATTK];
/* Components */
Expand Down
5 changes: 5 additions & 0 deletions include/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ struct ai {
#define is_guardian(actor) (actor->ai && actor->ai->guardian)

/* Function Prototypes */
void make_aware(struct actor *, struct actor *, int);
struct ai *init_ai(struct actor *);
void take_turn(struct actor *);
struct attack choose_attack(struct actor *, struct actor *);
int is_aware(struct actor *, struct actor *);

#define TURN_FULL 40
#define TURN_HALF 20
#define TURN_QUARTER 10

#endif
3 changes: 3 additions & 0 deletions include/combat.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ int calculate_accuracy(struct actor *, struct attack *);
struct attack *get_active_attack(int);
int change_stance(struct actor *, short, int);

#define DAMAGE_SCALING 0.9
#define HITSTUN_DETERIORATION 4

#endif
4 changes: 2 additions & 2 deletions src/action.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ int move_mon(struct actor* mon, int x, int y) {
stop_running();
return 0;
} else {
return 100;
return TURN_FULL;
}
}
/* If there is someone there, attack them! */
Expand All @@ -184,7 +184,7 @@ int move_mon(struct actor* mon, int x, int y) {
stop_running();
return 0;
} else {
return 100;
return TURN_FULL;
}
}
/* Perform movement */
Expand Down
95 changes: 55 additions & 40 deletions src/ai.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
#include "spawn.h"
#include "mapgen.h"

void make_aware(struct actor *, struct actor *);
int check_stealth(struct actor *, struct actor *);
void increment_regular_values(struct actor *);
struct action *get_tile_action(struct actor *);
struct action *ai_decision(struct actor *);

/**
* @brief Initialize an AI struct.
Expand All @@ -43,61 +43,70 @@ struct ai *init_ai(struct actor *actor) {
return actor->ai;
}

/**
* @brief Make a decision about the current turn;
*
* @param actor the actor making the decision
* @return struct action* the action to take
*/
struct action *ai_decision(struct actor *actor) {
struct action *action = NULL;
/* Stealth and Visibility */
if (!is_visible(actor->x, actor->y)) {
if (actor->ai->seekcur)
actor->ai->seekcur--;
}
if (!is_aware(actor, g.player))
check_stealth(actor, g.player);
action = get_tile_action(actor);
if (!action) {
struct coord cl;
/* AI Decision-Making */
if (is_aware(actor, g.player))
cl = best_adjacent_tile(actor->x, actor->y, 1, 1, HM_PLAYER);
else if (is_guardian(actor)) {
cl.x = 0;
cl.y = 0;
} else
cl = best_adjacent_tile(actor->x, actor->y, 1, 1, HM_DOWNSTAIR);
if (cl.x == -99 || cl.y == -99) {
action = dir_to_action(0, 0);
} else {
action = dir_to_action(cl.x, cl.y);
}
}
return action;
}

/**
* @brief An actor takes a turn if able. If the actor is the player, then
pass control to the user. Otherwise, make use of ai functionality.
*
* @param actor The actor who will be taking the turn.
*/
void take_turn(struct actor *actor) {
int cost = 100;
int cost = TURN_FULL;
short heatmap_field = heatmaps[HM_PLAYER].field;
struct action *action = NULL;

if (actor != g.player && !actor->ai)
return;

/* Refill energy */
actor->energy += 100;
if (actor->energy > 0 && actor->energy < 100)
actor->energy = 100;
increment_regular_values(actor);

while (actor->energy > 0) {
actor->can_tech = 0;
if (actor->stance == GRAB) {
change_stance(actor, actor->old_stance, 0);
if (actor->stance == GRAB || actor->stance == STANCE_STUN) {
change_stance(actor, actor->old_stance, actor->stance != STANCE_STUN);
}
actor->combo_counter = 0;
actor->old_stance = actor->stance;
if (actor == g.player) {
render_all();
/* Player input */
action = get_action();
} else {
/* Stealth and Visibility */
if (!is_visible(actor->x, actor->y)) {
if (actor->ai->seekcur)
actor->ai->seekcur--;
}
if (!is_aware(actor, g.player))
check_stealth(actor, g.player);
action = get_tile_action(actor);
if (!action) {
struct coord cl;
/* AI Decision-Making */
if (is_aware(actor, g.player))
cl = best_adjacent_tile(actor->x, actor->y, 1, 1, HM_PLAYER);
else if (is_guardian(actor)) {
cl.x = 0;
cl.y = 0;
} else
cl = best_adjacent_tile(actor->x, actor->y, 1, 1, HM_DOWNSTAIR);
if (cl.x == -99 || cl.y == -99) {
action = dir_to_action(0, 0);
} else {
action = dir_to_action(cl.x, cl.y);
}
}
action = ai_decision(actor);
}
if (action)
cost = execute_action(actor, action);
Expand Down Expand Up @@ -142,7 +151,9 @@ struct attack choose_attack(struct actor *aggressor, struct actor *target) {
}

int is_aware(struct actor *aggressor, struct actor *target) {
if (!aggressor->ai)
if (aggressor == g.player)
return 1;
else if (!aggressor->ai)
return 0;
else if (target == g.player)
return aggressor->ai->seekcur;
Expand All @@ -153,16 +164,17 @@ int is_aware(struct actor *aggressor, struct actor *target) {
#define MAX_SPOT_MSG 2
static const char *spot_msgs[MAX_SPOT_MSG] = { "spots", "notices" };

void make_aware(struct actor *aggressor, struct actor *target) {
if (aggressor == g.player && is_visible(aggressor->x, aggressor->y)) {
void make_aware(struct actor *aggressor, struct actor *target, int silent) {
if (!silent && aggressor == g.player && is_visible(aggressor->x, aggressor->y)) {
logm("%s notices %s.", actor_name(aggressor, NAME_THE | NAME_CAP),
actor_name(target, NAME_A));
}
if (aggressor->ai) {
logm("%s %s %s%s", actor_name(aggressor, NAME_A | NAME_CAP),
spot_msgs[rndmx(MAX_SPOT_MSG)],
actor_name(target, NAME_THE),
in_danger(g.player) ? "!" : "." );
if (!silent)
logm("%s %s %s%s", actor_name(aggressor, NAME_A | NAME_CAP),
spot_msgs[rndmx(MAX_SPOT_MSG)],
actor_name(target, NAME_THE),
in_danger(g.player) ? "!" : "." );
aggressor->ai->seekcur = aggressor->ai->seekdef;
if (!g.target) g.target = aggressor;
}
Expand All @@ -172,7 +184,7 @@ int check_stealth(struct actor *aggressor, struct actor *target) {
(void) aggressor;
if (is_visible(target->x, target->y) && is_visible(aggressor->x, aggressor->y)
&& !rndmx(2))
make_aware(aggressor, target);
make_aware(aggressor, target, 0);
return 0;
}

Expand All @@ -193,6 +205,9 @@ void increment_regular_values(struct actor *actor) {
logm("Something comes down the stairs.");
}
}
actor->energy += TURN_FULL;
if (actor->energy > 0 && actor->energy < TURN_FULL)
actor->energy = TURN_FULL;
/* Temporary evasion and accuracy slowly return to zero. */
if (actor->temp_accuracy != 0) {
actor->temp_accuracy < 0 ? actor->temp_accuracy++ : actor->temp_accuracy--;
Expand Down
Loading

0 comments on commit ba68393

Please sign in to comment.