Overhaul and improve force cages
- Add instant help message - Prevent from ending town combat if one member is in a forcecage or if everyone's in a different forcecage (you can still end it if everyone's in the same cage) - If entering town combat while caged, everyone remains in the cage - Penalties to melee combat if the target or attacker is in a forcecage (higher penalties if the attacker is caged, unless they're using a pole weapon) - Instead of placing a forcecage at every location where someone gains forcecage status, the game now syncs forcecages (placing them on locations where someone has forcecage status) at the end of the move. - The Flash Step and Word of Recall spells and any specials that move the party also clear their forcecage status, so that a new forcecage won't appear on their new location. If they're moved into a forcecage, the syncing process will give them the status at the end of the move. - Forcecage status now supported for the Occasional Status item ability - Forcecages now eventually expire on their own even if you can't break out. Unoccupied ones also have a slight (0.1%) chance each turn to expire. - Affect Status node now allows forcecage status. (It also works as documented, using Extra 1c rather than 2a as the status type.) - Preset monsters placed in preset force cages gain the status at town initialization; it's a lot more than they'd gain through the syncing process, so unless they break free, it'll last a very long time. - Monsters/PCs with spells (and PCs with Mage Lore) now have an increased chance of resisting entrapment in a forcecage - If the full party is subject to a forcecage, the PC with the best chance of breaking free (assuming they don't resist) is chosen to determine whether they resist. Note that this may not be the PC with the best chance of resisting in the first place. - Protection from Forcecage now implemented as an item ability Other stuff (though related): - Reset party's combat pos to the null location (-1,-1) after a cutscene ends; to not do so would mess up get_loc() calls
This commit is contained in:
@@ -812,7 +812,11 @@ set Extra 1b to 0 if you want to help the party or 1 if you want to harm them.
|
||||
<dt>Extra 1a:</dt><dd>The amount to change. (Usually ranges is 0 ... 8; Martyr's Shield is
|
||||
0 ... 10, and paralysis is 0 ... 5000)</dd>
|
||||
<dt>Extra 1b:</dt><dd>If 0, helps. Otherwise, harms.</dd>
|
||||
<dt>Extra 1c:</dt><dd>Which status effect. Press the Choose button to select one.</dd></dd>
|
||||
<dt>Extra 1c:</dt><dd>Which status effect. Press the Choose button to select one.</dd>
|
||||
<dt>Note:</dt><dd>If you affect their forcecage status, a forcecage barrier will
|
||||
automatically be placed on the space. However, if you reduce their forcecage status to 0,
|
||||
it won't work because being in a forcecage barrier will reset their status. To free them
|
||||
from a forcecage, you should instead use Place Fields with the "Cleanse" type.</dd></dd>
|
||||
|
||||
<dt>Type 88: Affect Traits</dt><dd>Adds or removes a special trait. If the target is a
|
||||
monster, this has no effect.
|
||||
@@ -1776,7 +1780,12 @@ or sfx (16 - sm blood, 17 - med blood, 18 - lg blood, 19 - sm slime, 20 - lg sli
|
||||
21 - ash, 22 - bones, 23 - stones) to place, or one of the special values (32 - cleanse,
|
||||
33 - smash fragile walls like Move Mountains).</dd>
|
||||
<dt>Note:</dt><dd>Fields are never placed on spaces that contain walls, or other
|
||||
impenetrable terrain.</dd></dd>
|
||||
impenetrable terrain.</dd>
|
||||
<dt>Note:</dt><dd>Though you can place a forcecage around a creature using this, if you
|
||||
know which creature you want it's better to use Affect Status which allows you to control
|
||||
how long the forcecage will last (unless they manage to break it). If you want to free
|
||||
them from a forcecage, on the other hand, use this node with the special value 32
|
||||
("Cleanse").</dd></dd>
|
||||
|
||||
<dt>Type 211: Set Explored</dt><dd>This node alters your map, marking spaces as having
|
||||
been explored or not.
|
||||
|
||||
@@ -43,7 +43,7 @@ This person has looked over your items and maybe offered to buy some of them. Cl
|
||||
Merchants will only buy items that have been identified. If you want to sell something, you may need to find a sage to identify it (such as Habecker in Fort Emergence).
|
||||
This character will, for a fee, identify some of your items whose nature is, as yet, unknown to you. Click on the 'ID' button by the item to get it identified.
|
||||
This merchant has offered to magically improve one of your weapons. Click on the 'Ench' button by the weapon to pay for this service. You can only augment identified weapons that aren't magic already.
|
||||
|
||||
One of your characters has just been trapped in a forcecage. This will hold the character in place but not prevent them from fighting, though melee combat will be hindered. If you have no other action to take, just pause repeatedly (click on the PC or type '5' on the keypad) and the cage will eventually break or expire.
|
||||
You have just opened a door. To open any door, walk into it. Some walls have secret doors in them. To search a wall for secret doors, walk into it. Many special things are hidden in the dungeons.
|
||||
You have just entered combat mode! To start using a weapon, click on that weapon's name. Your characters will now move one at a time ... to attack a bad guy, walk into it. For more information on combat mechanics, type '?'.
|
||||
When fighting early in the game, when you're still weak, rely heavily on magical spells (make sure to have a character with Mage Spells skill of 3). Your melee people won't be strong for a while. Hit the 'End' button to leave combat.
|
||||
|
||||
@@ -120,7 +120,7 @@ Unused
|
||||
Unused
|
||||
Amount (usually 0 .. 8)
|
||||
0 - help, 1 - harm
|
||||
Which effect (0 .. 13)
|
||||
Which effect (0 .. 14)
|
||||
Unused
|
||||
Unused
|
||||
Unused
|
||||
|
||||
@@ -891,7 +891,13 @@ static void handle_combat_switch(bool& did_something, bool& need_redraw, bool& n
|
||||
set_stat_window(current_pc);
|
||||
} else add_string_to_buf("Can't end combat yet.");
|
||||
} else {
|
||||
univ.party.direction = end_town_combat();
|
||||
eDirection dir = end_town_combat();
|
||||
if(dir == DIR_HERE) {
|
||||
add_string_to_buf("Failed to end combat.");
|
||||
need_reprint = true;
|
||||
return;
|
||||
}
|
||||
univ.party.direction = dir;
|
||||
center = univ.town.p_loc;
|
||||
set_stat_window(current_pc);
|
||||
redraw_screen(REFRESH_TERRAIN | REFRESH_TEXT | REFRESH_STATS);
|
||||
@@ -1450,7 +1456,13 @@ bool handle_action(sf::Event event) {
|
||||
// MARK: Handle non-PC stuff (like monsters) if the party actually did something
|
||||
if(did_something) handle_monster_actions(need_redraw, need_reprint);
|
||||
if(fog_lifted) need_redraw = true;
|
||||
if(cartoon_happening) need_redraw = true;
|
||||
if(cartoon_happening) {
|
||||
need_redraw = true;
|
||||
if(!is_combat()) {
|
||||
for(int i = 0; i < 6; i++)
|
||||
univ.party[i].combat_pos = {-1,-1};
|
||||
}
|
||||
}
|
||||
fog_lifted = false;
|
||||
cartoon_happening = false;
|
||||
if(need_redraw) draw_terrain();
|
||||
|
||||
@@ -384,6 +384,7 @@ bool pc_combat_move(location destination) {
|
||||
|
||||
if(monst_hit == nullptr && univ.party[current_pc].status[eStatus::FORCECAGE] > 0) {
|
||||
add_string_to_buf("Move: Can't escape.");
|
||||
add_string_to_buf(" (Try doing something else.)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -570,6 +571,10 @@ void pc_attack(short who_att,iLiving* target) {
|
||||
|
||||
r1 = get_ran(1,1,100) + hit_adj - 20;
|
||||
r1 += 5 * (univ.party[current_pc].status[eStatus::WEBS] / 3);
|
||||
if(univ.party[current_pc].status[eStatus::FORCECAGE] > 0)
|
||||
r1 += 3;
|
||||
if(target->status[eStatus::FORCECAGE] > 0)
|
||||
r1 += 1;
|
||||
r2 = get_ran(1,1,4) + dam_adj;
|
||||
|
||||
if(cPlayer* pc_target = dynamic_cast<cPlayer*>(target)) {
|
||||
@@ -681,9 +686,7 @@ static void apply_weapon_status(eStatus status, int how_much, int dmg, iLiving&
|
||||
break;
|
||||
case eStatus::FORCECAGE:
|
||||
add_string_to_buf(" " + weap_type + " emits a green flash.");
|
||||
which_m.sleep(eStatus::FORCECAGE, 0, dmg - how_much / 2);
|
||||
if(which_m.status[eStatus::FORCECAGE] > 0)
|
||||
univ.town.set_force_cage(which_m.get_loc().x, which_m.get_loc().y, true);
|
||||
which_m.sleep(eStatus::FORCECAGE, how_much, dmg - how_much / 2);
|
||||
break;
|
||||
case eStatus::MARTYRS_SHIELD:
|
||||
add_string_to_buf(" " + weap_type + " leaks an odd-coloured aura.");
|
||||
@@ -717,6 +720,12 @@ void pc_attack_weapon(short who_att,iLiving& target,short hit_adj,short dam_adj,
|
||||
if(primary != 1 && !attacker.traits[eTrait::AMBIDEXTROUS])
|
||||
r1 += 25;
|
||||
|
||||
// Forcecage penalties (reduced for pole weapons)
|
||||
if(attacker.status[eStatus::FORCECAGE] > 0)
|
||||
r1 += what_skill == eSkill::POLE_WEAPONS ? 1 : 3;
|
||||
if(target.status[eStatus::FORCECAGE] > 0)
|
||||
r1 += 1;
|
||||
|
||||
if(primary) {
|
||||
// race adj.
|
||||
if(attacker.race == eRace::SLITH && weap.weap_type == eSkill::POLE_WEAPONS)
|
||||
@@ -1303,6 +1312,9 @@ void do_combat_cast(location target) {
|
||||
else {
|
||||
add_string_to_buf(" Flash step!");
|
||||
caster.combat_pos = target;
|
||||
// This can get you out of a forcecage without breaking it
|
||||
caster.status[eStatus::FORCECAGE] = 0;
|
||||
// Of course, it can also get you into one. If that happens, force cage syncing will fix it.
|
||||
}
|
||||
default:
|
||||
add_string_to_buf(" Error: Summoning spell " + (*spell_being_cast).name() + " not implemented for combat mode.", 4);
|
||||
@@ -1866,12 +1878,44 @@ void fire_missile(location target) {
|
||||
print_buf();
|
||||
}
|
||||
|
||||
static bool sync_force_cages() {
|
||||
static const int fc_multipliers[10] = {1, 1, 1, 1, 2, 2, 2, 3, 3, 4};
|
||||
bool was_change = false;
|
||||
// This goes through the list of creatures and places forcecage barriers on any spaces containing someone with forcecage status.
|
||||
// If anyone is on a forcecage barrier but doesn't have forcecage status, they are given it.
|
||||
for(int i = 0; i < 6; i++) {
|
||||
cPlayer& who = univ.party[i];
|
||||
location loc = who.get_loc();
|
||||
if(who.status[eStatus::FORCECAGE] > 0) {
|
||||
was_change = true;
|
||||
univ.town.set_force_cage(loc.x, loc.y, true);
|
||||
} else if(univ.town.is_force_cage(loc.x, loc.y) && who.status[eStatus::FORCECAGE] == 0) {
|
||||
was_change = true;
|
||||
who.status[eStatus::FORCECAGE] = get_ran(2, 2, 7) * fc_multipliers[get_ran(1,1,10)];
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < univ.town.monst.size(); i++) {
|
||||
cCreature& who = univ.town.monst[i];
|
||||
location loc = who.get_loc();
|
||||
if(who.status[eStatus::FORCECAGE] > 0) {
|
||||
was_change = true;
|
||||
univ.town.set_force_cage(loc.x, loc.y, true);
|
||||
} else if(univ.town.is_force_cage(loc.x, loc.y) && who.status[eStatus::FORCECAGE] == 0) {
|
||||
was_change = true;
|
||||
who.status[eStatus::FORCECAGE] = get_ran(2, 2, 7) * fc_multipliers[get_ran(1,1,10)];
|
||||
}
|
||||
}
|
||||
return was_change;
|
||||
}
|
||||
|
||||
// Select next active PC and, if necessary, run monsters
|
||||
// if monsters go or PC switches (i.e. if need redraw above), return true
|
||||
bool combat_next_step() {
|
||||
bool to_return = false;
|
||||
short store_pc; // will print current pc name is active pc changes
|
||||
|
||||
to_return = sync_force_cages();
|
||||
|
||||
store_pc = current_pc;
|
||||
while(pick_next_pc()) {
|
||||
combat_run_monst();
|
||||
@@ -2024,7 +2068,7 @@ void combat_run_monst() {
|
||||
if(isStatusNegative(status))
|
||||
how_much *= -1;
|
||||
switch(status) {
|
||||
case eStatus::MAIN: case eStatus::FORCECAGE: case eStatus::CHARM:
|
||||
case eStatus::MAIN: case eStatus::CHARM:
|
||||
continue; // Not valid in this context.
|
||||
case eStatus::HASTE_SLOW:
|
||||
if(how_much > 0) add_string_to_buf("An item hastes you!");
|
||||
@@ -2078,6 +2122,12 @@ void combat_run_monst() {
|
||||
else if(univ.party[i].status[eStatus::ACID] > 0)
|
||||
add_string_to_buf("An item neutralizes the acid!");
|
||||
break;
|
||||
case eStatus::FORCECAGE:
|
||||
if(how_much > 0) add_string_to_buf("An item entraps you!");
|
||||
else if(-how_much > univ.party[i].status[eStatus::FORCECAGE])
|
||||
add_string_to_buf("An item frees you!");
|
||||
else add_string_to_buf("An item weakens the barrier!");
|
||||
break;
|
||||
case eStatus::POISONED_WEAPON:
|
||||
if(how_much > 0) {
|
||||
if(univ.party[i].status[eStatus::POISONED_WEAPON] <= 0) {
|
||||
@@ -2743,6 +2793,10 @@ void monster_attack(short who_att,iLiving* target) {
|
||||
r1 -= 5 * min(8, attacker->status[eStatus::BLESS_CURSE]);
|
||||
r1 += 5 * target->status[eStatus::BLESS_CURSE] - 15;
|
||||
r1 += 5 * (attacker->status[eStatus::WEBS] / 3);
|
||||
if(attacker->status[eStatus::FORCECAGE] > 0)
|
||||
r1 += 3; // TODO: PCs have reduced penalty for pole weapons; any way to mirror that here?
|
||||
if(target->status[eStatus::FORCECAGE] > 0)
|
||||
r1 += 1;
|
||||
if(pc_target != nullptr) {
|
||||
r1 += 5 * pc_target->stat_adj(eSkill::DEXTERITY);
|
||||
r1 += pc_target->get_prot_level(eItemAbil::EVASION);
|
||||
@@ -3171,8 +3225,6 @@ void monst_basic_abil(short m_num, std::pair<eMonstAbil,uAbility> abil, iLiving*
|
||||
break;
|
||||
case eStatus::FORCECAGE:
|
||||
target->sleep(abil.second.gen.stat, 8, abil.second.gen.strength);
|
||||
if(target->status[eStatus::FORCECAGE] > 0)
|
||||
univ.town.set_force_cage(target->get_loc().x, target->get_loc().y, true);
|
||||
break;
|
||||
// This only works on monsters
|
||||
case eStatus::CHARM:
|
||||
@@ -4176,9 +4228,6 @@ static void place_spell_pattern(effect_pat_type pat,location center,unsigned sho
|
||||
r1 = get_ran(6,1,8);
|
||||
damage_pc(univ.party[k],r1,eDamageType::WEAPON,eRace::UNKNOWN,0);
|
||||
break;
|
||||
case BARRIER_CAGE:
|
||||
univ.party[k].status[eStatus::FORCECAGE] = 8;
|
||||
break;
|
||||
default:
|
||||
eDamageType type = eDamageType::MARKED;
|
||||
unsigned short dice;
|
||||
@@ -4271,7 +4320,7 @@ static void place_spell_pattern(effect_pat_type pat,location center,unsigned sho
|
||||
damage_monst(univ.town.monst[k],who_hit,r1,eDamageType::WEAPON,0);
|
||||
break;
|
||||
case BARRIER_CAGE:
|
||||
univ.town.monst[k].status[eStatus::FORCECAGE] = 8;
|
||||
univ.town.monst[k].status[eStatus::FORCECAGE] = max(8, univ.town.monst[k].status[eStatus::FORCECAGE]);
|
||||
break;
|
||||
default:
|
||||
eDamageType type = eDamageType::MARKED;
|
||||
@@ -5186,34 +5235,55 @@ void spell_cast_hit_return() {
|
||||
}
|
||||
}
|
||||
|
||||
void break_force_cage(location loc) {
|
||||
for(int j = 0; j < 6; j++) {
|
||||
if(univ.party[j].get_loc() == loc)
|
||||
univ.party[j].status[eStatus::FORCECAGE] = 0;
|
||||
}
|
||||
for(int j = 0; j < univ.town.monst.size(); j++) {
|
||||
if(univ.town.monst[j].get_loc() == loc)
|
||||
univ.town.monst[j].status[eStatus::FORCECAGE] = 0;
|
||||
}
|
||||
univ.town.set_force_cage(loc.x, loc.y, false);
|
||||
}
|
||||
|
||||
void process_force_cage(location loc, short i, short adjust) {
|
||||
if(!univ.town.is_force_cage(loc.x,loc.y)) return;
|
||||
if(i >= 100) {
|
||||
short m = i - 100;
|
||||
cCreature& which_m = univ.town.monst[m];
|
||||
move_to_zero(which_m.status[eStatus::FORCECAGE]);
|
||||
if(which_m.status[eStatus::FORCECAGE] == 0) {
|
||||
add_string_to_buf(" Force cage flickers out.");
|
||||
break_force_cage(loc);
|
||||
return;
|
||||
}
|
||||
if(which_m.attitude % 2 == 1 && get_ran(1,1,100) < which_m.mu * 10 + which_m.cl * 4 + 5 + adjust) {
|
||||
// TODO: This sound is not right
|
||||
play_sound(60);
|
||||
which_m.spell_note(50);
|
||||
univ.town.set_force_cage(loc.x,loc.y,false);
|
||||
which_m.status[eStatus::FORCECAGE] = 0;
|
||||
} else which_m.status[eStatus::FORCECAGE] = 8;
|
||||
break_force_cage(loc);
|
||||
}
|
||||
} else if(i < 0) {
|
||||
/* For now, forcecages without occupants will be permanent. Might change this later.
|
||||
if(get_ran(1,1,100) < 35)
|
||||
univ.town.set_force_cage(loc.x,loc.y,false);
|
||||
*/
|
||||
// Force cages without occupants can last a very long time
|
||||
if(get_ran(1,1,1000) == 1)
|
||||
break_force_cage(loc);
|
||||
} else if(i < 6) {
|
||||
cPlayer& who = univ.party[i];
|
||||
move_to_zero(who.status[eStatus::FORCECAGE]);
|
||||
if(who.status[eStatus::FORCECAGE] == 0) {
|
||||
add_string_to_buf(" Force cage flickers out.");
|
||||
break_force_cage(loc);
|
||||
return;
|
||||
}
|
||||
// We want to make sure everyone has a chance of eventually breaking a cage, because it never ends on its own,
|
||||
// and because being trapped unconditionally prevents you from ending combat mode.
|
||||
short bonus = 5 + who.skill(eSkill::MAGE_LORE) + adjust;
|
||||
if(get_ran(1,1,100) < who.skill(eSkill::MAGE_SPELLS)*10 + who.skill(eSkill::PRIEST_SPELLS)*4 + bonus) {
|
||||
play_sound(60);
|
||||
add_string_to_buf(" " + who.name + " breaks force cage.");
|
||||
univ.town.set_force_cage(loc.x,loc.y,false);
|
||||
who.status[eStatus::FORCECAGE] = 0;
|
||||
} else who.status[eStatus::FORCECAGE] = 8;
|
||||
break_force_cage(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5262,6 +5332,8 @@ void process_fields() {
|
||||
if(univ.town.monst[i].active > 0)
|
||||
monst_inflict_fields(i);
|
||||
|
||||
sync_force_cages();
|
||||
|
||||
// First fry PCs, then call to handle damage to monsters
|
||||
processing_fields = true; // this, in hit_space, makes damage considered to come from whole party
|
||||
for(i = 0; i < univ.town->max_dim(); i++)
|
||||
@@ -5322,8 +5394,12 @@ void process_fields() {
|
||||
if(univ.town.is_force_cage(i,j)) {
|
||||
loc.x = i; loc.y = j;
|
||||
short who = univ.get_target_i(*univ.target_there(loc));
|
||||
if(who == 6) who = -1;
|
||||
process_force_cage(loc, who);
|
||||
// If we got a PC, check the others too, in case they're on the same space
|
||||
while(++who > 0 && who < 6 && univ.town.is_force_cage(i,j)) {
|
||||
loc = univ.party[who].get_loc();
|
||||
process_force_cage(loc, who);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ void start_fancy_spell_targeting(eSpell num, bool freebie = false, int spell_ran
|
||||
void spell_cast_hit_return();
|
||||
void process_fields();
|
||||
void process_force_cage(location loc, short i, short bonus = 0);
|
||||
void break_force_cage(location loc);
|
||||
void scloud_space(short m,short n);
|
||||
void web_space(short m,short n);
|
||||
void sleep_cloud_space(short m,short n);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "universe.hpp"
|
||||
#include "boe.locutils.hpp"
|
||||
#include "boe.monster.hpp"
|
||||
#include "boe.combat.hpp"
|
||||
#include "boe.text.hpp"
|
||||
#include "boe.specials.hpp"
|
||||
#include "boe.items.hpp"
|
||||
@@ -856,8 +857,7 @@ void monst_inflict_fields(short which_monst) {
|
||||
break;
|
||||
}
|
||||
if(univ.town.is_force_cage(where_check.x,where_check.y))
|
||||
univ.town.monst[which_monst].status[eStatus::FORCECAGE] = 8;
|
||||
else univ.town.monst[which_monst].status[eStatus::FORCECAGE] = 0;
|
||||
process_force_cage(where_check, univ.get_target_i(*which_m));
|
||||
}
|
||||
if(univ.town.monst[which_monst].active > 0)
|
||||
for(i = 0; i < univ.town.monst[which_monst].x_width; i++)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "boe.town.hpp"
|
||||
#include "boe.combat.hpp"
|
||||
#include "boe.locutils.hpp"
|
||||
#include "boe.combat.hpp"
|
||||
#include "boe.text.hpp"
|
||||
#include "soundtool.hpp"
|
||||
#include "boe.main.hpp"
|
||||
@@ -1026,6 +1027,9 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) {
|
||||
}
|
||||
if(!freebie)
|
||||
univ.party[pc_num].cur_sp -= (*spell_num).cost;
|
||||
// Clear forcecage status
|
||||
for(int i = 0; i < 6; i++)
|
||||
univ.party[i].status[eStatus::FORCECAGE] = 0;
|
||||
add_string_to_buf(" You are moved... ");
|
||||
force_town_enter(univ.scenario.which_town_start,univ.scenario.where_start);
|
||||
start_town_mode(univ.scenario.which_town_start,9);
|
||||
@@ -1431,7 +1435,7 @@ void cast_town_spell(location where) {
|
||||
}
|
||||
} else if(univ.town.is_force_cage(where.x,where.y)) {
|
||||
add_string_to_buf(" Cage broken.");
|
||||
univ.town.set_force_cage(where.x,where.y,false);
|
||||
break_force_cage(where);
|
||||
}
|
||||
|
||||
else add_string_to_buf(" No barrier there.");
|
||||
@@ -1572,7 +1576,7 @@ void dispel_fields(short i,short j,short mode) {
|
||||
univ.town.set_blade_wall(i,j,false);
|
||||
r1 = get_ran(1,1,12) + mode;
|
||||
if(r1 < 3)
|
||||
univ.town.set_force_cage(i,j,false);
|
||||
break_force_cage(loc(i,j));
|
||||
}
|
||||
|
||||
bool pc_can_cast_spell(short pc_num,eSkill type) {
|
||||
|
||||
@@ -192,16 +192,16 @@ bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,
|
||||
return false; // TODO: Maybe replace eTrimType::CITY check with a blockage == clear/special && is_special() check?
|
||||
}
|
||||
|
||||
if(univ.town.is_force_barr(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Magic barrier!");
|
||||
can_enter = false;
|
||||
}
|
||||
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Force cage!");
|
||||
can_enter = false;
|
||||
}
|
||||
if((mode == eSpecCtx::TOWN_MOVE || (mode == eSpecCtx::COMBAT_MOVE && which_combat_type == 1))
|
||||
&& (univ.town.is_special(where_check.x,where_check.y))) {
|
||||
if(univ.town.is_force_barr(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Magic barrier!");
|
||||
return false;
|
||||
}
|
||||
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Force cage!");
|
||||
return false;
|
||||
}
|
||||
&& can_enter && univ.town.is_special(where_check.x,where_check.y)) {
|
||||
for(i = 0; i < univ.town->special_locs.size(); i++)
|
||||
if(where_check == univ.town->special_locs[i]) {
|
||||
*spec_num = univ.town->special_locs[i].spec;
|
||||
@@ -250,14 +250,6 @@ bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,
|
||||
put_pc_screen();
|
||||
univ.town.set_web(where_check.x,where_check.y,false);
|
||||
}
|
||||
if(univ.town.is_force_barr(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Magic barrier!");
|
||||
can_enter = false;
|
||||
}
|
||||
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" Force cage!");
|
||||
can_enter = false;
|
||||
}
|
||||
if(univ.town.is_crate(where_check.x,where_check.y)) {
|
||||
add_string_to_buf(" You push the crate.");
|
||||
to_loc = push_loc(from_loc,where_check);
|
||||
@@ -426,9 +418,6 @@ bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,
|
||||
case eStatus::FORCECAGE:
|
||||
if(is_out()) break;
|
||||
univ.party[i].sleep(eStatus::FORCECAGE,ter_flag1,ter_flag1 / 2);
|
||||
if(univ.party[i].status[eStatus::FORCECAGE] > 0)
|
||||
univ.town.set_force_cage(univ.party[i].get_loc().x, univ.party[i].get_loc().y, true);
|
||||
// TODO: Do we need to process fields here? Or is it done after returning from this function?
|
||||
break;
|
||||
case eStatus::MAIN: case eStatus::CHARM: // These magic values are illegal in this context
|
||||
break;
|
||||
@@ -549,11 +538,6 @@ void check_fields(location where_check,eSpecCtx mode,cPlayer& which_pc) {
|
||||
else hit_party(r1,eDamageType::MAGIC,0);
|
||||
fast_bang = 0;
|
||||
}
|
||||
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
||||
if(which_pc.status[eStatus::FORCECAGE] == 0)
|
||||
add_string_to_buf(" Trapped in force cage!");
|
||||
which_pc.status[eStatus::FORCECAGE] = 8;
|
||||
} else which_pc.status[eStatus::FORCECAGE] = 0;
|
||||
put_pc_screen();
|
||||
}
|
||||
|
||||
@@ -883,8 +867,6 @@ void use_item(short pc,short item) {
|
||||
break;
|
||||
case eItemUse::HARM_ONE:
|
||||
univ.party[pc].sleep(eStatus::FORCECAGE, str, str / 2);
|
||||
if(univ.party[pc].status[eStatus::FORCECAGE] > 0)
|
||||
univ.town.set_force_cage(univ.party[pc].get_loc().x, univ.party[pc].get_loc().y, true);
|
||||
break;
|
||||
case eItemUse::HELP_ALL:
|
||||
for(i = 0; i < 6; i++)
|
||||
@@ -892,13 +874,11 @@ void use_item(short pc,short item) {
|
||||
break;
|
||||
case eItemUse::HARM_ALL:
|
||||
univ.party.sleep(eStatus::FORCECAGE, str, str / 2);
|
||||
for(i = 0; i < 6; i++)
|
||||
if(univ.party[i].status[eStatus::FORCECAGE] > 0)
|
||||
univ.town.set_force_cage(univ.party[i].get_loc().x, univ.party[i].get_loc().y, true);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case eItemAbil::BLISS_DOOM:
|
||||
switch(type) {
|
||||
case eItemUse::HELP_ONE:
|
||||
@@ -1339,6 +1319,10 @@ void teleport_party(short x,short y,short mode) {
|
||||
if(mode == 0 || mode == 2) fadeOut = true;
|
||||
if(mode == 0 || mode == 3) fadeIn = true;
|
||||
|
||||
// Clear forcecage status
|
||||
for(int i = 0; i < 6; i++)
|
||||
univ.party[i].status[eStatus::FORCECAGE] = 0;
|
||||
|
||||
if(is_combat())
|
||||
mode = 1;
|
||||
|
||||
@@ -1403,6 +1387,10 @@ void change_level(short town_num,short x,short y) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear forcecage status
|
||||
for(int i = 0; i < 6; i++)
|
||||
univ.party[i].status[eStatus::FORCECAGE] = 0;
|
||||
|
||||
force_town_enter(town_num,l);
|
||||
end_town_mode(1,l);
|
||||
start_town_mode(town_num,9);
|
||||
@@ -1722,6 +1710,7 @@ void push_things() {
|
||||
case DIR_W: l.x--; break;
|
||||
}
|
||||
if(l != univ.town.p_loc) {
|
||||
// TODO: Will this push you into a placed forcecage or barrier? Should it?
|
||||
ASB("You get pushed.");
|
||||
if(univ.scenario.ter_types[ter].special == eTerSpec::CONVEYOR)
|
||||
draw_terrain(0);
|
||||
@@ -2922,7 +2911,7 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_STATUS:
|
||||
switch(eStatus(spec.ex2a)) {
|
||||
switch(eStatus(spec.ex1c)) {
|
||||
case eStatus::POISON:
|
||||
if(spec.ex1b == 0)
|
||||
pc->cure(spec.ex1a);
|
||||
@@ -2997,10 +2986,15 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
pc->apply_status(eStatus::ACID, -spec.ex1a);
|
||||
else pc->acid(spec.ex1a);
|
||||
break;
|
||||
case eStatus::FORCECAGE:
|
||||
if(is_out()) break;
|
||||
if(spec.ex1b == 0)
|
||||
pc->apply_status(eStatus::FORCECAGE, -spec.ex1a);
|
||||
else pc->sleep(eStatus::FORCECAGE, spec.ex1a, 10);
|
||||
break;
|
||||
// Invalid values
|
||||
case eStatus::MAIN:
|
||||
case eStatus::CHARM:
|
||||
case eStatus::FORCECAGE:
|
||||
break;
|
||||
}
|
||||
put_pc_screen();
|
||||
@@ -3888,7 +3882,12 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
break;
|
||||
} else {
|
||||
was_active = current_pc;
|
||||
univ.party.direction = end_town_combat();
|
||||
eDirection dir = end_town_combat();
|
||||
if(dir == DIR_HERE) {
|
||||
ASB("Can't change level now.");
|
||||
break;
|
||||
}
|
||||
univ.party.direction = dir;
|
||||
}
|
||||
}
|
||||
*a = 1;
|
||||
@@ -4067,6 +4066,10 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
univ.town.p_loc = univ.party.left_at;
|
||||
update_explored(univ.town.p_loc);
|
||||
center = univ.town.p_loc;
|
||||
|
||||
// Clear forcecage status
|
||||
for(int i = 0; i < 6; i++)
|
||||
univ.party[i].status[eStatus::FORCECAGE] = 0;
|
||||
} else change_level(univ.party.left_in, univ.party.left_at.x, univ.party.left_at.y);
|
||||
break;
|
||||
case eSpecType::TOWN_TIMER_START:
|
||||
|
||||
@@ -333,6 +333,12 @@ void start_town_mode(short which_town, short entry_dir) {
|
||||
case eMonstTime::ALWAYS:
|
||||
break;
|
||||
}
|
||||
|
||||
if(univ.town.monst[i].active) {
|
||||
// In forcecage?
|
||||
if(univ.town.is_force_cage(univ.town.monst[i].cur_loc.x, univ.town.monst[i].cur_loc.y))
|
||||
univ.town.monst[i].status[eStatus::FORCECAGE] = 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,7 +755,23 @@ void start_town_combat(eDirection direction) {
|
||||
|
||||
eDirection end_town_combat() {
|
||||
short num_tries = 0,r1,i;
|
||||
// TODO: Don't allow ending combat if someone is trapped in a forcecage or if two PCs are separated by an impassable wall with no way around it
|
||||
int in_cage = 0;
|
||||
location cage_loc;
|
||||
bool same_cage = true;
|
||||
for(int i = 0; i < 6; i++) {
|
||||
if(univ.party[i].status[eStatus::FORCECAGE] > 0) {
|
||||
if(in_cage == 0)
|
||||
cage_loc = univ.party[i].get_loc();
|
||||
in_cage++;
|
||||
}
|
||||
if(univ.party[i].get_loc() != cage_loc)
|
||||
same_cage = false;
|
||||
}
|
||||
if(in_cage != 0 && in_cage != univ.party.count() && !same_cage) {
|
||||
add_string_to_buf(" Someone trapped.");
|
||||
return DIR_HERE;
|
||||
}
|
||||
// TODO: Don't allow ending combat if two PCs are separated by an impassable wall with no way around it
|
||||
|
||||
r1 = get_ran(1,0,5);
|
||||
while(univ.party[r1].main_status != eMainStatus::ALIVE && num_tries++ < 1000)
|
||||
@@ -767,6 +789,17 @@ eDirection end_town_combat() {
|
||||
}
|
||||
|
||||
void place_party(short direction) {
|
||||
bool in_cage = false;
|
||||
for(int n = 0; n < 6; n++)
|
||||
if(univ.party[n].status[eStatus::FORCECAGE])
|
||||
in_cage = true;
|
||||
|
||||
if(in_cage) {
|
||||
for(int n = 0; n < 6; n++)
|
||||
univ.party[n].combat_pos = univ.town.p_loc;
|
||||
return;
|
||||
}
|
||||
|
||||
bool spot_ok[14] = {true,true,true,true,true,true,true,
|
||||
true,true,true,true,true,true,true};
|
||||
location pos_locs[14];
|
||||
|
||||
@@ -213,9 +213,9 @@ void cCreature::dumbfound(int how_much) {
|
||||
|
||||
// For charm, amount is the resulting attitude of the charmed monster; if 0, attitude is 2.
|
||||
void cCreature::sleep(eStatus which_status,int amount,int penalty) {
|
||||
if(which_status != eStatus::CHARM && which_status != eStatus::FORCECAGE && amount < 0) {
|
||||
if(which_status != eStatus::CHARM && amount < 0) {
|
||||
status[which_status] -= amount;
|
||||
if(which_status == eStatus::PARALYZED)
|
||||
if(which_status != eStatus::ASLEEP)
|
||||
status[which_status] = max(0, status[which_status]);
|
||||
return;
|
||||
}
|
||||
@@ -230,6 +230,8 @@ void cCreature::sleep(eStatus which_status,int amount,int penalty) {
|
||||
r1 /= magic_res;
|
||||
} else r1 = 200;
|
||||
r1 += penalty;
|
||||
if(which_status == eStatus::FORCECAGE && (mu > 0 || cl > 0))
|
||||
r1 += 5;
|
||||
if(which_status == eStatus::ASLEEP)
|
||||
r1 -= 25;
|
||||
if(which_status == eStatus::PARALYZED)
|
||||
@@ -247,7 +249,7 @@ void cCreature::sleep(eStatus which_status,int amount,int penalty) {
|
||||
attitude = amount;
|
||||
spell_note(23);
|
||||
} else if(which_status == eStatus::FORCECAGE) {
|
||||
status[eStatus::FORCECAGE] = 8;
|
||||
status[eStatus::FORCECAGE] = amount;
|
||||
spell_note(52);
|
||||
} else {
|
||||
status[which_status] = amount;
|
||||
|
||||
@@ -1109,7 +1109,6 @@ std::string cItem::getAbilName() const {
|
||||
case eStatus::POISONED_WEAPON:
|
||||
case eStatus::INVULNERABLE:
|
||||
case eStatus::MARTYRS_SHIELD:
|
||||
case eStatus::FORCECAGE:
|
||||
case eStatus::CHARM:
|
||||
case eStatus::INVISIBLE:
|
||||
break; // These have no negative aspect, so protection from them isn't implemented
|
||||
@@ -1123,6 +1122,7 @@ std::string cItem::getAbilName() const {
|
||||
case eStatus::DUMB: sout << "Dumbfounding"; break;
|
||||
case eStatus::ASLEEP: sout << "Sleep"; break;
|
||||
case eStatus::PARALYZED: sout << "Paralysis"; break;
|
||||
case eStatus::FORCECAGE: sout << "Forcecage"; break;
|
||||
}
|
||||
break;
|
||||
case eItemAbil::BOOST_STAT:
|
||||
@@ -1132,9 +1132,8 @@ std::string cItem::getAbilName() const {
|
||||
sout << "Occasional ";
|
||||
switch(eStatus(abil_data[1])) {
|
||||
case eStatus::MAIN: break; // Invalid
|
||||
case eStatus::FORCECAGE:
|
||||
case eStatus::CHARM: // Doesn't affect PCs
|
||||
break; // TODO: Not implemented?
|
||||
case eStatus::FORCECAGE: sout << (harmful ? "Entrapment" : "Release"); break;
|
||||
case eStatus::DISEASE: sout << (harmful ? "Disease" : "Cure Disease"); break;
|
||||
case eStatus::HASTE_SLOW: sout << (harmful ? "Slow" : "Haste"); break;
|
||||
case eStatus::BLESS_CURSE: sout << (harmful ? "Curse" : "Bless"); break;
|
||||
|
||||
@@ -30,6 +30,8 @@ void iLiving::apply_status(eStatus which, int how_much) {
|
||||
hi = 10;
|
||||
else if(which == eStatus::PARALYZED)
|
||||
hi = 5000;
|
||||
else if(which == eStatus::FORCECAGE)
|
||||
hi = 1000;
|
||||
|
||||
if(allow_negative.count(which))
|
||||
lo = -hi;
|
||||
|
||||
@@ -241,6 +241,14 @@ size_t cParty::free_space() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
size_t cParty::count(eMainStatus type) {
|
||||
size_t sz = 0;
|
||||
for(int i = 0; i < 6; i++)
|
||||
if(adven[i]->main_status == type)
|
||||
sz++;
|
||||
return sz;
|
||||
}
|
||||
|
||||
void cParty::swap_pcs(size_t a, size_t b) {
|
||||
if(a < 6 && b < 6)
|
||||
std::swap(adven[a], adven[b]);
|
||||
@@ -387,6 +395,21 @@ void cParty::dumbfound(int how_much) {
|
||||
}
|
||||
|
||||
void cParty::sleep(eStatus type, int how_much, int adj) {
|
||||
if(type == eStatus::FORCECAGE) {
|
||||
int who = 0;
|
||||
int best = 0;
|
||||
for(int i = 0; i < 6; i++) {
|
||||
int cur = adven[i]->skill(eSkill::MAGE_LORE) + adven[i]->skill(eSkill::MAGE_SPELLS) + adven[i]->skill(eSkill::PRIEST_SPELLS);
|
||||
if(adven[i]->is_alive() && cur > best) {
|
||||
best = cur;
|
||||
who = i;
|
||||
}
|
||||
}
|
||||
adven[who]->sleep(type, how_much, adj);
|
||||
for(int i = 0; i < 6; i++)
|
||||
adven[i]->status[eStatus::FORCECAGE] = adven[who]->status[eStatus::FORCECAGE];
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i < 6; i++)
|
||||
adven[i]->sleep(type, how_much, adj);
|
||||
}
|
||||
@@ -1039,9 +1062,11 @@ bool cParty::start_split(short x,short y,snd_num_t noise,short who) {
|
||||
left_in = univ.town.num;
|
||||
univ.town.p_loc.x = x;
|
||||
univ.town.p_loc.y = y;
|
||||
for(i = 0; i < 6; i++)
|
||||
for(i = 0; i < 6; i++) {
|
||||
if(i != who)
|
||||
adven[i]->main_status += eMainStatus::SPLIT;
|
||||
adven[i]->status[eStatus::FORCECAGE] = 0;
|
||||
}
|
||||
play_sound(noise);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ public:
|
||||
void new_pc(size_t spot);
|
||||
void replace_pc(size_t spot, cPlayer* with);
|
||||
size_t free_space();
|
||||
size_t count(eMainStatus type = eMainStatus::ALIVE);
|
||||
void void_pcs();
|
||||
bool save_talk(const std::string& who, const std::string& where, const std::string& str1, const std::string& str2);
|
||||
bool add_to_journal(const std::string& event, short day);
|
||||
|
||||
@@ -215,9 +215,12 @@ void cPlayer::sleep(eStatus what_type,int how_much,int adjust) {
|
||||
level = get_prot_level(eItemAbil::FREE_ACTION);
|
||||
how_much -= (what_type == eStatus::ASLEEP) ? level : level * 300;
|
||||
how_much -= get_prot_level(eItemAbil::STATUS_PROTECTION,int(what_type)) / 4;
|
||||
}
|
||||
} else if(what_type == eStatus::FORCECAGE)
|
||||
how_much -= 1 + get_prot_level(eItemAbil::STATUS_PROTECTION,int(what_type)) / 8;
|
||||
|
||||
short r1 = get_ran(1,1,100) + adjust;
|
||||
if(what_type == eStatus::FORCECAGE)
|
||||
r1 -= stat_adj(eSkill::MAGE_LORE);
|
||||
if(r1 < 30 + level * 2)
|
||||
how_much = -1;
|
||||
if(what_type == eStatus::ASLEEP && (traits[eTrait::HIGHLY_ALERT] || status[eStatus::ASLEEP] < 0))
|
||||
@@ -245,6 +248,8 @@ void cPlayer::sleep(eStatus what_type,int how_much,int adjust) {
|
||||
give_help(30,0);
|
||||
else if(what_type == eStatus::PARALYZED)
|
||||
give_help(32,0);
|
||||
else if(what_type == eStatus::FORCECAGE)
|
||||
give_help(46,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -742,10 +742,10 @@ pic_num_t choose_status_effect(short cur, bool party, cDialog* parent) {
|
||||
static const char*const status[] = {
|
||||
"Poisoned Weapon", "Bless/Curse", "Poison", "Haste/Slow", "Invulnerable",
|
||||
"Magic Resist/Amplify", "Webs", "Disease", "Sanctuary", "Dumbfounding/Enlightening",
|
||||
"Martyr's Shield", "Sleep/Hyperactivity", "Paralysis", "Acid"
|
||||
"Martyr's Shield", "Sleep/Hyperactivity", "Paralysis", "Acid", "Forcecage",
|
||||
};
|
||||
static const char*const pstatus[] = {"Stealth", "Flight", "Detect Life", "Firewalk"};
|
||||
static const std::vector<pic_num_t> status_pics = {4,2,0,6,5,9,10,11,12,13,14,15,16,17};
|
||||
static const std::vector<pic_num_t> status_pics = {4,2,0,6,5,9,10,11,12,13,14,15,16,17,20};
|
||||
static const std::vector<pic_num_t> pstatus_pics = {25,23,24,26};
|
||||
short prev = cur;
|
||||
if(cur < 0 || cur >= (party ? pstatus_pics : status_pics).size()) cur = 0;
|
||||
|
||||
Reference in New Issue
Block a user