Tech-demo combo system, and some other things.
authorMartin Read <martin@blackswordsonics.com>
Tue, 18 Feb 2014 23:42:25 +0000 (23:42 +0000)
committerMartin Read <martin@blackswordsonics.com>
Tue, 18 Feb 2014 23:42:25 +0000 (23:42 +0000)
* core.hh: New command CMD_POWER_ATTACK, and some Doxygenation.
* default.permobjs: All weapons now flagged melee or ranged.
* monsters.cc, monsters.hh: Elimination of newsym() calls and addition of new
  functions to adjust flow of updates.
* notify-local-tty.cc, notify.hh: New notifications galore.
* permobj.hh, pobj_comp: New item flags
* player.hh: new boolean functions for examining player's wielding status
* u.cc: Action rewriting system with tech-demo combo detection case.

Sir Not Appearing In This Commit: Making a power attack be an actual thing
instead of just an ordinary attack.

core.hh
default.permobjs
monsters.cc
monsters.hh
notify-local-tty.cc
notify.hh
permobj.hh
player.hh
pobj_comp
u.cc

diff --git a/core.hh b/core.hh
index 61fe154..d2cbbe0 100644 (file)
--- a/core.hh
+++ b/core.hh
@@ -99,7 +99,7 @@ enum Gamecolour
 #define LAST_FIXED_COLOUR Gcol_silver
 #define LAST_COLOUR Gcol_prio_bug
 
-/* XXX enum damtyp - types of damage. */
+/*! \brief Identification code for damage types */
 enum Damtyp {
     DT_PHYS = 0, DT_COLD, DT_FIRE, DT_NECRO,
     DT_ELEC, DT_HELLFIRE, DT_POISON,
@@ -107,30 +107,36 @@ enum Damtyp {
 };
 #define DT_COUNT (1 + DT_DROWNING)
 
-/* XXX enum Game_cmd - player actions. */
+/*! \brief Identification code for player actions */
 enum Game_cmd {
-    WALK, STAND_STILL, GO_DOWN_STAIRS, ATTACK,
+    REJECTED_ACTION = -1, WALK, STAND_STILL, GO_DOWN_STAIRS, ATTACK,
     GET_ITEM, DROP_ITEM,
     WIELD_WEAPON, WEAR_ARMOUR, TAKE_OFF_ARMOUR, PUT_ON_RING, REMOVE_RING,
     QUAFF_POTION, READ_SCROLL, THROW_FLASK, EAT_FOOD,
     EMANATE_ARMOUR, ZAP_WEAPON, MAGIC_RING,
     USE_ACTIVE_SKILL, ALLOCATE_SKILL_POINT,
     SAVE_GAME, QUIT,
-    WIZARD_LEVELUP, WIZARD_DESCEND
+    WIZARD_LEVELUP, WIZARD_DESCEND,
+    /* combos begin here */
+    CMD_POWER_ATTACK
 };
 
-/* XXX enum Death */
-/* Sadly, there are not 52 kinds of way to die. */
+/*! \brief Identification code for ways to die
+ * 
+ * Sadly, there are not 52 kinds of way to die.
+ */
 enum Death {
     DEATH_KILLED, DEATH_KILLED_MON, DEATH_BODY, DEATH_AGILITY,
     DEATH_LASH, DEATH_RIBBONS
 };
 
+/*! \brief Fell powers that might influence the Princess's fate
+ */
 enum Fell_powers {
-    FePo_iron, // boosted by ironguard armour, hellglaive
-    FePo_decay, // boosted by foetid vestments, 
-    FePo_bone, // boosted by lich's robe, vampire ring
-    FePo_flesh // boosted by t's lash or the ribbons,
+    FePo_iron,
+    FePo_decay,
+    FePo_bone,
+    FePo_flesh
 };
 
 #define TOTAL_FELL_POWERS (1 + FePo_flesh)
@@ -140,7 +146,9 @@ enum Fell_powers {
 #define RESIST_RING     0x00010000u
 #define RESIST_ARMOUR   0x00020000u
 
-/*! \brief Represent an in-game action in more detail than Game_cmd
+/*! \brief Represent an in-game action by the player
+ *
+ * This might, at some point, get adapted to 
  */
 struct Action
 {
index b6858e2..27e441b 100644 (file)
@@ -33,6 +33,7 @@ POWER 4
 POWER2 0
 DEPTH 1
 DAMAGEABLE
+MELEE_WEAPON
 
 WEAPON long sword
 PLURAL long swords
@@ -45,6 +46,7 @@ POWER 10
 POWER2 0
 DEPTH 4
 DAMAGEABLE
+MELEE_WEAPON
 
 WEAPON mace
 PLURAL maces
@@ -57,6 +59,7 @@ POWER 7
 POWER2 0
 DEPTH 2
 DAMAGEABLE
+MELEE_WEAPON
 
 WEAPON runesword
 PLURAL runeswords
@@ -69,6 +72,7 @@ POWER 20
 POWER2 0
 DEPTH 12
 DAMAGEABLE
+MELEE_WEAPON
 
 WEAPON hellglaive
 PLURAL hellglaives
@@ -83,6 +87,7 @@ DEPTH 30
 NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
+MELEE_WEAPON
 
 WEAPON plague scythe
 PLURAL plague sycthes
@@ -95,6 +100,7 @@ POWER 20
 POWER2 0
 DEPTH 30
 NOTIFY_EQUIP
+MELEE_WEAPON
 
 WEAPON tormentor's lash
 PLURAL tormentor's lashes
@@ -109,6 +115,7 @@ DEPTH 30
 NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
+MELEE_WEAPON
 
 WEAPON death staff
 PLURAL death staves
@@ -121,6 +128,7 @@ POWER 18
 POWER2 0
 DEPTH 30
 NOTIFY_EQUIP
+MELEE_WEAPON
 
 WEAPON staff of fire
 PLURAL staves of fire
@@ -134,6 +142,7 @@ POWER2 0
 DEPTH 20
 DAMAGEABLE
 ACTIVATABLE
+MELEE_WEAPON
 
 WEAPON bow
 PLURAL bows
@@ -146,7 +155,7 @@ POWER 8
 POWER2 0
 DEPTH 1
 DAMAGEABLE
-
+RANGED_WEAPON
 WEAPON crossbow
 PLURAL crossbows
 DESC A crossbow.
@@ -158,6 +167,7 @@ POWER 16
 POWER2 0
 DEPTH 6
 DAMAGEABLE
+RANGED_WEAPON
 
 WEAPON thunderbow
 PLURAL thunderbows
@@ -170,6 +180,7 @@ POWER 16
 POWER2 0
 DEPTH 30
 DAMAGEABLE
+RANGED_WEAPON
 
 POTION healing potion
 PLURAL healing potions
index 4c667a9..6f3d4cc 100644 (file)
@@ -145,7 +145,10 @@ int create_mon(int pm_idx, Coord c)
             }
             monsters[mon].awake = false;
             lvl.set_mon_at(c, mon);
-            newsym(c);
+            if (mon_visible(mon))
+            {
+                notify_new_mon_at(c, mon);
+            }
             return mon;
         }
     }
@@ -298,26 +301,39 @@ void heal_mon(int mon, int amount, int cansee)
     }
 }
 
+void unplace_mon(int mon)
+{
+    lvl.set_mon_at(monsters[mon].pos, NO_MON);
+    monsters[mon].used = false;
+}
+
+/*! \brief Handle the death of a monster
+ *
+ * \todo Support special effects on monster death
+ */
+void kill_mon(int mon, bool by_you)
+{
+    death_drop(mon); // phat lewt!
+    monsters[mon].hpcur = -1; // Set HP to -1 so nothing will think it's alive
+    unplace_mon(mon); // cleanup
+    if (by_you)
+    {
+        notify_player_killed_mon(mon);
+        gain_experience(permons[monsters[mon].mon_id].exp);
+    }
+    else if (mon_visible(mon))
+    {
+        notify_mon_dies(mon);
+    }
+}
+
 void damage_mon(int mon, int amount, bool by_you)
 {
     Mon *mptr;
     mptr = monsters + mon;
     if (amount >= mptr->hpcur)
     {
-        if (by_you)
-        {
-            notify_player_killed_mon(mon);
-            gain_experience(permons[mptr->mon_id].exp);
-        }
-        else if (mon_visible(mon))
-        {
-            notify_mon_dies(mon);
-        }
-        death_drop(mon);
-        lvl.set_mon_at(mptr->pos, NO_MON);
-        newsym(mptr->pos);
-        mptr->used = 0;
-        map_updated = 1;
+        kill_mon(mon, by_you);
         display_update();
     }
     else
@@ -386,6 +402,7 @@ int knockback_mon(int mon, Offset step, bool cansee, bool by_you)
     /* 0 = blocked, 1 = knocked, 2 = killed */
     Mon *mptr = monsters + mon;
     Coord c = mptr->pos + step;
+    Coord savedpos = mptr->pos;
     Terrain terr = lvl.terrain_at(c);
 
     if (mptr->resists(DT_KNOCKBACK))
@@ -404,6 +421,7 @@ int knockback_mon(int mon, Offset step, bool cansee, bool by_you)
         }
         return 0;
     }
+    reloc_mon(mon, c);
     switch (terr)
     {
     case LAVA:
@@ -426,7 +444,7 @@ int knockback_mon(int mon, Offset step, bool cansee, bool by_you)
             }
             if (!mptr->resists(DT_FIRE))
             {
-                damage_mon(mon, 9999, by_you);
+                kill_mon(mon, by_you);
                 return 2;
             }
         }
@@ -451,7 +469,7 @@ int knockback_mon(int mon, Offset step, bool cansee, bool by_you)
             }
             if (!mptr->resists(DT_DROWNING))
             {
-                damage_mon(mon, 9999, by_you);
+                kill_mon(mon, by_you);
                 return 2;
             }
         }
@@ -459,7 +477,6 @@ int knockback_mon(int mon, Offset step, bool cansee, bool by_you)
     default:
         break;
     }
-    reloc_mon(mon, c);
     return 1;
 }
 
@@ -467,10 +484,10 @@ void reloc_mon(int mon, Coord newpos)
 {
     Mon *mptr = monsters + mon;
     lvl.set_mon_at(mptr->pos, NO_MON);
-    newsym(mptr->pos);
+    notify_new_mon_at(mptr->pos, NO_MON);
     mptr->pos = newpos;
     lvl.set_mon_at(mptr->pos, mon);
-    newsym(mptr->pos);
+    notify_new_mon_at(mptr->pos, mon);
 }
 
 void move_mon(int mon, Coord c)
index 535df27..fc8fd38 100644 (file)
@@ -82,6 +82,8 @@ extern void reloc_mon(int mon, Coord c);
 extern Pass_fail teleport_mon(int mon);       /* Randomly relocate monster. */
 extern Pass_fail teleport_mon_to_you(int mon);        /* Relocate monster to your vicinity. */
 extern void heal_mon(int mon, int amount, int cansee);
+extern void kill_mon(int mon, bool by_you);
+extern void unplace_mon(int mon);
 
 /* XXX mon2.c data and funcs */
 extern void select_space(int *py, int *px, int dy, int dx, int selection_mode);
index 00b9f15..b9738c1 100644 (file)
@@ -385,8 +385,9 @@ void notify_player_hurt_mon(int mon, int damage)
 
 void notify_player_killed_mon(int mon)
 {
+    newsym(monsters[mon].pos);
     print_msg("You kill ");
-    if (mon_visible(mon))
+    if (occupant_visible(monsters[mon].pos))
     {
         print_mon_name(mon, 1);
     }
@@ -399,8 +400,12 @@ void notify_player_killed_mon(int mon)
 
 void notify_mon_dies(int mon)
 {
-    print_mon_name(mon, 2);
-    print_msg(" dies.\n");
+    newsym(monsters[mon].pos);
+    if (occupant_visible(monsters[mon].pos))
+    {
+        print_mon_name(mon, 2);
+        print_msg(" dies.\n");
+    }
 }
 
 void notify_knockback_mon_resisted(int mon)
@@ -915,6 +920,12 @@ void notify_pack_full(void)
 {
     print_msg(Msg_prio::Alert, "Your pack is full.\n");
 }
+
+void notify_new_mon_at(Coord c, int mon)
+{
+    newsym(c);
+}
+
 /* Debugging notifications */
 
 void debug_bad_monspell(int spell)
@@ -1077,5 +1088,15 @@ void debug_save_unlink_failed(void)
     print_msg(Msg_prio::Alert, "NOTICE: savefile unlink() failed - are you trying to savescum?\n");
 }
 
+void debug_attacking_with_wielded_nonweapon(void)
+{
+    print_msg(Msg_prio::Bug, "BUG: attacking with wielded nonweapon\n");
+}
+
+void debug_control_flow(char const *func, int line)
+{
+    print_msg(Msg_prio::Bug, "BUG: control flow passed through unexpected line %d in function %s\n", line, func);
+}
+
 /* notify-local-tty.cc */
 // vim:cindent
index e91b523..67766f5 100644 (file)
--- a/notify.hh
+++ b/notify.hh
@@ -133,6 +133,7 @@ void notify_mon_ranged_hit_player(int mon);
 void notify_mon_ranged_missed_player(int mon);
 void notify_mon_ranged_hit_mon(int er, int ee);
 void notify_mon_dies(int mon);
+void notify_new_mon_at(Coord c, int mon);
 
 void notify_player_damage_taken(int amount);
 void notify_player_touch_effect(Damtyp dt);
@@ -184,6 +185,10 @@ void debug_unimplemented_activation(int po);
 void debug_unimplemented_break_reaction(int po);
 void debug_unimplemented_radiance_order(int order);
 void debug_save_unlink_failed(void);
+void debug_attacking_with_wielded_nonweapon(void);
+void debug_control_flow(char const *func, int line);
+
+#define DEBUG_CONTROL_FLOW() debug_control_flow(__FUNCTION__, __LINE__)
 
 // Provisional object designs
 class Notify_uattkm
index b77d87d..985c8a0 100644 (file)
@@ -64,7 +64,12 @@ enum poclass_num {
 #define POF_STACKABLE 0x00000004u // item can stack
 #define POF_DAMAGEABLE 0x00000008u // track durability
 #define POF_BREAK_REACT 0x00000010u // item reacts to breakage attempts
+/* Yes, DRESS and MELEE_WEAPON have the same value. This is OK because
+ * only an ARMOUR can possibly be a DRESS, and only a WEAPON can possibly be
+ * a MELEE_WEAPON. */
 #define POF_DRESS 0x00010000u
+#define POF_MELEE_WEAPON 0x00010000u
+#define POF_RANGED_WEAPON 0x00020000u
 
 /*! \brief The 'permanent object' database */
 class Permobj {
index 17c2693..4d46938 100644 (file)
--- a/player.hh
+++ b/player.hh
@@ -76,6 +76,7 @@ public:
 #define SLOT_NOTHING (-2)
 
 /* XXX u.c data and funcs */
+extern Player u;
 extern void u_init(char const *name);
 extern void write_char_dump(void);
 extern int do_death(Death d, char const *what);
@@ -92,8 +93,9 @@ extern void reloc_player(Coord c);
 extern void recalc_defence(void);
 extern Pass_fail teleport_u(void);
 extern void update_player(void);
-
-extern Player u;
+inline bool empty_handed(void) { return u.weapon == NO_OBJ; }
+extern bool wielding_melee_weapon(void);
+extern bool wielding_ranged_weapon(void);
 
 #endif
 
index cf02534..a86e72d 100755 (executable)
--- a/pobj_comp
+++ b/pobj_comp
@@ -27,6 +27,8 @@ our %flag_indices =
     'DAMAGEABLE' => 0,
     'BREAK_REACT' => 0,
     'DRESS' => 0,
+    'RANGED_WEAPON' => 0,
+    'MELEE_WEAPON' => 0,
 );
 
 
diff --git a/u.cc b/u.cc
index 74ce705..63c029a 100644 (file)
--- a/u.cc
+++ b/u.cc
 #include <stdio.h>
 #include <deque>
 
+bool action_rewrite(Action const *act, Action *revised_act);
 struct Player u;
 
+bool wielding_ranged_weapon(void)
+{
+    return permobjs[objects[u.weapon].obj_id].flags[0] & POF_RANGED_WEAPON;
+}
+
+bool wielding_melee_weapon(void)
+{
+    return permobjs[objects[u.weapon].obj_id].flags[0] & POF_MELEE_WEAPON;
+}
+
 void recalc_defence(void)
 {
     int i;
@@ -534,7 +545,7 @@ std::deque<Action> past_actions;
 enum Combo_phase
 {
     Combo_invalid,
-    Combo_link,
+    Combo_valid,
     Combo_finisher
 };
 
@@ -551,40 +562,146 @@ enum Combo_phase
  * \return true if revised_act was written to; false otherwise
  */
 
-bool detect_combo(Action const *act, Action *revised_act)
+bool action_rewrite(Action const *act, Action *revised_act)
 {
-    //Combo_phase p;
-    // for now, we use if rather than switch because there are only two
-    // valid action types in a combo: move, and attack.
-    if (act->cmd == WALK)
+    Coord c;
+    Offset o;
+    Action tmp_act = *act;
+    Combo_phase p;
+    bool rewrite_flag;
+    switch (tmp_act.cmd)
     {
-        *revised_act = *act;
-        return true;
+    case STAND_STILL:
+        p = Combo_valid;
+        break;
+    case ATTACK:
+        o.y = tmp_act.details[0];
+        o.x = tmp_act.details[1];
+        c = u.pos + o;
+        if (!lvl.in_bounds(c))
+        {
+            debug_move_oob(c); // TODO notify_attack_oob()
+            tmp_act.cmd = REJECTED_ACTION;
+            rewrite_flag = true;
+            p = Combo_invalid;
+            break;
+        }
+        p = Combo_finisher;
+        break;
+    case WALK:
+        o.y = tmp_act.details[0];
+        o.x = tmp_act.details[1];
+        c = u.pos + o;
+        if (!lvl.in_bounds(c))
+        {
+            debug_move_oob(c);
+            tmp_act.cmd = REJECTED_ACTION;
+            rewrite_flag = true;
+            p = Combo_invalid;
+        }
+        else if (lvl.mon_at(c) != NO_MON)
+        {
+            tmp_act.cmd = ATTACK;
+            rewrite_flag = true;
+            p = Combo_finisher;
+        }
+        else
+        {
+            p = Combo_valid;
+        }
+        break;
+    default:
+        rewrite_flag = false;
+        p = Combo_invalid;
+        break;
     }
-    else if (act->cmd == ATTACK)
+    switch (p)
     {
-        *revised_act = *act;
-        return true;
+    case Combo_invalid:
+        break;
+    case Combo_valid:
+        if (past_actions.empty())
+        {
+            past_actions.push_front(tmp_act);
+        }
+        else
+        {
+            switch (tmp_act.cmd)
+            {
+            case STAND_STILL:
+                /* For now, no combo opens with more than one stand still,
+                 * and no combo starts with something else then chains through
+                 * a stand still */
+                if (past_actions.front().cmd != STAND_STILL)
+                {
+                    past_actions.clear();
+                    past_actions.push_front(tmp_act);
+                }
+                break;
+            case WALK:
+                /* For now, no combo chains through a WALK, but the charge
+                 * combo will when I have the mental effort to write it. */
+                past_actions.clear();
+                break;
+            default:
+                DEBUG_CONTROL_FLOW();
+                break;
+            }
+        }
+        break;
+    case Combo_finisher:
+        if (!past_actions.empty())
+        {
+            switch (past_actions.front().cmd)
+            {
+            case STAND_STILL:
+                if (empty_handed() || wielding_melee_weapon())
+                {
+                    rewrite_flag = true;
+                    tmp_act.cmd = CMD_POWER_ATTACK;
+                }
+                break;
+            case WALK:
+                break;
+            default:
+                DEBUG_CONTROL_FLOW();
+                break;
+            }
+        }
+        past_actions.clear();
+        break;
     }
-    else
+    if (rewrite_flag)
     {
-        return false;
+        *revised_act = tmp_act;
     }
+    return rewrite_flag;
 }
 
-/*! \brief Process a player action.  */
+/*! \brief Process a player action.
+ *
+ * \todo Make CMD_POWER_ATTACK do something special
+ */
 Action_cost do_player_action(Action *act)
 {
     int slot;
     Offset step;
     Action redact;
-    bool rewritten = detect_combo(act, &redact);
+    bool rewritten = action_rewrite(act, &redact);
     if (rewritten)
     {
         act = &redact;
     }
     switch (act->cmd)
     {
+    case REJECTED_ACTION:
+        return Cost_none;
+
+    case CMD_POWER_ATTACK:
+        step.y = act->details[0];
+        step.x = act->details[1];
+        return player_attack(step);
+
     case WALK:
         step.y = act->details[0];
         step.x = act->details[1];