From: Martin Read Date: Mon, 10 Mar 2014 16:18:43 +0000 (+0000) Subject: Renaming monsters.cc to mon1.cc X-Git-Tag: 1.0.0~12 X-Git-Url: http://git.blackswordsonics.com/?a=commitdiff_plain;h=49fc9ff2f2d64f842935c3e072f3aa89943beafd;p=obumbrata Renaming monsters.cc to mon1.cc --- diff --git a/mon1.cc b/mon1.cc new file mode 100644 index 0000000..04f8a59 --- /dev/null +++ b/mon1.cc @@ -0,0 +1,683 @@ +/*! \file monsters.cc + * \brief Monster-related functions + */ + +/* Copyright 2005-2014 Martin Read + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define MONSTERS_CC +#include "obumbrata.hh" +#include "monsters.hh" +#include "objects.hh" + +const Mon_handle NO_MON = 0u; + +std::map monsters; +static bool reject_mon(int pm); + +/*! \brief Summon some monsters + * + * \return Number of monsters summoned + * \param how_many Maximum number of monsters to summon + */ +int summoning(Coord c, int how_many) +{ + int i; + Offset delta; + Coord testpos; + int tryct; + Mon_handle mon; + int created = 0; + int pmon; + for (i = 0; i < how_many; i++) + { + for (tryct = 0; tryct < 20; tryct++) + { + delta = random_step(); + testpos = c + delta; + if ((lvl.terrain_at(testpos) == FLOOR) && + (lvl.mon_at(testpos) == NO_MON) && + (testpos != u.pos)) + { + pmon = get_random_pmon(); + if (pmon_is_magician(pmon)) + { + /* Never summon magicians! */ + continue; + } + mon = create_mon(NO_PMON, testpos); + if (mon != NO_MON) + { + created++; + break; + } + } + } + } + return created; +} + +int ood(int power, int ratio) +{ + return (depth - power + ratio - 1) / ratio; +} + +int get_random_pmon(void) +{ + int tryct; + int pm; + for (tryct = 0; tryct < 200; tryct++) + { + pm = zero_die(NUM_OF_PERMONS); + if (reject_mon(pm)) + { + pm = NO_PMON; + continue; + } + break; + } + return pm; +} + +Mon_handle first_free_mon_handle = 1u; +static Mon_handle get_first_free_mon(void) +{ + if (first_free_mon_handle == NO_MON) + { + return NO_MON; + } + return first_free_mon_handle++; +} + +Mon_handle create_mon(int pm_idx, Coord c) +{ + Mon_handle mon; + if (lvl.mon_at(c) != NO_MON) + { + debug_create_mon_occupied(c); + return NO_MON; + } + if (pm_idx == NO_PMON) + { + pm_idx = get_random_pmon(); + if (pm_idx == NO_PMON) + { + debug_pmon_select_failed(); + return NO_MON; + } + } + mon = get_first_free_mon(); + if (mon != NO_MON) + { + Mon m; + m.self = mon; + m.pm_ref = pm_idx; + m.flags = MF_USED | MF_ALIVE; + m.pos = c; + m.ai_lastpos = Nowhere; + m.hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1); + m.hpcur = m.hpmax; + m.mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3); + m.defence = permons[pm_idx].defence + ood(permons[pm_idx].power, 3); + m.mdam = permons[pm_idx].mdam + ood(permons[pm_idx].power, 5); + if (permons[pm_idx].rdam != NO_ATK) + { + m.rtohit = permons[pm_idx].rtohit + ood(permons[pm_idx].power, 3); + m.rdam = permons[pm_idx].rdam + ood(permons[pm_idx].power, 5); + } + else + { + m.rtohit = NO_ATK; + m.rdam = NO_ATK; + } + monsters[mon] = m; + lvl.set_mon_at(c, mon); + if (mon_visible(mon)) + { + notify_new_mon_at(c, mon); + } + return mon; + } + return NO_MON; +} + +/* The rules: + * A monster gets eight entries. + */ + +struct Death_drop_entry +{ + int pm_ref; + void (*func)(Coord c); +}; + +void goblin_death_drop(Coord c) +{ + if (!zero_die(4)) + { + create_obj_near(PO_DAGGER, 1, c); + } +} + +void thug_death_drop(Coord c) +{ + if (!zero_die(4)) + { + create_obj_near(PO_MACE, 1, c); + } + else if (!zero_die(3)) + { + create_obj_near(PO_LEATHER_ARMOUR, 1, c); + } +} + +void hunter_death_drop(Coord c) +{ + if (!zero_die(6)) + { + create_obj_near(PO_BOW, 1, c); + } +} + +void duellist_death_drop(Coord c) +{ + if (!zero_die(6)) + { + create_obj_near(PO_LONG_SWORD, 1, c); + } +} + +void wizard_death_drop(Coord c) +{ + if (!zero_die(4)) + { + create_obj_class_near(POCLASS_SCROLL, 1, false, c); + } + else if (!zero_die(3)) + { + create_obj_class_near(POCLASS_POTION, 1, false, c); + } +} + +void warlord_death_drop(Coord c) +{ + if (!zero_die(3)) + { + create_obj_near(PO_RUNESWORD, 1, c); + } +} + +void demon_death_drop(Coord c) +{ + if (!zero_die(100)) + { + create_obj_near(PO_DEVIL_SPLEEN, 1, c); + } +} + +Death_drop_entry death_drops[] = +{ + { PM_GOBLIN, goblin_death_drop }, + { PM_THUG, thug_death_drop }, + { PM_GOON, thug_death_drop }, + { PM_HUNTER, hunter_death_drop }, + { PM_DUELLIST, duellist_death_drop }, + { PM_WIZARD, wizard_death_drop }, + { PM_WARLORD, warlord_death_drop }, + { PM_DEMON, demon_death_drop }, + { NO_PMON, nullptr } +}; + +void death_drop(Mon_handle mon) +{ + Mon const *mptr = mon_snap(mon); + for (int i = 0; death_drops[i].func != nullptr; ++i) + { + if (death_drops[i].pm_ref == mptr->pm_ref) + { + death_drops[i].func(mptr->pos); + } + } +} + +bool Mon::can_pass(Coord c) const +{ + Terrain terr; + if (!lvl.in_bounds(c)) + { + return false; + } + if (lvl.mon_at(c) != NO_MON) + { + return false; + } + if (c == u.pos) + { + /* Sanity check! */ + return false; + } + if (is_ethereal()) + { + return true; + } + terr = lvl.terrain_at(c); + if (terrain_blocks_beings(terr)) + { + /* Let's *not* stuff all the wall types into a switch, eh? */ + return false; + } + /* Keep the switch, so that we can maintain a convenient distinction + * between floor hazards and volumetric hazards. */ + switch (terr) + { + case LAVA: + if (!can_fly() && !resists(DT_FIRE)) + { + return false; + } + break; + case WATER: + if (!can_fly() && !resists(DT_DROWNING)) + { + return false; + } + default: + break; + } + return true; +} + +void heal_mon(Mon_handle mon, int amount, int cansee) +{ + if (amount > (monsters[mon].hpmax - monsters[mon].hpcur)) + { + amount = monsters[mon].hpmax - monsters[mon].hpcur; + } + if (amount > 0) + { + if (cansee) + { + notify_mon_healed(mon); + } + monsters[mon].hpcur += amount; + } +} + +void unplace_mon(Mon_handle mon) +{ + lvl.set_mon_at(monsters[mon].pos, NO_MON); + monsters[mon].flags &= ~MF_USED; +} + +void apply_liquid(int pm, Coord c) +{ + if (pmon_bleeds(pm)) + { + lvl.set_decal_at(c, Decal_blood); + } + else if (pmon_suppurates(pm)) + { + lvl.set_decal_at(c, Decal_pus); + } +} + +/*! \brief Handle the death of a monster + * + * \todo Support special effects on monster death + */ +void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse) +{ + Mon *mptr = mon_snapv(mon); + int pm = mptr->pm_ref; + apply_liquid(pm, mptr->pos); + if (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse)) + { + // TODO shower the surroundings with the monster's liquid + } + else if (pmon_leaves_corpse(pm)) + { + create_corpse(pm, mptr->pos); + } + death_drop(mon); // phat lewt! + mptr->flags &= ~MF_ALIVE; + monsters[mon].hpcur = -1; + unplace_mon(mon); // cleanup + if (by_you) + { + notify_player_killed_mon(mon); + gain_experience(permons[monsters[mon].pm_ref].exp); + } + else if (mon_visible(mon)) + { + notify_mon_dies(mon); + } +} + +void damage_mon(Mon_handle mon, int amount, bool by_you) +{ + Mon *mptr; + mptr = mon_snapv(mon); + if (amount >= mptr->hpcur) + { + kill_mon(mon, by_you); + } + else + { + mptr->hpcur -= amount; + } +} + +bool reject_mon(int pm) +{ + if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity)) + { + return true; + } + return false; +} + +Pass_fail teleport_mon_to_you(Mon_handle mon) +{ + int tryct; + Offset delta; + Coord c; + int success = 0; + Mon *mptr = mon_snapv(mon); + Coord oldpos = mptr->pos; + for (tryct = 0; tryct < 40; tryct++) + { + delta = random_step(); + c = u.pos + delta; + if (mptr->can_pass(c)) + { + success = 1; + break; + } + } + if (success) + { + reloc_mon(mon, c); + notify_mon_appears(mon); + return You_pass; + } + return You_fail; +} + +Pass_fail teleport_mon(Mon_handle mon) +{ + Pass_fail rval = You_fail; + int cell_try; + Coord c; + for (cell_try = 0; cell_try < 200; cell_try++) + { + c = lvl.random_point(1); + if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos)) + { + reloc_mon(mon, c); + rval = You_pass; + break; + } + } + return rval; +} + +int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you) +{ + /* 0 = blocked, 1 = knocked, 2 = killed */ + Mon *mptr = mon_snapv(mon); + Coord c = mptr->pos + step; + Coord savedpos = mptr->pos; + Terrain terr = lvl.terrain_at(c); + + if (mptr->resists(DT_KNOCKBACK)) + { + if (cansee) + { + notify_knockback_mon_resisted(mon); + } + return 0; + } + if (terrain_blocks_beings(terr)) + { + if (cansee) + { + notify_knockback_mon_blocked(mon); + } + return 0; + } + reloc_mon(mon, c); + switch (terr) + { + case LAVA: + if (mptr->can_fly()) + { + if (cansee) + { + notify_knockback_mon_hover_lava(mon); + } + } + else + { + if (cansee) + { + notify_knockback_mon_immersed_lava(mon); + } + else + { + notify_knockback_mon_lava_offscreen(); + } + if (!mptr->resists(DT_FIRE)) + { + kill_mon(mon, by_you); + return 2; + } + } + break; + case WATER: + if (mptr->can_fly()) + { + if (cansee) + { + notify_knockback_mon_hover_water(mon); + } + } + else + { + if (cansee) + { + notify_knockback_mon_immersed_water(mon); + } + else + { + notify_knockback_mon_water_offscreen(); + } + if (!mptr->resists(DT_DROWNING)) + { + kill_mon(mon, by_you); + return 2; + } + } + break; + default: + break; + } + return 1; +} + +void reloc_mon(Mon_handle mon, Coord newpos) +{ + Mon *mptr = mon_snapv(mon); + lvl.set_mon_at(mptr->pos, NO_MON); + notify_new_mon_at(mptr->pos, NO_MON); + mptr->pos = newpos; + lvl.set_mon_at(mptr->pos, mon); + notify_new_mon_at(mptr->pos, mon); +} + +void move_mon(Mon_handle mon, Coord c) +{ + Mon *mptr; + mptr = mon_snapv(mon); + if (!mptr->can_pass(c)) + { + debug_mon_invalid_move(mon, c); + return; + } + if (lvl.mon_at(mptr->pos) != mon) + { + debug_misplaced_monster(); + return; + } + reloc_mon(mon, c); +} + +void summon_demon_near(Coord c) +{ + Coord c2 = c + random_step(); + Mon_handle mon; + if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) && + (c2 != u.pos)) + { + mon = create_mon(PM_DEMON, c2); + notify_summon_demon(mon); + } +} + +bool mon_visible(Mon_handle mon) +{ + Offset delta; + Mon const *mptr = mon_snap(mon); + if (!(mptr->flags & MF_USED)) + { + return false; + } + delta = mptr->pos.delta(u.pos); + if (delta.len_cheb() <= MAX_FOV_RADIUS) + { + return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central; + } + else + { + return false; + } +} + +void update_mon(Mon_handle mon) +{ + int cansee; + Mon *mptr = mon_snapv(mon); + if (mptr->hpcur < mptr->hpmax) + { + cansee = mon_visible(mon); + // TODO modify regen handling to use flags/fields instead of switching on pm_ref + switch (mptr->pm_ref) + { + case PM_TROLL: + if (!(game_tick % 10)) + { + if (cansee) + { + notify_mon_regenerates(mon); + } + heal_mon(mon, one_die(3) + 3, 0); + } + break; + + case PM_ZOMBIE: + /* Zombies don't recover from their injuries. */ + break; + + default: + if (!(game_tick % 20)) + { + heal_mon(mon, 1, cansee); + } + break; + } + } +} + +bool Mon::resists(Damtyp dt) const +{ + switch (dt) + { + case DT_COLD: + return pmon_resists_cold(pm_ref); + case DT_FIRE: + return pmon_resists_fire(pm_ref); + case DT_POISON: + return pmon_resists_poison(pm_ref); + case DT_NECRO: + return pmon_resists_necro(pm_ref); + case DT_ELEC: + return pmon_resists_elec(pm_ref); + case DT_KNOCKBACK: + return pmon_resists_knockback(pm_ref); + case DT_DROWNING: + return pmon_resists_drowning(pm_ref); + default: + return false; + } +} + +bool Mon::is_ethereal(void) const +{ + return pmon_is_ethereal(pm_ref); +} + +bool Mon::can_fly(void) const +{ + return pmon_can_fly(pm_ref); +} + +void asprint_mon_name(char **buf, Mon_handle mon, int article) +{ + Mon const *mptr = mon_snap(mon); + int i; + switch (article) + { + case 0: + i = asprintf(buf, "a%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name); + break; + case 1: + i = asprintf(buf, "the %s", permons[mptr->pm_ref].name); + break; + case 2: + i = asprintf(buf, "A%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name); + break; + case 3: + i = asprintf(buf, "The %s", permons[mptr->pm_ref].name); + break; + default: + i = -1; + } + if (i == -1) + { + // TODO wibble + } + return; +} + +/* monsters.cc */ +// vim:cindent diff --git a/monsters.cc b/monsters.cc deleted file mode 100644 index 04f8a59..0000000 --- a/monsters.cc +++ /dev/null @@ -1,683 +0,0 @@ -/*! \file monsters.cc - * \brief Monster-related functions - */ - -/* Copyright 2005-2014 Martin Read - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define MONSTERS_CC -#include "obumbrata.hh" -#include "monsters.hh" -#include "objects.hh" - -const Mon_handle NO_MON = 0u; - -std::map monsters; -static bool reject_mon(int pm); - -/*! \brief Summon some monsters - * - * \return Number of monsters summoned - * \param how_many Maximum number of monsters to summon - */ -int summoning(Coord c, int how_many) -{ - int i; - Offset delta; - Coord testpos; - int tryct; - Mon_handle mon; - int created = 0; - int pmon; - for (i = 0; i < how_many; i++) - { - for (tryct = 0; tryct < 20; tryct++) - { - delta = random_step(); - testpos = c + delta; - if ((lvl.terrain_at(testpos) == FLOOR) && - (lvl.mon_at(testpos) == NO_MON) && - (testpos != u.pos)) - { - pmon = get_random_pmon(); - if (pmon_is_magician(pmon)) - { - /* Never summon magicians! */ - continue; - } - mon = create_mon(NO_PMON, testpos); - if (mon != NO_MON) - { - created++; - break; - } - } - } - } - return created; -} - -int ood(int power, int ratio) -{ - return (depth - power + ratio - 1) / ratio; -} - -int get_random_pmon(void) -{ - int tryct; - int pm; - for (tryct = 0; tryct < 200; tryct++) - { - pm = zero_die(NUM_OF_PERMONS); - if (reject_mon(pm)) - { - pm = NO_PMON; - continue; - } - break; - } - return pm; -} - -Mon_handle first_free_mon_handle = 1u; -static Mon_handle get_first_free_mon(void) -{ - if (first_free_mon_handle == NO_MON) - { - return NO_MON; - } - return first_free_mon_handle++; -} - -Mon_handle create_mon(int pm_idx, Coord c) -{ - Mon_handle mon; - if (lvl.mon_at(c) != NO_MON) - { - debug_create_mon_occupied(c); - return NO_MON; - } - if (pm_idx == NO_PMON) - { - pm_idx = get_random_pmon(); - if (pm_idx == NO_PMON) - { - debug_pmon_select_failed(); - return NO_MON; - } - } - mon = get_first_free_mon(); - if (mon != NO_MON) - { - Mon m; - m.self = mon; - m.pm_ref = pm_idx; - m.flags = MF_USED | MF_ALIVE; - m.pos = c; - m.ai_lastpos = Nowhere; - m.hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1); - m.hpcur = m.hpmax; - m.mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3); - m.defence = permons[pm_idx].defence + ood(permons[pm_idx].power, 3); - m.mdam = permons[pm_idx].mdam + ood(permons[pm_idx].power, 5); - if (permons[pm_idx].rdam != NO_ATK) - { - m.rtohit = permons[pm_idx].rtohit + ood(permons[pm_idx].power, 3); - m.rdam = permons[pm_idx].rdam + ood(permons[pm_idx].power, 5); - } - else - { - m.rtohit = NO_ATK; - m.rdam = NO_ATK; - } - monsters[mon] = m; - lvl.set_mon_at(c, mon); - if (mon_visible(mon)) - { - notify_new_mon_at(c, mon); - } - return mon; - } - return NO_MON; -} - -/* The rules: - * A monster gets eight entries. - */ - -struct Death_drop_entry -{ - int pm_ref; - void (*func)(Coord c); -}; - -void goblin_death_drop(Coord c) -{ - if (!zero_die(4)) - { - create_obj_near(PO_DAGGER, 1, c); - } -} - -void thug_death_drop(Coord c) -{ - if (!zero_die(4)) - { - create_obj_near(PO_MACE, 1, c); - } - else if (!zero_die(3)) - { - create_obj_near(PO_LEATHER_ARMOUR, 1, c); - } -} - -void hunter_death_drop(Coord c) -{ - if (!zero_die(6)) - { - create_obj_near(PO_BOW, 1, c); - } -} - -void duellist_death_drop(Coord c) -{ - if (!zero_die(6)) - { - create_obj_near(PO_LONG_SWORD, 1, c); - } -} - -void wizard_death_drop(Coord c) -{ - if (!zero_die(4)) - { - create_obj_class_near(POCLASS_SCROLL, 1, false, c); - } - else if (!zero_die(3)) - { - create_obj_class_near(POCLASS_POTION, 1, false, c); - } -} - -void warlord_death_drop(Coord c) -{ - if (!zero_die(3)) - { - create_obj_near(PO_RUNESWORD, 1, c); - } -} - -void demon_death_drop(Coord c) -{ - if (!zero_die(100)) - { - create_obj_near(PO_DEVIL_SPLEEN, 1, c); - } -} - -Death_drop_entry death_drops[] = -{ - { PM_GOBLIN, goblin_death_drop }, - { PM_THUG, thug_death_drop }, - { PM_GOON, thug_death_drop }, - { PM_HUNTER, hunter_death_drop }, - { PM_DUELLIST, duellist_death_drop }, - { PM_WIZARD, wizard_death_drop }, - { PM_WARLORD, warlord_death_drop }, - { PM_DEMON, demon_death_drop }, - { NO_PMON, nullptr } -}; - -void death_drop(Mon_handle mon) -{ - Mon const *mptr = mon_snap(mon); - for (int i = 0; death_drops[i].func != nullptr; ++i) - { - if (death_drops[i].pm_ref == mptr->pm_ref) - { - death_drops[i].func(mptr->pos); - } - } -} - -bool Mon::can_pass(Coord c) const -{ - Terrain terr; - if (!lvl.in_bounds(c)) - { - return false; - } - if (lvl.mon_at(c) != NO_MON) - { - return false; - } - if (c == u.pos) - { - /* Sanity check! */ - return false; - } - if (is_ethereal()) - { - return true; - } - terr = lvl.terrain_at(c); - if (terrain_blocks_beings(terr)) - { - /* Let's *not* stuff all the wall types into a switch, eh? */ - return false; - } - /* Keep the switch, so that we can maintain a convenient distinction - * between floor hazards and volumetric hazards. */ - switch (terr) - { - case LAVA: - if (!can_fly() && !resists(DT_FIRE)) - { - return false; - } - break; - case WATER: - if (!can_fly() && !resists(DT_DROWNING)) - { - return false; - } - default: - break; - } - return true; -} - -void heal_mon(Mon_handle mon, int amount, int cansee) -{ - if (amount > (monsters[mon].hpmax - monsters[mon].hpcur)) - { - amount = monsters[mon].hpmax - monsters[mon].hpcur; - } - if (amount > 0) - { - if (cansee) - { - notify_mon_healed(mon); - } - monsters[mon].hpcur += amount; - } -} - -void unplace_mon(Mon_handle mon) -{ - lvl.set_mon_at(monsters[mon].pos, NO_MON); - monsters[mon].flags &= ~MF_USED; -} - -void apply_liquid(int pm, Coord c) -{ - if (pmon_bleeds(pm)) - { - lvl.set_decal_at(c, Decal_blood); - } - else if (pmon_suppurates(pm)) - { - lvl.set_decal_at(c, Decal_pus); - } -} - -/*! \brief Handle the death of a monster - * - * \todo Support special effects on monster death - */ -void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse) -{ - Mon *mptr = mon_snapv(mon); - int pm = mptr->pm_ref; - apply_liquid(pm, mptr->pos); - if (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse)) - { - // TODO shower the surroundings with the monster's liquid - } - else if (pmon_leaves_corpse(pm)) - { - create_corpse(pm, mptr->pos); - } - death_drop(mon); // phat lewt! - mptr->flags &= ~MF_ALIVE; - monsters[mon].hpcur = -1; - unplace_mon(mon); // cleanup - if (by_you) - { - notify_player_killed_mon(mon); - gain_experience(permons[monsters[mon].pm_ref].exp); - } - else if (mon_visible(mon)) - { - notify_mon_dies(mon); - } -} - -void damage_mon(Mon_handle mon, int amount, bool by_you) -{ - Mon *mptr; - mptr = mon_snapv(mon); - if (amount >= mptr->hpcur) - { - kill_mon(mon, by_you); - } - else - { - mptr->hpcur -= amount; - } -} - -bool reject_mon(int pm) -{ - if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity)) - { - return true; - } - return false; -} - -Pass_fail teleport_mon_to_you(Mon_handle mon) -{ - int tryct; - Offset delta; - Coord c; - int success = 0; - Mon *mptr = mon_snapv(mon); - Coord oldpos = mptr->pos; - for (tryct = 0; tryct < 40; tryct++) - { - delta = random_step(); - c = u.pos + delta; - if (mptr->can_pass(c)) - { - success = 1; - break; - } - } - if (success) - { - reloc_mon(mon, c); - notify_mon_appears(mon); - return You_pass; - } - return You_fail; -} - -Pass_fail teleport_mon(Mon_handle mon) -{ - Pass_fail rval = You_fail; - int cell_try; - Coord c; - for (cell_try = 0; cell_try < 200; cell_try++) - { - c = lvl.random_point(1); - if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos)) - { - reloc_mon(mon, c); - rval = You_pass; - break; - } - } - return rval; -} - -int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you) -{ - /* 0 = blocked, 1 = knocked, 2 = killed */ - Mon *mptr = mon_snapv(mon); - Coord c = mptr->pos + step; - Coord savedpos = mptr->pos; - Terrain terr = lvl.terrain_at(c); - - if (mptr->resists(DT_KNOCKBACK)) - { - if (cansee) - { - notify_knockback_mon_resisted(mon); - } - return 0; - } - if (terrain_blocks_beings(terr)) - { - if (cansee) - { - notify_knockback_mon_blocked(mon); - } - return 0; - } - reloc_mon(mon, c); - switch (terr) - { - case LAVA: - if (mptr->can_fly()) - { - if (cansee) - { - notify_knockback_mon_hover_lava(mon); - } - } - else - { - if (cansee) - { - notify_knockback_mon_immersed_lava(mon); - } - else - { - notify_knockback_mon_lava_offscreen(); - } - if (!mptr->resists(DT_FIRE)) - { - kill_mon(mon, by_you); - return 2; - } - } - break; - case WATER: - if (mptr->can_fly()) - { - if (cansee) - { - notify_knockback_mon_hover_water(mon); - } - } - else - { - if (cansee) - { - notify_knockback_mon_immersed_water(mon); - } - else - { - notify_knockback_mon_water_offscreen(); - } - if (!mptr->resists(DT_DROWNING)) - { - kill_mon(mon, by_you); - return 2; - } - } - break; - default: - break; - } - return 1; -} - -void reloc_mon(Mon_handle mon, Coord newpos) -{ - Mon *mptr = mon_snapv(mon); - lvl.set_mon_at(mptr->pos, NO_MON); - notify_new_mon_at(mptr->pos, NO_MON); - mptr->pos = newpos; - lvl.set_mon_at(mptr->pos, mon); - notify_new_mon_at(mptr->pos, mon); -} - -void move_mon(Mon_handle mon, Coord c) -{ - Mon *mptr; - mptr = mon_snapv(mon); - if (!mptr->can_pass(c)) - { - debug_mon_invalid_move(mon, c); - return; - } - if (lvl.mon_at(mptr->pos) != mon) - { - debug_misplaced_monster(); - return; - } - reloc_mon(mon, c); -} - -void summon_demon_near(Coord c) -{ - Coord c2 = c + random_step(); - Mon_handle mon; - if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) && - (c2 != u.pos)) - { - mon = create_mon(PM_DEMON, c2); - notify_summon_demon(mon); - } -} - -bool mon_visible(Mon_handle mon) -{ - Offset delta; - Mon const *mptr = mon_snap(mon); - if (!(mptr->flags & MF_USED)) - { - return false; - } - delta = mptr->pos.delta(u.pos); - if (delta.len_cheb() <= MAX_FOV_RADIUS) - { - return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central; - } - else - { - return false; - } -} - -void update_mon(Mon_handle mon) -{ - int cansee; - Mon *mptr = mon_snapv(mon); - if (mptr->hpcur < mptr->hpmax) - { - cansee = mon_visible(mon); - // TODO modify regen handling to use flags/fields instead of switching on pm_ref - switch (mptr->pm_ref) - { - case PM_TROLL: - if (!(game_tick % 10)) - { - if (cansee) - { - notify_mon_regenerates(mon); - } - heal_mon(mon, one_die(3) + 3, 0); - } - break; - - case PM_ZOMBIE: - /* Zombies don't recover from their injuries. */ - break; - - default: - if (!(game_tick % 20)) - { - heal_mon(mon, 1, cansee); - } - break; - } - } -} - -bool Mon::resists(Damtyp dt) const -{ - switch (dt) - { - case DT_COLD: - return pmon_resists_cold(pm_ref); - case DT_FIRE: - return pmon_resists_fire(pm_ref); - case DT_POISON: - return pmon_resists_poison(pm_ref); - case DT_NECRO: - return pmon_resists_necro(pm_ref); - case DT_ELEC: - return pmon_resists_elec(pm_ref); - case DT_KNOCKBACK: - return pmon_resists_knockback(pm_ref); - case DT_DROWNING: - return pmon_resists_drowning(pm_ref); - default: - return false; - } -} - -bool Mon::is_ethereal(void) const -{ - return pmon_is_ethereal(pm_ref); -} - -bool Mon::can_fly(void) const -{ - return pmon_can_fly(pm_ref); -} - -void asprint_mon_name(char **buf, Mon_handle mon, int article) -{ - Mon const *mptr = mon_snap(mon); - int i; - switch (article) - { - case 0: - i = asprintf(buf, "a%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name); - break; - case 1: - i = asprintf(buf, "the %s", permons[mptr->pm_ref].name); - break; - case 2: - i = asprintf(buf, "A%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name); - break; - case 3: - i = asprintf(buf, "The %s", permons[mptr->pm_ref].name); - break; - default: - i = -1; - } - if (i == -1) - { - // TODO wibble - } - return; -} - -/* monsters.cc */ -// vim:cindent