All further fixes will have to come after the 7drlc is over.
GENERATED_OBJS:=permobj.o permons.o
GENERATED_SOURCE:=permobj.cc pobj_id.hh permons.cc pmon_id.hh
GENERATED_MAKES:=dirs.mk features.mk
-HANDWRITTEN_OBJS:=cave.o combat.o combo.o coord.o deeds.o display-nc.o dungeon.o fov.o log.o main.o map.o mon1.o mon2.o mon3.o notify-local-tty.o obj1.o obj2.o pmon2.o rng.o role.o shrine.o sorcery.o u.o util.o
+HANDWRITTEN_OBJS:=cave.o combat.o combo.o coord.o deeds.o display-nc.o dungeon.o fov.o log.o main.o map.o mon1.o mon2.o mon3.o notify-local-tty.o obj1.o obj2.o pmon2.o rng.o role.o shrine.o skills.o sorcery.o u.o util.o
OBJS:=$(GENERATED_OBJS) $(HANDWRITTEN_OBJS)
GAME:=obumbrata
MAJVERS:=1
role.o: $(srcdir)/role.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/monsters.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
shrine.o: $(srcdir)/shrine.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/objects.hh $(srcdir)/monsters.hh $(srcdir)/map.hh $(srcdir)/mapgen.hh
+
+skills.o: $(srcdir)/skills.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/monsters.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
+
sorcery.o: $(srcdir)/sorcery.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/sorcery.hh $(srcdir)/objects.hh $(srcdir)/monsters.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
u.o: $(srcdir)/u.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/monsters.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
/*! \brief Calculate the effect of the player's damage amplifier ring
*/
-bool ring_effectiveness(Mon_handle mon, int ring_pobj, int damage, int *bonus_damage, int *vamp_healing)
+bool damage_amplification(Mon_handle mon, int ring_pobj, int damage, int *bonus_damage, int *vamp_healing)
{
bool rv = false;
int pm = monsters[mon].pm_ref;
*vamp_healing = 0;
- // Testing order is fire, vampire, frost.
+ *bonus_damage = 0;
+ // Fire and frost mutually exclude.
if (u.damage_amp[DT_FIRE] && !pmon_resists_fire(pm))
{
- *bonus_damage = dice(2, 4) + ((damage + 1) / 2);
+ *bonus_damage += dice(2, 4) + ((damage + 1) / 2);
rv = true;
}
else if (u.damage_amp[DT_COLD] && !pmon_resists_cold(pm))
{
- *bonus_damage = dice(2, 4) + ((damage + 3) / 4);
+ *bonus_damage += dice(2, 4) + ((damage + 3) / 4);
rv = true;
}
- else if (u.damage_amp[DT_NECRO] && !pmon_resists_necro(pm))
+ if (u.damage_amp[DT_NECRO] && !pmon_resists_necro(pm))
{
- *bonus_damage = dice(2, 4) + ((damage + 3) / 4);
+ *bonus_damage += dice(2, 4) + ((damage + 3) / 4);
*vamp_healing = std::min(monsters[mon].hpcur, (damage + 5) / 6);
rv = true;
}
- else
+ // only one "slay" applies
+ if (u.damage_amp[DT_SLAY_DEMON] && pmon_is_demonic(pm))
+ {
+ *bonus_damage += damage;
+ }
+ else if (u.damage_amp[DT_SLAY_UNDEAD] && pmon_is_undead(pm))
{
- *bonus_damage = 0;
- *vamp_healing = 0;
+ *bonus_damage += damage;
}
return rv;
}
int healing = 0;
if (u.ring != NO_OBJ)
{
- ring_eff = ring_effectiveness(mon, objects[u.ring].po_ref, damage, &ring_bonus, &healing);
+ ring_eff = damage_amplification(mon, objects[u.ring].po_ref, damage, &ring_bonus, &healing);
if (ring_eff)
{
notify_ring_boost(mon, objects[u.ring].po_ref);
return Cost_std;
}
+Action_cost player_charge(Offset step, int power)
+{
+ Mon const *mp;
+ Coord c = u.pos + step;
+ int mon = lvl.mon_at(c);
+ int damage;
+ mp = mon_snap(mon);
+ notify_player_charge_mon(mon);
+ /* +25%, and a further +25% for each step in the charge */
+ damage = (player_melee_base() * (5 + power)) / 4;
+ resolve_player_melee(mon, damage);
+ return Cost_std; /* Hit. */
+}
+
int uhitm(Mon_handle mon)
{
- Mon *mp;
+ Mon const *mp;
int tohit;
int damage;
int hitbase = u.agility + u.level;
- mp = mon_snapv(mon);
+ mp = mon_snap(mon);
tohit = hitbase / 3 + zero_die(hitbase - hitbase / 3);
if (tohit < mp->defence)
{
#define agility_modifier() (u.withering ? (u.agility / 10) : (u.agility / 5))
/* XXX combat.c data and funcs */
-extern Action_cost player_attack(Offset step);
-extern Action_cost player_power_attack(Offset step);
-extern void resolve_player_melee(Mon_handle mon, int damage);
-extern int mhitu(Mon_handle mon, Damtyp dtyp);
-extern int uhitm(Mon_handle mon);
-extern int mshootu(Mon_handle mon);
-extern int ushootm(Offset step);
+Action_cost player_attack(Offset step);
+Action_cost player_power_attack(Offset step);
+Action_cost player_charge(Offset step, int power);
+void resolve_player_melee(Mon_handle mon, int damage);
+int mhitu(Mon_handle mon, Damtyp dtyp);
+int uhitm(Mon_handle mon);
+int mshootu(Mon_handle mon);
+int ushootm(Offset step);
class Combo_spec
{
static bool powatk_avail(Player const *player)
{
- if (player->level >= 2)
+ if (player->known_skill[Skill_power_attack])
{
- if (player->weapon != NO_OBJ)
+ Obj const *optr = obj_snap(player->weapon);
+ if (optr && (permobjs[optr->po_ref].flags[0] & POF_RANGED_WEAPON))
{
- Obj const *optr = obj_snap(player->weapon);
- if (permobjs[optr->po_ref].flags[0] & POF_RANGED_WEAPON)
- {
- return false;
- }
+ return false;
}
return true;
}
}
else
{
+ DEBUG_CONTROL_FLOW();
+ return Combo_state_none;
+ }
+}
+
+static bool blade_dance_avail(Player const *player)
+{
+ if (player->known_skill[Skill_blade_dance])
+ {
+ Obj const *optr = obj_snap(player->weapon);
+ if (optr)
+ {
+ if (po_is_sword(optr->po_ref) || po_is_dagger(optr->po_ref))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static Combo_state blade_dance_detector(std::deque<Action>* action_list)
+{
+ if ((*action_list)[0].cmd == WALK)
+ {
+ if (action_list->size() == 1)
+ {
+ Offset o = { (int32_t) (*action_list)[0].details[0], (int32_t) (*action_list)[0].details[1] };
+ if (myabs(o.y) == myabs(o.x))
+ {
+ Offset o1 = { o.y, 0 };
+ Offset o2 = { 0, o.x };
+ Coord c1 = u.pos + o1;
+ Coord c2 = u.pos + o2;
+ Mon_handle m1 = lvl.mon_at(c1);
+ Mon_handle m2 = lvl.mon_at(c2);
+ Coord target;
+ if ((m1 != NO_MON) && ((m2 == NO_MON) || (zero_die(2))))
+ {
+ target = c1;
+ }
+ else if (m2 != NO_MON)
+ {
+ target = c2;
+ }
+ else
+ {
+ return Combo_state_none;
+ }
+ action_list->resize(1);
+ (*action_list)[0].cmd = COMBO_BLADE_DANCE;
+ (*action_list)[0].details[0] = o.y;
+ (*action_list)[0].details[1] = o.x;
+ (*action_list)[0].details[2] = target.y;
+ (*action_list)[0].details[3] = target.x;
+ return Combo_state_complete;
+ }
+ }
+ return Combo_state_none;
+ }
+ else
+ {
+ DEBUG_CONTROL_FLOW();
+ return Combo_state_none;
+ }
+}
+
+static bool charge_avail(Player const *player)
+{
+ if (player->known_skill[Skill_charge])
+ {
+ Obj const *optr = obj_snap(player->weapon);
+ if (optr && (permobjs[optr->po_ref].flags[0] & POF_RANGED_WEAPON))
+ {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+static Combo_state charge_detector(std::deque<Action>* action_list)
+{
+ if ((*action_list)[0].cmd == WALK)
+ {
+ uint32_t size = action_list->size();
+ if (size > 1)
+ {
+ bool valid = true;
+ for (uint32_t i = 1; i < size; ++i)
+ {
+ if (i == (size - 1))
+ {
+ if (((*action_list)[size - 1].cmd == ATTACK) &&
+ ((*action_list)[size - 1].details[0] ==
+ (*action_list)[0].details[0]) &&
+ ((*action_list)[size - 1].details[1] ==
+ (*action_list)[0].details[1]))
+ {
+ (*action_list)[0].cmd = COMBO_CHARGE;
+ (*action_list)[0].details[2] = i;
+ action_list->resize(1);
+ return Combo_state_complete;
+ }
+ }
+ if ((*action_list)[i] != (*action_list)[0])
+ {
+ // Do we have a prefix consisting entirely of identical
+ // WALK actions?
+ valid = false;
+ }
+ }
+ if (!valid)
+ {
+ return Combo_state_none;
+ }
+ }
+ return Combo_state_partial;
+ }
+ else
+ {
+ DEBUG_CONTROL_FLOW();
+ return Combo_state_none;
+ }
+}
+
+static bool flying_leap_avail(Player const *player)
+{
+ if (player->known_skill[Skill_flying_leap])
+ {
+ if (u.armour == NO_OBJ)
+ {
+ return true;
+ }
+ Obj const *optr = obj_snap(u.armour);
+ if (po_is_dress(optr->po_ref) || po_is_leatherwear(optr->po_ref) ||
+ po_is_robe(optr->po_ref))
+ {
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
+static Combo_state flying_leap_detector(std::deque<Action>* action_list)
+{
+ if ((*action_list)[0].cmd == WALK)
+ {
+ uint32_t size = action_list->size();
+ if (size == 1)
+ {
+ Offset o = { (int32_t) (*action_list)[0].details[0], (int32_t) (*action_list)[0].details[1] };
+ Coord c = u.pos + o;
+ Mon_handle mon = lvl.mon_at(c);
+ if (mon != NO_MON)
+ {
+ return Combo_state_none;
+ }
+ Terrain t = lvl.terrain_at(c);
+ if (terrain_gapes(t) || terrain_drowns(t) || terrain_is_hot(t))
+ {
+ Coord c2 = c + o;
+ Terrain t2 = lvl.terrain_at(c2);
+ Mon_handle mon = lvl.mon_at(c2);
+ if (terrain_is_floor(t2) && (mon == NO_MON))
+ {
+ (*action_list)[0].cmd = COMBO_FLYING_LEAP;
+ (*action_list)[0].details[0] = o.y*2;
+ (*action_list)[0].details[1] = o.x*2;
+ return Combo_state_complete;
+ }
+ }
+ }
+ return Combo_state_none;
+ }
+ else
+ {
+ DEBUG_CONTROL_FLOW();
return Combo_state_none;
}
}
Combo_entry combo_entries[] =
{
- /* Everyone gets power attack at level 2. */
+ { "charge", WALK, charge_avail, charge_detector },
+ { "flying leap", WALK, flying_leap_avail, flying_leap_detector },
+ { "blade dance", WALK, blade_dance_avail, blade_dance_detector },
{ "power attack", STAND_STILL, powatk_avail, powatk_detector },
{ "empty fencepost entry", REJECTED_ACTION, nullptr, nullptr }
};
DT_NONE = -1,
DT_PHYS = 0, DT_COLD, DT_FIRE, DT_NECRO,
DT_ELEC, DT_HELLFIRE, DT_POISON,
- DT_KNOCKBACK, DT_DROWNING
+ DT_KNOCKBACK, DT_DROWNING,
+ // Now the special DTs for class bonuses
+ DT_SLAY_DEMON, DT_SLAY_UNDEAD
};
-#define LAST_DAMTYPE (DT_DROWNING)
+#define LAST_DAMTYPE (DT_SLAY_UNDEAD)
#define NUM_DAMTYPES (1 + LAST_DAMTYPE)
/*! \brief Identification code for player actions */
SAVE_GAME, QUIT,
WIZARD_LEVELUP, WIZARD_DESCEND,
/* combos begin here */
- COMBO_POWATK
+ COMBO_POWATK, COMBO_BLADE_DANCE, COMBO_CHARGE, COMBO_FLYING_LEAP
};
-#define LAST_COMMAND (COMBO_POWATK)
+#define LAST_COMMAND (COMBO_FLYING_LEAP)
#define NUM_COMMANDS (1 + LAST_COMMAND)
/*! \brief Is this action a valid combo step? */
#define TOTAL_FELL_POWERS (1 + FePo_flesh)
#define BONUS_MASK_TEMPORARY 0x0000FFFFu
-#define BONUS_MASK_PERM_EQUIP 0xFFFF0000u
+#define BONUS_MASK_PERM_EQUIP 0x7FFF0000u
+#define BONUS_MASK_PERM_SKILL 0x80000000u
#define BONUS_RING 0x00010000u
#define BONUS_ARMOUR 0x00020000u
#define BONUS_WEAPON 0x00040000u
{
Game_cmd cmd;
uint32_t details[8];
+ bool operator ==(Action const& right)
+ {
+ if (cmd != right.cmd) return false;
+ for (int i = 0; i < 8; ++i)
+ {
+ if (details[i] != right.details[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ bool operator !=(Action const& right)
+ {
+ if (cmd != right.cmd) return true;
+ for (int i = 0; i < 8; ++i)
+ {
+ if (details[i] != right.details[i])
+ {
+ return true;
+ }
+ }
+ return false;
+ }
};
class Level;
NO_DECAL = -1,
Decal_blood,
Decal_ichor,
+ Decal_slime,
Decal_pus
};
#define MAX_DECAL (Decal_pus)
return player_power_attack(step);
}
+static Action_cost deed_combo_blade_dance(Action const *act)
+{
+ Offset step = { (int32_t) act->details[0], (int32_t) act->details[1] };
+ Mon_handle mon = (Mon_handle) act->details[2];
+ Action_cost ac = move_player(step);
+ if (ac == Cost_std)
+ {
+ Mon const *mptr = mon_snap(mon);
+ Offset atkstep = mptr->pos - u.pos;
+ player_attack(atkstep);
+ }
+ return ac;
+}
+
+static Action_cost deed_combo_charge(Action const *act)
+{
+ Offset step = { (int32_t) act->details[0], (int32_t) act->details[1] };
+ int power = std::min((int) act->details[2], 4);
+ return player_charge(step, power);
+}
+
+static Action_cost deed_combo_flying_leap(Action const *act)
+{
+ Offset step = { (int32_t) act->details[0], (int32_t) act->details[1] };
+ notify_flying_leap();
+ reloc_player(u.pos + step);
+ return Cost_std;
+}
+
static Action_cost deed_wiz_descend(Action const *act)
{
if (wizard_mode)
deed_quit,
deed_wiz_levup,
deed_wiz_descend,
- deed_combo_powatk
+ deed_combo_powatk,
+ deed_combo_blade_dance,
+ deed_combo_charge,
+ deed_combo_flying_leap,
};
/* deeds.cc */
DEPTH 1
DAMAGEABLE
MELEE_WEAPON
+DAGGER
WEAPON long sword
PLURAL long swords
DEPTH 4
DAMAGEABLE
MELEE_WEAPON
+SWORD
WEAPON windsword
PLURAL windswords
MELEE_WEAPON
NOTIFY_EQUIP
FLIGHT
+SWORD
WEAPON runesword
PLURAL runeswords
-DESC An eerily glowing sword engraved with many strange runes.
+DESC An eerily glowing long sword engraved with many strange runes.
RARITY 80
ASCII ')'
UTF8 ")"
DEPTH 12
DAMAGEABLE
MELEE_WEAPON
+SWORD
WEAPON mace
PLURAL maces
DEPTH 2
DAMAGEABLE
MELEE_WEAPON
+BLUDGEON
WEAPON spear
PLURAL spears
DEPTH 3
DAMAGEABLE
MELEE_WEAPON
+POLEARM
WEAPON hellglaive
PLURAL hellglaives
BREAK_REACT
MELEE_WEAPON
DEMONIC
+POLEARM
WEAPON plague scythe
PLURAL plague sycthes
NOTIFY_EQUIP
MELEE_WEAPON
DEMONIC
+POLEARM
WEAPON tormentor's lash
PLURAL tormentor's lashes
BREAK_REACT
MELEE_WEAPON
DEMONIC
+WHIP
WEAPON death staff
PLURAL death staves
DEPTH 30
NOTIFY_EQUIP
MELEE_WEAPON
+BLUDGEON
WEAPON staff of fire
PLURAL staves of fire
DAMAGEABLE
ACTIVATABLE
MELEE_WEAPON
+BLUDGEON
WEAPON bow
PLURAL bows
DEPTH 1
DAMAGEABLE
RANGED_WEAPON
+
WEAPON crossbow
PLURAL crossbows
DESC A crossbow.
POWER2 10
DEPTH 1
DAMAGEABLE
+LEATHERY
ARMOUR chainmail
PLURAL suits of chainmail
POWER2 5
DEPTH 1
DAMAGEABLE
+ROBE
ARMOUR robe of swiftness
PLURAL robes of swiftness
DEPTH 8
DAMAGEABLE
SPEED
+ROBE
ARMOUR robe of shadows
PLURAL robes of shadows
POWER2 -15
DEPTH 18
DAMAGEABLE
+ROBE
ARMOUR dragonhide armour
PLURAL suits of dragonhide armour
DEPTH 21
DAMAGEABLE
RES_FIRE
+LEATHERY
ARMOUR meteoric plate armour
PLURAL suits of meteoric plate armour
BREAK_REACT
RES_POISON
DEMONIC
+ROBE
ARMOUR lich's robe
PLURAL lich's robes
DEPTH 30
NOTIFY_EQUIP
RES_NECRO
+ROBE
ARMOUR infernite armour
PLURAL suits of infernite armour
}
}
+static char const *tips[] = {
+ "Pay attention to your hit points.",
+ "Magic rings never wear out; don't bother carrying duplicates.",
+ "Demon hunters cannot use demonic equipment; it is unclean.",
+ "Chasms, lava, and water can all be crossed using suitable gear.",
+ "Dying with a healing potion in your inventory is embarrassing.",
+ "Fire scrolls are a powerful weapon if you resist fire.",
+ "Corpses are not a food source.",
+ "Corpses blessed by Death do not rise again to trouble the living.",
+ "The PRNG is a dispassionate algorithm, incapable of love or hate.",
+ "Life is not fair, and neither is Obumbrata et Velata.",
+ "Most weapons and armour are consumable resources.",
+ "More tips wanted. Send suggestions to the author, please."
+};
+
+int tip_pool_size = (sizeof tips) / (sizeof tips[0]);
+
+static void draw_mainmenu_tip(void)
+{
+ char const *tip = tips[zero_die(tip_pool_size)];
+ mvwprintw(fullscreen_window, 19, 1, "\n");
+ mvwprintw(fullscreen_window, 19, 31, "Tip of the moment:\n");
+ mvwprintw(fullscreen_window, 21, 1, "\n");
+ mvwprintw(fullscreen_window, 21, (40 - strlen(tip) / 2), "%s\n", tip);
+}
+
/*! \brief Draw the main menu. */
static void draw_main_menu(void)
{
wclear(fullscreen_window);
mvwprintw(fullscreen_window, 1, 20, "----====< Obumbrata et Velata >====----\n");
- mvwprintw(fullscreen_window, 3, 25, "A roguelike game by Martin Read\n");
+ mvwprintw(fullscreen_window, 5, 24, "A roguelike game by Martin Read\n");
mvwprintw(fullscreen_window, 10, 25, "S)tart new game\n");
mvwprintw(fullscreen_window, 11, 25, "R)esume existing game\n");
mvwprintw(fullscreen_window, 12, 25, "Q)uit\n");
- mvwprintw(fullscreen_window, 23, 25, "Version %d.%d.%d\n", MAJVERS, MINVERS, PATCHVERS);
+ draw_mainmenu_tip();
+ mvwprintw(fullscreen_window, 3, 23, "Version %d.%d.%d (7DRLC 2014 Edition)\n", MAJVERS, MINVERS, PATCHVERS);
show_panel(fullscreen_panel);
update_panels();
doupdate();
return;
}
break;
+ case '\\':
+ print_msg("You have the following skills:\n");
+ for (int i = 0; i < NUM_SKILLS; ++i)
+ {
+ if (u.known_skill[i])
+ {
+ print_msg(" %s\n", skill_props[i].name);
+ }
+ }
+ break;
#ifdef DEBUG_MAPDUMP
case 'M':
{
print_msg("i show your inventory\n");
print_msg("I examine an item you are carrying\n");
print_msg("# show underlying terrain of occupied squares\n");
- print_msg("\\ list all recognised items\n");
+ print_msg("\\ list your character's known skills \n");
print_msg("@ dump your character's details to <name>.dump\n");
print_msg("? print this message\n");
print_msg("\nPress any key to continue...\n");
mvwprintw(fullscreen_window, 10, 1, "\n");
mvwprintw(fullscreen_window, 11, 1, "\n");
mvwprintw(fullscreen_window, 12, 1, "\n");
- mvwprintw(fullscreen_window, 13, 19, "Welcome. Remind me of thy name?\n");
+ mvwprintw(fullscreen_window, 13, 24, "Welcome. Remind me of thy name?\n");
mvwprintw(fullscreen_window, 14, 1, "\n");
mvwprintw(fullscreen_window, 15, 1, "\n");
mvwprintw(fullscreen_window, 16, 1, "\n");
mvwprintw(fullscreen_window, 16, 25, "(P)rincess\n");
mvwprintw(fullscreen_window, 17, 25, "(D)emon Hunter\n");
mvwprintw(fullscreen_window, 18, 25, "(T)hanatophile\n");
+ mvwprintw(fullscreen_window, 19, 1, "\n");
+ mvwprintw(fullscreen_window, 21, 1, "\n");
role_selected = false;
do
{
case 'Q':
endwin();
exit(0);
+ default:
+ break;
}
+ draw_mainmenu_tip();
} while (!done);
if (!game_finished)
{
char *s;
asprint_obj_name(&s, obj);
print_msg("%s", s);
+ free(s);
}
void print_mon_name(Mon_handle mon, int article)
fwrite(&tmp, 1, sizeof tmp, fp);
}
+static inline void deserialize_bool(FILE *fp, bool *b)
+{
+ char tmp;
+ wrapped_fread(&tmp, 1, 1, fp);
+ *b = !!tmp;
+}
+
+static inline void serialize_bool(FILE *fp, bool b)
+{
+ char tmp = b;
+ fwrite(&tmp, 1, 1, fp);
+}
+
/*! \brief Read a Coord */
static inline void deserialize_coord(FILE *fp, Coord * c)
{
deserialize_uint32(fp, &(player->passwater));
deserialize_uint32(fp, &(player->flight));
deserialize_uint32(fp, &(player->protective_gear));
+ for (int i = 0; i < NUM_SKILLS; ++i)
+ {
+ deserialize_bool(fp, &(player->known_skill[i]));
+ }
for (int i = 0; i < INVENTORY_SIZE; ++i)
{
deserialize_objhandle(fp, &(player->inventory[i]));
serialize_uint32(fp, player.passwater);
serialize_uint32(fp, player.flight);
serialize_uint32(fp, player.protective_gear);
+ for (int i = 0; i < NUM_SKILLS; ++i)
+ {
+ serialize_bool(fp, player.known_skill[i]);
+ }
for (int i = 0; i < INVENTORY_SIZE; ++i)
{
serialize_objhandle(fp, player.inventory[i]);
Level lvl;
int depth;
+Decal_desc decal_props[NUM_DECALS] =
+{
+ { "blood", "Blood, presumably from a mortal creature.", Gcol_blood },
+ { "demonic ichor", "The uncanny, faintly glowing blue ichor of a demon.", Gcol_l_blue },
+ { "slime", "Unpleasant sticky green goo.", Gcol_green },
+ { "pus", "Sickly yellow gunk from an infected creature.", Gcol_blood },
+};
+
void drop_all_chunks(Level *l)
{
uint32_t i;
//! Array of terrain properties
extern Terrain_props terrain_props[NUM_TERRAINS];
+struct Decal_desc
+{
+ char const *name;
+ char const *description;
+ Gamecolour colour;
+};
+
+extern Decal_desc decal_props[NUM_DECALS];
+
#define NO_STAIRS (-1)
#define TFLAG_opaque 0x00000001u
Mon *mptr = mon_snapv(mon);
Terrain t = lvl.terrain_at(mptr->pos);
int pm = mptr->pm_ref;
+ bool detonate =
+ (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse));
if (!terrain_blocks_items(t))
{
apply_liquid(pm, mptr->pos);
}
- if (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse))
+ if (detonate)
{
detonate_mon(mon);
}
notify_player_killed_mon(mon);
gain_experience(permons[monsters[mon].pm_ref].exp);
}
- else if (mon_visible(mon))
+ else if (mon_visible(mon) && !detonate)
{
notify_mon_dies(mon);
}
Coord c;
int pm = mptr->pm_ref;
Decal_tag decal = pmon_fluid_decal(pm);
+ notify_mon_detonates(mon, decal);
if (decal != NO_DECAL)
{
for (c.y = mptr->pos.y - 1; c.y <= mptr->pos.y + 1; ++c.y)
}
}
+void notify_learned_skill(Skill_id skill)
+{
+ print_msg("You learned a new skill: %s\n", skill_props[skill].name);
+ print_msg("%s\n", skill_props[skill].desc);
+}
+
void notify_leadfoot_recovered(void)
{
print_msg("You shed your feet of lead.\n");
void notify_obj_at(Coord c)
{
- print_msg("You see here ");
- print_obj_name(lvl.obj_at(c));
- print_msg(".\n");
+ char *s;
+ asprint_obj_name(&s, lvl.obj_at(c));
+ print_msg("You see here %s.\n", s);
+ free(s);
}
void debug_move_oob(Coord c)
void notify_summon_help(Mon_handle mon, bool success)
{
/* Do the summoning... */
- print_mon_name(mon, 3);
- print_msg(" calls for help...\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s calls for help...\n", s);
if (success)
{
print_msg("... and gets it.\n");
{
print_msg("... luckily for you, help wasn't listening.\n");
}
+ free(s);
}
void notify_summon_demon(Mon_handle mon)
void notify_mon_disappear(Mon_handle mon)
{
- print_mon_name(mon, 3);
+ char *s;
+ asprint_mon_name(&s, mon, 3);
print_msg(" vanishes in a puff of smoke.\n");
}
void notify_monster_cursing(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" points at you and curses horribly.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s points at you and curses horribly.\n", s);
+ free(s);
}
void notify_no_attackee(void)
void notify_player_miss(Mon_handle mon)
{
- print_msg("You miss ");
- print_mon_name(mon, 1);
- print_msg(".\n");
+ char *s;
+ asprint_mon_name(&s, mon, 1);
+ print_msg("You miss %s.\n", s);
+ free(s);
+}
+
+void notify_player_charge_mon(Mon_handle mon)
+{
+ char *s;
+ asprint_mon_name(&s, mon, 1);
+ print_msg("You charge %s.\n", s);
+ free(s);
}
void notify_player_hit_mon(Mon_handle mon)
{
- print_msg("You hit ");
- print_mon_name(mon, 1);
- print_msg(".\n");
+ char *s;
+ asprint_mon_name(&s, mon, 1);
+ print_msg("You hit %s.\n", s);
+ free(s);
}
void notify_player_combo_powatk(Mon_handle mon)
{
- print_msg("You deal a powerful blow to ");
- print_mon_name(mon, 1);
- print_msg(".\n");
+ char *s;
+ asprint_mon_name(&s, mon, 1);
+ print_msg("You deal a powerful blow to %s.\n", s);
}
void notify_point_blank_warning(void)
void notify_ring_boost(Mon_handle mon, int pobj)
{
+ char *s;
+ asprint_mon_name(&s, mon, 1);
switch (pobj)
{
case PO_FIRE_RING:
- print_msg("Your ring burns ");
- print_mon_name(mon, 1);
- print_msg("!\n");
+ print_msg("Your ring burns %s!\n", s);
break;
case PO_VAMPIRE_RING:
- print_msg("Your ring drains ");
- print_mon_name(mon, 1);
- print_msg("!\n");
+ print_msg("Your ring drains %s!\n", s);
break;
case PO_FROST_RING:
- print_msg("Your ring freezes ");
- print_mon_name(mon, 1);
- print_msg("!\n");
+ print_msg("Your ring freezes %s!\n", s);
break;
}
+ free(s);
+}
+
+void notify_flying_leap(void)
+{
+ print_msg("You vault through the air.\n");
}
+
void notify_player_hurt_mon(Mon_handle mon, int damage)
{
print_msg("You do %d damage.\n", damage);
void notify_player_killed_mon(Mon_handle mon)
{
newsym(monsters[mon].pos);
- print_msg("You kill ");
if (occupant_visible(monsters[mon].pos))
{
- print_mon_name(mon, 1);
+ char *s;
+ asprint_mon_name(&s, mon, 1);
+ print_msg("You kill %s!\n", s);
+ free(s);
}
else
{
- print_msg("something");
+ print_msg("You kill something...\n");
}
- print_msg("!\n");
}
void notify_mon_dies(Mon_handle mon)
newsym(monsters[mon].pos);
if (occupant_visible(monsters[mon].pos))
{
- print_mon_name(mon, 2);
- print_msg(" dies.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 2);
+ print_msg("%s dies.\n", &s);
+ free(s);
+ }
+}
+
+void notify_mon_detonates(Mon_handle mon, Decal_tag tag)
+{
+ newsym(monsters[mon].pos);
+ if (occupant_visible(monsters[mon].pos))
+ {
+ char *s;
+ asprint_mon_name(&s, mon, 2);
+ if (tag != NO_DECAL)
+ {
+ print_msg("%s explodes in a welter of %s!\n", s, decal_props[tag]);
+
+ }
+ else
+ {
+ print_msg("%s explodes!\n", s);
+ }
+ free(s);
}
}
void notify_knockback_mon_resisted(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" wobbles slightly.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s wobbles slightly.\n", s);
+ free(s);
}
void notify_knockback_mon_blocked(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" is slammed against the wall.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s is slammed against the wall.\n", s);
+ free(s);
}
void notify_knockback_mon_hover_lava(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" is hurled out over the lava.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s is hurled out over the lava.\n", s);
+ free(s);
}
void notify_knockback_mon_hover_water(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" is hurled out over the water.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s is hurled out over the water.\n", s);
+ free(s);
}
void notify_knockback_mon_immersed_lava(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" tumbles into a pool of molten rock.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s tumbles into a pool of molten rock.\n", s);
+ free(s);
}
void notify_knockback_mon_immersed_water(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" tumbles into the water.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s tumbles into the water.\n", s);
+ free(s);
}
void notify_knockback_mon_lava_offscreen(void)
void notify_mon_appears(Mon_handle mon)
{
- print_mon_name(mon, 2);
- print_msg(" appears in a puff of smoke.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 2);
+ print_msg("%s appears in a puff of smoke.\n", s);
+ free(s);
}
void notify_mon_hit_armour(Mon_handle mon)
{
- print_msg("Your armour deflects ");
- print_mon_name(mon, 1);
- print_msg("'s blow.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 1);
+ print_msg("Your armour deflects %s's blow.\n", s);
+ free(s);
}
void notify_mon_missed_player(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" misses you.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s misses you.\n", s);
+ free(s);
}
void notify_mon_hit_player(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" hits you.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s hits you.\n", s);
+ free(s);
}
void notify_mon_ranged_attack(Mon_handle mon)
{
int pm = monsters[mon].pm_ref;
Damtyp dt = permons[pm].rdtyp;
- print_mon_name(mon, 3);
+ char *s;
+ asprint_mon_name(&s, mon, 3);
if (dt == DT_PHYS)
{
- print_msg(" %s at you!\n", permons[pm].shootverb);
+ print_msg("%s %s at you!\n", s, permons[pm].shootverb);
}
else
{
- print_msg(" %s %s at you!\n", permons[pm].shootverb, damtype_names[dt]);
+ print_msg("%s %s %s at you!\n", s, permons[pm].shootverb, damtype_names[dt]);
}
+ free(s);
}
void notify_mon_ranged_hit_mon(int er, int ee)
}
else
{
- print_msg("Wearing ");
- print_obj_name(u.armour);
- print_msg(".\n");
+ char *s;
+ asprint_obj_name(&s, u.armour);
+ print_msg("Wearing %s.\n", s);
+ free(s);
}
}
void notify_armour_unequip(Obj_handle obj)
{
Permobj *pobj = permobjs + objects[obj].po_ref;
- // TODO add unequip messages for NOTIFY_EQUIP armours.
if (pobj->flags[0] & POF_NOTIFY_EQUIP)
{
switch (objects[obj].po_ref)
void notify_ring_equip(Obj_handle obj)
{
- print_msg("You put on ");
- print_obj_name(obj);
- print_msg(".\n");
+ char *s;
+ asprint_obj_name(&s, obj);
+ print_msg("You put on %s.\n", s);
+ free(s);
}
void notify_ring_unequip(Obj_handle obj)
print_msg(Msg_prio::Warn, "Setting that item aside here would send you plummetting to your death.\n");
}
+void notify_role_blocks_equip(void)
+{
+ switch (u.role)
+ {
+ case Role_demon_hunter:
+ print_msg(Msg_prio::Alert, "Unclean! Unclean! Cast this filth aside!\n");
+ break;
+ default:
+ print_msg(Msg_prio::Alert, "Your chosen path in life precludes the use of that item.\n");
+ break;
+ }
+}
+
void notify_player_touch_effect(Damtyp dt)
{
switch (dt)
void notify_inferno_hit(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" is engulfed in roaring flames.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s is engulfed in roaring flames.\n", s);
+ free(s);
}
void notify_lash_activation(int specific)
void notify_mon_healed(Mon_handle mon)
{
- print_mon_name(mon, 3);
- print_msg(" looks healthier.\n");
+ char *s;
+ asprint_mon_name(&s, mon, 3);
+ print_msg("%s looks healthier.\n", s);
+ free(s);
}
void notify_mon_regenerates(Mon_handle mon)
}
else
{
- print_msg("Wielding ");
- print_obj_name(obj);
- print_msg(".\n");
+ char *s;
+ asprint_obj_name(&s, obj);
+ print_msg("Wielding %s.\n", s);
+ free(s);
}
}
void notify_drop_item(Obj_handle obj)
{
- print_msg("You drop ");
- print_obj_name(obj);
- print_msg(".\n");
+ char *s;
+ asprint_obj_name(&s, obj);
+ print_msg("You drop %s.\n", s);
+ free(s);
}
void notify_drop_blocked(void)
void notify_wasted_gain(void);
void notify_defence_recalc(void);
void notify_food_use(int food_use, int hunger_severity);
+void notify_learned_skill(Skill_id skill);
// Resistance notifications
void notify_blocked_water(Terrain t);
void notify_cant_go(void);
void notify_portal_underfoot(void);
+void notify_flying_leap(void);
// Unsorted notifications
void notify_new_obj_at(Coord c, Obj_handle obj);
void notify_nothing_to_get(void);
void notify_unwield(Obj_handle obj, Noisiness noisy);
void notify_wield_weapon(Obj_handle obj);
+void notify_role_blocks_equip(void);
void notify_weapon_broke(Obj_handle obj);
void notify_armour_broke(Obj_handle obj);
void notify_drop_item(Obj_handle obj);
void notify_player_miss(Mon_handle mon);
void notify_ring_boost(Mon_handle mon, int pobj);
void notify_player_hit_mon(Mon_handle mon);
+void notify_player_charge_mon(Mon_handle mon);
void notify_player_hurt_mon(Mon_handle mon, int damage);
void notify_player_killed_mon(Mon_handle mon);
void notify_player_combo_powatk(Mon_handle mon);
void notify_mon_ranged_missed_player(Mon_handle mon);
void notify_mon_ranged_hit_mon(int er, int ee);
void notify_mon_dies(Mon_handle mon);
+void notify_mon_detonates(Mon_handle mon, Decal_tag decal);
void notify_new_mon_at(Coord c, Mon_handle mon);
void notify_player_damage_taken(int amount);
return DT_NONE;
}
+bool po_is_sword(int po)
+{
+ return (permobjs[po].flags[0] & POF_SWORD);
+}
+
+bool po_is_dagger(int po)
+{
+ return (permobjs[po].flags[0] & POF_DAGGER);
+}
+
+bool po_is_polearm(int po)
+{
+ return (permobjs[po].flags[0] & POF_POLEARM);
+}
+
+bool po_is_bludgeon(int po)
+{
+ return (permobjs[po].flags[0] & POF_BLUDGEON);
+}
+
+bool po_is_demonic(int po)
+{
+ return (permobjs[po].flags[0] & POF_DEMONIC);
+}
+
+bool po_is_dress(int po)
+{
+ return (permobjs[po].flags[0] & POF_DRESS);
+}
+
+bool po_is_leatherwear(int po)
+{
+ return (permobjs[po].flags[0] & POF_LEATHERY);
+}
+
+bool po_is_robe(int po)
+{
+ return (permobjs[po].flags[0] & POF_ROBE);
+}
+
bool po_is_stackable(int po)
{
return (permobjs[po].flags[0] & POF_STACKABLE);
int saved_weapon = u.weapon;
if (u.weapon == NO_OBJ)
{
- debug_unwield_nothing();
- return Cost_none;
+ debug_unwield_nothing();
+ return Cost_none;
}
u.weapon = NO_OBJ;
recalc_defence();
return Cost_std;
}
+Pass_fail role_equip_check(Obj_handle obj, Noisiness noisy)
+{
+ Obj const *optr = obj_snap(obj);
+ if ((u.role == Role_demon_hunter) && po_is_demonic(optr->po_ref))
+ {
+ notify_role_blocks_equip();
+ return You_fail;
+ }
+ return You_pass;
+}
+
Action_cost player_wield(Obj_handle obj, Noisiness noisy)
{
- if (u.weapon != NO_OBJ)
+ Pass_fail rej = role_equip_check(obj, noisy);
+ if (rej == You_pass)
{
- player_unwield(Noise_low);
+ if (u.weapon != NO_OBJ)
+ {
+ player_unwield(Noise_low);
+ }
+ u.weapon = obj;
+ notify_wield_weapon(u.weapon);
+ recalc_defence();
+ return Cost_std;
+ }
+ else
+ {
+ return Cost_none;
}
- u.weapon = obj;
- notify_wield_weapon(u.weapon);
- recalc_defence();
- return Cost_std;
}
Action_cost wear_armour(Obj_handle obj)
return You_pass;
}
+bool corpse_is_blessed(Obj const *optr)
+{
+ return (optr && (optr->po_ref == PO_CORPSE) && (optr->meta[1] == 1));
+}
+
/* objects.cc */
// vim:cindent
Obj_handle create_obj_class_near(enum poclass_num pocl, int quantity, bool with_you, Coord c);
void damage_obj(Obj_handle obj);
int evasion_penalty(Obj_handle obj);
+bool corpse_is_blessed(Obj const *optr);
inline Obj *obj_snapv(Obj_handle obj)
{
#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_ROBE 0x00020000u
+#define POF_LEATHERY 0x00040000u
#define POF_MELEE_WEAPON 0x00010000u
#define POF_RANGED_WEAPON 0x00020000u
+#define POF_SWORD 0x00040000u
+#define POF_POLEARM 0x00080000u
+#define POF_BLUDGEON 0x00100000u
+#define POF_DAGGER 0x00200000u
+#define POF_WHIP 0x00400000u
#define POF_DEMONIC 0x10000000u
// POF field 1
extern Permobj permobjs[];
bool po_is_stackable(int po);
+bool po_is_dress(int po);
+bool po_is_leatherwear(int po);
+bool po_is_robe(int po);
+bool po_is_sword(int po);
+bool po_is_dagger(int po);
+bool po_is_polearm(int po);
+bool po_is_bludgeon(int po);
+bool po_is_demonic(int po);
Damtyp po_resistance(int po);
Damtyp po_damage_amp(int po);
bool po_grants_flight(int po);
enum Skill_id
{
- Skill_power_attack,
+ NO_SKILL = -1,
+ Skill_power_attack = 0,
/* Princess skills */
- Skill_artificer,
+ Skill_blood_royal,
Skill_flying_leap,
+ Skill_regal_radiance,
/* Demon Hunter skills */
Skill_charge,
Skill_blade_dance,
Skill_demon_slayer,
/* Thanatophile skills */
Skill_deaths_grace,
- Skill_deaths_vengeance
+ Skill_deaths_vengeance,
+ Skill_sanctified_by_death
};
-#define NO_SKILL (-1)
-#define LAST_SKILL (Skill_deaths_vengeance)
+#define LAST_SKILL (Skill_sanctified_by_death)
#define NUM_SKILLS (1 + LAST_SKILL)
/*! \brief Skill descriptor */
struct Skill_desc
{
char const *name;
- char const *description;
+ char const *desc;
};
extern Skill_desc skill_props[NUM_SKILLS];
+struct Skill_gain_table
+{
+ int level;
+ Skill_id skill;
+};
+
+extern Skill_gain_table universal_skills[];
+extern Skill_gain_table *skill_tables[NUM_ROLES];
+
/*! \brief Internal representation of the player character
*/
#define INVENTORY_SIZE 19
Obj_handle armour; //!< currently equipped armour.
Obj_handle ring; //!< currently equipped ring.
int sympathy[TOTAL_FELL_POWERS]; //!< Level of alignment with fell powers
+ bool known_skill[NUM_SKILLS]; //!<
/* Methods only after here plzkthx */
bool resists(Damtyp dtype) const; //!< Does player resist this Damtyp?
bool martial(void) const //!< Is player significantly influenced by iron?
return !!(permons[pm].flags[0] & PMF_UNDEAD);
}
+bool pmon_is_demonic(int pm)
+{
+ return !!(permons[pm].flags[0] & PMF_DEMONIC);
+}
+
bool pmon_is_stupid(int pm)
{
return !!(permons[pm].flags[0] & PMF_STUPID);
'DRESS' => 0,
'RANGED_WEAPON' => 0,
'MELEE_WEAPON' => 0,
+ 'SWORD' => 0,
+ 'DAGGER' => 0,
+ 'BLUDGEON' => 0,
+ 'POLEARM' => 0,
+ 'WHIP' => 0,
'DEMONIC' => 0,
+ 'ROBE' => 0,
+ 'LEATHERY' => 0,
'RES_FIRE' => 1,
'RES_COLD' => 1,
'RES_ELEC' => 1,
static void thanatophile_inventory(Player *p)
{
- p->inventory[0] = create_obj(PO_MACE, 1, true, Nowhere);
+ p->inventory[0] = create_obj(PO_DAGGER, 1, true, Nowhere);
p->inventory[1] = create_obj(PO_IRON_RATION, 1, true, Nowhere);
p->inventory[2] = create_obj(PO_LEATHER_ARMOUR, 1, true, Nowhere);
p->weapon = p->inventory[0];
"normal."),
},
{
+ "Blood Royal",
+ ("The stress of your banishment has partially unlocked the power of "
+ "your bloodline, allowing you to make full use of certain magical "
+ "items that would be ineffective, or less effective, for others."),
+ },
+ {
"Flying Leap",
("While wearing a dress, robe, or leather armour, you can jump across "
"any one-square environmental hazard (lava, chasms, etc.) that has "
- "clear space on the other side by attempting to move onto it.");
+ "clear space on the other side by attempting to move onto it. This "
+ "ability has no effect if you have equipped an item allowing you to "
+ "fly."),
+ },
+ {
+ "Regal Radiance",
+ ("If you are wearing an imperatrix gown, you can use the talismans "
+ "embroidered into it to stun nearby enemies for five turns using "
+ "the (E)manate command, at a cost of 100 points of food. Stunned "
+ "enemies cannot take any action against you."),
},
{
"Charge",
},
{
"Death's Grace",
- ("Beloved Death has granted you partial protection from the malign "
- "arts of necromancers. You take greatly reduced damage from "
- "necromantic magic."),
+ ("Beloved Death has granted you protection from the malign arts of "
+ "necromancers, making you immune to the necromantic damage type. "
+ "This does not make you immune to effects such as the 'leadfoot' "
+ "curse."),
},
{
"Death's Vengeance",
("Your melee attacks are imbued with the blessing of beloved Death, "
"causing them to do double damage against the undead."),
},
+ {
+ "Sanctified by Death",
+ ("Each turn, all corpses adjacent to you are purified by beloved "
+ "Death's holy touch, making them impossible to reanimate as zombies. "
+ "This ability has no effect on existing zombies."),
+ },
+};
+
+Skill_gain_table universal_skills[] = {
+ { 2, Skill_power_attack },
+ { 0, NO_SKILL }
+};
+
+static Skill_gain_table princess_skills[] = {
+ { 2, Skill_flying_leap },
+ //{ 4, Skill_blood_royal },
+ //{ 10, Skill_regal_radiance },
+ { 0, NO_SKILL }
+};
+
+static Skill_gain_table demon_hunter_skills[] = {
+ { 2, Skill_charge },
+ { 4, Skill_demon_slayer },
+ { 6, Skill_blade_dance },
+ { 0, NO_SKILL }
+};
+
+static Skill_gain_table thanatophile_skills[] = {
+ { 2, Skill_deaths_grace },
+ { 4, Skill_deaths_vengeance },
+ { 6, Skill_sanctified_by_death },
+ { 0, NO_SKILL }
+};
+
+Skill_gain_table *skill_tables[NUM_ROLES] = {
+ princess_skills,
+ demon_hunter_skills,
+ thanatophile_skills
};
/* skills.cc */
#include "obumbrata.hh"
#include "sorcery.hh"
#include "monsters.hh"
+#include "objects.hh"
#include "combat.hh"
/* SORCERY
}
}
+static bool reanimation_effector(Coord c, void *pvt)
+{
+ Obj_handle o = lvl.obj_at(c);
+ Mon_handle m = lvl.mon_at(c);
+ if (m == NO_MON)
+ {
+ Obj const *optr = obj_snap(o);
+ if (!corpse_is_blessed(optr))
+ {
+ m = create_mon(PM_ZOMBIE, c);
+ if (m != NO_MON)
+ {
+ consume_obj(o);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static Radiance reanimate_radiance =
+{
+ {},
+ Nowhere,
+ MAX_FOV_RADIUS,
+ Reo_spiral_out,
+ false,
+ nullptr,
+ dflt_blk,
+ reanimation_effector
+};
+
static void cast_animate_dead(Mon_handle mon)
{
+ Mon const *mptr = mon_snap(mon);
+ reanimate_radiance.clear();
+ reanimate_radiance.centre = mptr->pos;
+ reanimate_radiance.compute();
+ reanimate_radiance.resolve();
}
Monspell_cast_func sorcery_funcs[NUM_MONSPELLS] =
{
switch (zero_die(6))
{
+ case 3:
+ return MS_ANIMATE_DEAD;
case 4:
if (!u.leadfoot)
{
{
switch (zero_die(6))
{
+ case 3:
+ return MS_ANIMATE_DEAD;
case 4:
if (!u.leadfoot)
{
{
switch (zero_die(7))
{
+ case 3:
+ return MS_ANIMATE_DEAD;
case 6:
if (!u.withering)
{
{
switch (zero_die(10))
{
+ case 6:
+ return MS_ANIMATE_DEAD;
case 9:
if (!u.withering)
{
{
switch (zero_die(7))
{
+ case 3:
+ return MS_ANIMATE_DEAD;
case 6:
if (!u.withering)
{
return MS_CURSE_WITHERING;
}
+ /* fall through */
case 4:
if (!u.leadfoot)
{
apply_bonuses(u.armour, BONUS_ARMOUR);
apply_bonuses(u.ring, BONUS_RING);
apply_bonuses(u.weapon, BONUS_WEAPON);
+ if (u.known_skill[Skill_deaths_grace])
+ {
+ u.resistances[DT_NECRO] |= BONUS_MASK_PERM_SKILL;
+ }
+ if (u.known_skill[Skill_deaths_vengeance])
+ {
+ u.damage_amp[DT_SLAY_UNDEAD] |= BONUS_MASK_PERM_SKILL;
+ }
+ if (u.known_skill[Skill_demon_slayer])
+ {
+ u.damage_amp[DT_SLAY_DEMON] |= BONUS_MASK_PERM_SKILL;
+ }
notify_defence_recalc();
}
{
strncpy(u.name, name, 16);
}
+ u.role = role;
u.body = 10;
u.bdam = 0;
u.agility = 10;
u.experience = 0;
u.level = 1;
u.food = 2000;
- for (int i = 0; i < 19; ++i)
+ for (int i = 0; i < INVENTORY_SIZE; ++i)
{
u.inventory[i] = NO_OBJ;
}
+ for (int i = 0; i < NUM_SKILLS; ++i)
+ {
+ u.known_skill[i] = false;
+ }
role_inventories[role](&u);
recalc_defence();
}
{
u.level++;
notify_level_gain();
+ for (int i = 0; universal_skills[i].skill != NO_SKILL; ++i)
+ {
+ if (u.level == universal_skills[i].level)
+ {
+ u.known_skill[universal_skills[i].skill] = true;
+ recalc_defence();
+ notify_learned_skill(universal_skills[i].skill);
+ }
+ }
+ for (int i = 0; skill_tables[u.role][i].skill != NO_SKILL; ++i)
+ {
+ if (u.level == skill_tables[u.role][i].level)
+ {
+ u.known_skill[skill_tables[u.role][i].skill] = true;
+ recalc_defence();
+ notify_learned_skill(skill_tables[u.role][i].skill);
+ }
+ }
if (!zero_die(2))
{
bodygain = gain_body(2);
}
if (hpgain > 0)
{
- /* v1.3: Policy change - gaining a level effectively
- * heals you. */
u.hpcur += hpgain;
u.hpmax += hpgain;
notify_hp_gain(hpgain);
u.food -= food_use;
notify_food_use(food_use, squeal);
}
+ if (u.known_skill[Skill_sanctified_by_death])
+ {
+ for (int i = 0; i < INVENTORY_SIZE; ++i)
+ {
+ Obj *optr = obj_snapv(u.inventory[i]);
+ if (optr && (optr->po_ref == PO_CORPSE))
+ {
+ optr->meta[1] = 1; // bless this corpse
+ }
+ }
+ Coord c;
+ for (c.y = u.pos.y - 1; c.y <= u.pos.x + 1; ++c.y)
+ {
+ for (c.x = u.pos.x - 1; c.x <= u.pos.x + 1; ++c.x)
+ {
+ Obj_handle o = lvl.obj_at(c);
+ Obj *optr = obj_snapv(o);
+ if (optr && (optr->po_ref == PO_CORPSE))
+ {
+ optr->meta[1] = 1;
+ }
+ }
+ }
+ }
if (u.leadfoot > 0)
{
u.leadfoot--;
u.level = 0;
u.flight = u.passwater = u.protective_gear = 0;
u.weapon = u.armour = u.ring = NO_OBJ;
- for (i = 0; i < 19; ++i)
+ for (i = 0; i < INVENTORY_SIZE; ++i)
{
u.inventory[i] = NO_OBJ;
}
+ for (i = 0; i < NUM_SKILLS; ++i)
+ {
+ u.known_skill[i] = false;
+ }
}
/* u.cc */