diff --git a/src/boe.actions.cpp b/src/boe.actions.cpp index bde25def..40a6f049 100644 --- a/src/boe.actions.cpp +++ b/src/boe.actions.cpp @@ -573,7 +573,7 @@ static void handle_talk(location destination, bool& did_something, bool& need_re if(s1 > 0) break; } - if(univ.town.monst[i].attitude % 2 == 1) { + if(!univ.town.monst[i].is_friendly()) { add_string_to_buf(" Creature is hostile."); } else if(univ.town.monst[i].summon_time > 0 || univ.town.monst[i].personality < 0) { short small_talk = 1; @@ -1929,10 +1929,10 @@ bool handle_keystroke(sf::Event& event){ case 'K': if(!in_scen_debug) break; for(i = 0; i < univ.town.monst.size(); i++) { - if((is_combat()) && (univ.town.monst[i].active > 0) && (univ.town.monst[i].attitude % 2 == 1)) + if(is_combat() && univ.town.monst[i].active > 0 && !univ.town.monst[i].is_friendly()) univ.town.monst[i].active = 0; - if((univ.town.monst[i].active > 0) && (univ.town.monst[i].attitude % 2 == 1) + if(univ.town.monst[i].active > 0 && !univ.town.monst[i].is_friendly() && (dist(univ.town.monst[i].cur_loc,univ.town.p_loc) <= 10) ) damage_monst(univ.town.monst[i], 7,1000,eDamageType::UNBLOCKABLE,0); } diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index 37a3d3f0..a953f4f3 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -288,13 +288,13 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,ter_num_t in_which_terr how_many = nums[i]; if(encounter.what_monst.monst[i] != 0) for(j = 0; j < how_many; j++) - set_up_monst(0,encounter.what_monst.monst[i]); + set_up_monst(eAttitude::HOSTILE_A,encounter.what_monst.monst[i]); } for(i = 0; i < 3; i++) { how_many = nums[i + 7]; if(encounter.what_monst.friendly[i] != 0) for(j = 0; j < how_many; j++) - set_up_monst(1,encounter.what_monst.friendly[i]); + set_up_monst(eAttitude::FRIENDLY,encounter.what_monst.friendly[i]); } // place PCs @@ -324,7 +324,7 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,ter_num_t in_which_terr univ.town.monst[i].cur_loc.x = get_ran(1,15,25); univ.town.monst[i].cur_loc.y = get_ran(1,14,18); - if(univ.town.monst[i].attitude == 2) + if(univ.town.monst[i].attitude == eAttitude::FRIENDLY) univ.town.monst[i].cur_loc.y += 9; else if((univ.town.monst[i].mu > 0) || (univ.town.monst[i].cl > 0)) univ.town.monst[i].cur_loc.y -= 4;//max(12,univ.town.monst[i].m_loc.y - 4); @@ -335,7 +335,7 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,ter_num_t in_which_terr (num_tries++ < 50)) { univ.town.monst[i].cur_loc.x = get_ran(1,15,25); univ.town.monst[i].cur_loc.y = get_ran(1,14,18); - if(univ.town.monst[i].attitude == 2) + if(univ.town.monst[i].attitude == eAttitude::FRIENDLY) univ.town.monst[i].cur_loc.y += 9; else if((univ.town.monst[i].mu > 0) || (univ.town.monst[i].cl > 0)) univ.town.monst[i].cur_loc.y -= 4;//max(12,univ.town.monst[i].m_loc.y - 4); @@ -421,15 +421,13 @@ bool pc_combat_move(location destination) { } else if(monst_hit != nullptr) { // s2 = 2 here appears to mean "go ahead and attack", while s2 = 1 means "cancel attack". - // Then s1 % 2 == 1 means the monster is hostile to the party. - s1 = dynamic_cast(monst_hit)->attitude; - if(s1 % 2 == 1) s2 = 2; + if(!monst_hit->is_friendly()) s2 = 2; else { std::string result = cChoiceDlog("attack-friendly",{"cancel","attack"}).show(); if(result == "cancel") s2 = 1; else if(result == "attack") s2 = 2; } - if((s2 == 2) && (s1 % 2 != 1)) + if(s2 == 2 && monst_hit->is_friendly()) make_town_hostile(); if(s2 == 2) { univ.party[current_pc].last_attacked = monst_hit; @@ -466,7 +464,7 @@ bool pc_combat_move(location destination) { s1 = current_pc; if((monst_exist > 0) && (monst_adjacent(univ.party[current_pc].combat_pos,i)) && !monst_adjacent(destination,i) && - (univ.town.monst[i].attitude % 2 == 1) && + !univ.town.monst[i].is_friendly() && univ.town.monst[i].status[eStatus::ASLEEP] <= 0 && univ.town.monst[i].status[eStatus::PARALYZED] <= 0) { combat_posing_monster = current_working_monster = 100 + i; @@ -1270,59 +1268,59 @@ void do_combat_cast(location target) { switch(spell_being_cast) { case eSpell::SIMULACRUM: r2 = get_ran(3,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster(store_sum_monst,target,r2,2,true)) + if(!summon_monster(store_sum_monst,target,r2,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_BEAST: r2 = get_ran(3,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if((summon == 0) || (!summon_monster(summon,target,r2,2,true))) + if((summon == 0) || (!summon_monster(summon,target,r2,eAttitude::FRIENDLY,true))) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_WEAK: r2 = get_ran(4,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if((summon == 0) || (!summon_monster(summon,target,r2,2,true))) + if((summon == 0) || (!summon_monster(summon,target,r2,eAttitude::FRIENDLY,true))) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON: case eSpell::SUMMON_AID: r2 = get_ran(5,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if((summon == 0) || (!summon_monster(summon,target,r2,2,true))) + if((summon == 0) || (!summon_monster(summon,target,r2,eAttitude::FRIENDLY,true))) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_MAJOR: case eSpell::SUMMON_AID_MAJOR: r2 = get_ran(7,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if((summon == 0) || (!summon_monster(summon,target,r2,2,true))) + if((summon == 0) || (!summon_monster(summon,target,r2,eAttitude::FRIENDLY,true))) add_string_to_buf(" Summon failed."); break; case eSpell::DEMON: r2 = get_ran(5,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster(85,target,r2,2,true)) + if(!summon_monster(85,target,r2,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_RAT: r1 = get_ran(3,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster(80,target,r1,2,true)) + if(!summon_monster(80,target,r1,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_SPIRIT: r2 = get_ran(2,1,5) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster(125,target,r2,2,true)) + if(!summon_monster(125,target,r2,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::STICKS_TO_SNAKES: r1 = get_ran(1,0,7); r2 = get_ran(2,1,5) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster((r1 == 1) ? 100 : 99,target,r2,2,true)) + if(!summon_monster((r1 == 1) ? 100 : 99,target,r2,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_HOST: // host r2 = get_ran(2,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster((i == 0) ? 126 : 125,target,r2,2,true)) + if(!summon_monster((i == 0) ? 126 : 125,target,r2,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_GUARDIAN: // guardian r2 = get_ran(6,1,4) + caster.stat_adj(eSkill::INTELLIGENCE); - if(!summon_monster(122,target,r2,2,true)) + if(!summon_monster(122,target,r2,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::FLASH_STEP: @@ -1348,7 +1346,7 @@ void do_combat_cast(location target) { add_string_to_buf(" Nobody there."); else { cCreature* cur_monst = dynamic_cast(victim); - if(cur_monst && cur_monst->attitude % 2 != 1 && spell_being_cast != eSpell::SCRY_MONSTER && spell_being_cast != eSpell::CAPTURE_SOUL) + if(cur_monst && cur_monst->is_friendly() && spell_being_cast != eSpell::SCRY_MONSTER && spell_being_cast != eSpell::CAPTURE_SOUL) make_town_hostile(); switch(spell_being_cast) { case eSpell::ACID_SPRAY: @@ -2216,7 +2214,7 @@ void do_monster_turn() { cur_monst = &univ.town.monst[i]; // See if hostile monster notices party, during combat - if((cur_monst->active == 1) && (cur_monst->attitude % 2 == 1) && (overall_mode == MODE_COMBAT)) { + if(cur_monst->active == 1 && !cur_monst->is_friendly() && overall_mode == MODE_COMBAT) { r1 = get_ran(1,1,100); // Check if see PCs first // TODO: Hang on, isn't stealth supposed to get better as you level up? r1 += (univ.party.status[ePartyStatus::STEALTH] > 0) ? 45 : 0; @@ -2229,12 +2227,12 @@ void do_monster_turn() { cur_monst->active = 2; } } - if((cur_monst->active == 1) && (cur_monst->attitude % 2 == 1)) { + if(cur_monst->active == 1 && !cur_monst->is_friendly()) { // Now it looks for PC-friendly monsters // dist check is for efficiency for(j = 0; j < univ.town.monst.size(); j++) if((univ.town.monst[j].active > 0) && - (univ.town.monst[j].attitude % 2 != 1) && + univ.town.monst[j].is_friendly() && (dist(cur_monst->cur_loc,univ.town.monst[j].cur_loc) <= 6) && (can_see_light(cur_monst->cur_loc,univ.town.monst[j].cur_loc,sight_obscurity) < 5)) cur_monst->active = 2; @@ -2242,9 +2240,9 @@ void do_monster_turn() { // See if friendly, fighting monster see hostile monster. If so, make mobile // dist check is for efficiency - if((cur_monst->active == 1) && (cur_monst->attitude == 2)) { + if(cur_monst->active == 1 && cur_monst->attitude == eAttitude::FRIENDLY) { for(j = 0; j < univ.town.monst.size(); j++) - if((univ.town.monst[j].active > 0) && (univ.town.monst[j].attitude % 2 == 1) && + if(univ.town.monst[j].active > 0 && !univ.town.monst[j].is_friendly() && (dist(cur_monst->cur_loc,univ.town.monst[j].cur_loc) <= 6) && (can_see_light(cur_monst->cur_loc,univ.town.monst[j].cur_loc,sight_obscurity) < 5)) { cur_monst->active = 2; @@ -2256,7 +2254,7 @@ void do_monster_turn() { cur_monst->ap = 0; if(cur_monst->active == 2) { // Begin action loop for angry, active monsters // First note that hostile monsters are around. - if(cur_monst->attitude % 2 == 1) + if(!cur_monst->is_friendly()) PSD[SDF_HOSTILES_PRESENT] = 30; // Give monster its action points @@ -2347,8 +2345,8 @@ void do_monster_turn() { // Draw w. monster in center, if can see if((cur_monst->ap > 0) && (is_combat()) // First make sure it has a target and is close, if not, don't bother - && (cur_monst->attitude > 0) && (cur_monst->picture_num > 0) - && ((target != 6) || (cur_monst->attitude % 2 == 1)) + && cur_monst->attitude != eAttitude::DOCILE + && (target != 6 || !cur_monst->is_friendly()) && (party_can_see_monst(i)) ) { center_on_monst = true; center = cur_monst->cur_loc; @@ -2396,13 +2394,10 @@ void do_monster_turn() { if(acted_yet) take_m_ap(1,cur_monst); } } - if((target != 6) && (cur_monst->attitude > 0) + if(target != 6 && cur_monst->attitude != eAttitude::DOCILE && (monst_can_see(i,targ_space)) && (can_see_monst(targ_space,i))) { // Begin spec. attacks -// sprintf((char *)create_line,"%d: %d %d %d",i,cur_monst->breath,cur_monst->mu,cur_monst->cl); -// add_string_to_buf((char *)create_line); - // Basic breath weapons if(cur_monst->abil[eMonstAbil::DAMAGE2].active && cur_monst->abil[eMonstAbil::DAMAGE2].gen.type == eMonstGen::BREATH && !acted_yet && get_ran(1,1,1000) < cur_monst->abil[eMonstAbil::DAMAGE2].gen.odds) { @@ -2518,11 +2513,11 @@ void do_monster_turn() { bool do_attack = false; if(dynamic_cast(&who)) { // Attack a PC only if hostile - if(cur_monst->attitude % 2 == 1) + if(!cur_monst->is_friendly()) do_attack = true; } else if(dynamic_cast(&who)) { // Attack a monster only if not docile - if(cur_monst->attitude > 0) + if(cur_monst->attitude != eAttitude::DOCILE) do_attack = true; } if(do_attack) { @@ -2546,7 +2541,7 @@ void do_monster_turn() { if(monst_hate_spot(i,&move_targ)) // First, maybe move out of dangerous space seek_party (i,cur_monst->cur_loc,move_targ); else { // spot is OK, so go nuts - if((cur_monst->attitude % 2 == 1) && (move_target < 6)) // Monsters seeking party do so + if(!cur_monst->is_friendly() && move_target < 6) // Monsters seeking party do so if(univ.party[move_target].main_status == eMainStatus::ALIVE) { seek_party (i,cur_monst->cur_loc,univ.party[move_target].combat_pos); for(k = 0; k < 6; k++) @@ -2562,14 +2557,14 @@ void do_monster_turn() { seek_party (i,cur_monst->cur_loc,univ.town.monst[move_target - 100].cur_loc); for(k = 0; k < 6; k++) if(univ.party[k].parry > 99 && monst_adjacent(univ.party[k].combat_pos,i) - && (cur_monst->active > 0) && (cur_monst->attitude % 2 == 1) + && cur_monst->active > 0 && !cur_monst->is_friendly() && !univ.party[k].traits[eTrait::PACIFIST]) { univ.party[k].parry = 0; pc_attack(k,cur_monst); } } - if(cur_monst->attitude == 0) { + if(cur_monst->attitude == eAttitude::DOCILE) { acted_yet = rand_move(i); futzing++; } @@ -2590,7 +2585,7 @@ void do_monster_turn() { if((overall_mode >= MODE_COMBAT) && (overall_mode < MODE_TALKING)) for(k = 0; k < 6; k++) if(univ.party[k].main_status == eMainStatus::ALIVE && !monst_adjacent(univ.party[k].combat_pos,i) - && (pc_adj[k]) && (cur_monst->attitude % 2 == 1) && (cur_monst->active > 0) && + && pc_adj[k] && !cur_monst->is_friendly() && cur_monst->active > 0 && univ.party[k].status[eStatus::INVISIBLE] == 0 && !univ.party[k].traits[eTrait::PACIFIST]) { combat_posing_monster = current_working_monster = k; pc_attack(k,cur_monst); @@ -2661,7 +2656,7 @@ void do_monster_turn() { combat_posing_monster = current_working_monster = -1; // Redraw monster after it goes - if((cur_monst->attitude > 0) && (cur_monst->active > 0) && (cur_monst->ap == 0) + if(cur_monst->attitude != eAttitude::DOCILE && cur_monst->active > 0 && cur_monst->ap == 0 && (is_combat()) && (cur_monst->picture_num > 0) && (party_can_see_monst(i) )) { center = cur_monst->cur_loc; draw_terrain(0); @@ -2805,8 +2800,8 @@ void monster_attack(short who_att,iLiving* target) { // add_string_to_buf((char *) create_line); // if target monster friendly to party, make able to attack - if(m_target != nullptr && m_target->attitude == 0) - m_target->attitude = 2; + if(m_target != nullptr && m_target->attitude == eAttitude::DOCILE) + m_target->attitude = eAttitude::FRIENDLY; // Attack roll r1 = get_ran(1,1,100); @@ -3256,7 +3251,7 @@ void monst_basic_abil(short m_num, std::pair abil, iLiving* break; // This only works on monsters case eStatus::CHARM: - target->sleep(abil.second.gen.stat, univ.town.monst[m_num].attitude, abil.second.gen.strength); + target->sleep(abil.second.gen.stat, int(univ.town.monst[m_num].attitude), abil.second.gen.strength); break; // These two don't make sense in this context case eStatus::MAIN: @@ -3435,8 +3430,8 @@ bool monst_cast_mage(cCreature *caster,short targ) { level = minmax(1,7,caster->mu - caster->status[eStatus::DUMB]) - 1; - target = find_fireball_loc(caster->cur_loc,1,(caster->attitude % 2 == 1) ? 0 : 1,&target_levels); - friend_levels_near = (caster->attitude % 2 != 1) ? count_levels(caster->cur_loc,3) : -1 * count_levels(caster->cur_loc,3); + target = find_fireball_loc(caster->cur_loc,1,caster->is_friendly(),&target_levels); + friend_levels_near = caster->is_friendly() ? count_levels(caster->cur_loc,3) : -1 * count_levels(caster->cur_loc,3); if((caster->health * 4 < caster->m_health) && (get_ran(1,0,10) < 9)) spell = emer_spells[level][3]; @@ -3484,15 +3479,11 @@ bool monst_cast_mage(cCreature *caster,short targ) { return false; // How about shockwave? Good idea? - if(spell == eSpell::SHOCKWAVE && caster->attitude % 2 != 1) + if(spell == eSpell::SHOCKWAVE && caster->is_friendly()) spell = eSpell::SUMMON_MAJOR; - if(spell == eSpell::SHOCKWAVE && caster->attitude % 2 == 1 && count_levels(caster->cur_loc,10) < 45) + if(spell == eSpell::SHOCKWAVE && !caster->is_friendly() && count_levels(caster->cur_loc,10) < 45) spell = eSpell::SUMMON_MAJOR; -// sprintf((char *)create_line,"m att %d trg %d trg2 x%dy%d spl %d mp %d tl:%d ",caster->attitude,targ, -// (short)target.x,(short)target.y,spell,caster->mp,target_levels); -// add_string_to_buf((char *) create_line); - l = caster->cur_loc; if(caster->direction < 4 && caster->x_width > 1) l.x++; @@ -3613,15 +3604,12 @@ bool monst_cast_mage(cCreature *caster,short targ) { break; case eSpell::SLOW_GROUP: play_sound(25); - if(caster->attitude % 2 == 1) + if(!caster->is_friendly()) for(i = 0; i < 6; i++) if(pc_near(i,caster->cur_loc,8)) univ.party[i].slow(2 + caster->level / 4); for(i = 0; i < univ.town.monst.size(); i++) { - if((univ.town.monst[i].active != 0) && - (((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude % 2 != 1)) || - ((univ.town.monst[i].attitude % 2 != 1) && (caster->attitude % 2 == 1)) || - ((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude != univ.town.monst[i].attitude))) + if(univ.town.monst[i].active != 0 && !caster->is_friendly(univ.town.monst[i]) && (dist(caster->cur_loc,univ.town.monst[i].cur_loc) <= 7)) univ.town.monst[i].slow(2 + caster->level / 4); } @@ -3761,8 +3749,8 @@ bool monst_cast_priest(cCreature *caster,short targ) { eSpell spell; - target = find_fireball_loc(caster->cur_loc,1,(caster->attitude % 2 == 1) ? 0 : 1,&target_levels); - friend_levels_near = (caster->attitude % 2 != 1) ? count_levels(caster->cur_loc,3) : -1 * count_levels(caster->cur_loc,3); + target = find_fireball_loc(caster->cur_loc,1,caster->is_friendly(),&target_levels); + friend_levels_near = caster->is_friendly() ? count_levels(caster->cur_loc,3) : -1 * count_levels(caster->cur_loc,3); if((caster->health * 4 < caster->m_health) && (get_ran(1,0,10) < 9)) spell = emer_spells[level][3]; @@ -3918,7 +3906,7 @@ bool monst_cast_priest(cCreature *caster,short targ) { play_sound(24); r1 = get_ran(2,0,2); r2 = get_ran(1,0,2); - if(caster->attitude % 2 == 1) + if(!caster->is_friendly()) for(i = 0; i < 6; i++) if(pc_near(i,caster->cur_loc,8)) { if(spell == eSpell::CURSE_ALL) @@ -3927,10 +3915,7 @@ bool monst_cast_priest(cCreature *caster,short targ) { univ.party[i].disease(2 + r2); } for(i = 0; i < univ.town.monst.size(); i++) { - if((univ.town.monst[i].active != 0) && - (((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude % 2 != 1)) || - ((univ.town.monst[i].attitude % 2 != 1) && (caster->attitude % 2 == 1)) || - ((univ.town.monst[i].attitude % 2 == 1) && (caster->attitude != univ.town.monst[i].attitude))) + if(univ.town.monst[i].active != 0 && !caster->is_friendly(univ.town.monst[i]) && (dist(caster->cur_loc,univ.town.monst[i].cur_loc) <= 7)) { if(spell == eSpell::CURSE_ALL) univ.town.monst[i].curse(2 + r1); @@ -4064,7 +4049,7 @@ short count_levels(location where,short radius) { for(i = 0; i < univ.town.monst.size(); i++) if(monst_near(i,where,radius,0)) { - if(univ.town.monst[i].attitude % 2 == 1) + if(!univ.town.monst[i].is_friendly()) store = store - univ.town.monst[i].level; else store = store + univ.town.monst[i].level; } @@ -4674,7 +4659,7 @@ bool out_monst_all_dead() { short i; for(i = 0; i < univ.town.monst.size(); i++) - if((univ.town.monst[i].active > 0) && (univ.town.monst[i].attitude % 2 == 1)) { + if(univ.town.monst[i].active > 0 && !univ.town.monst[i].is_friendly()) { //print_nums(5555,i,univ.town.monst[i].number); //print_nums(5555,univ.town.monst[i].m_loc.x,univ.town.monst[i].m_loc.y); return false; @@ -4886,7 +4871,7 @@ void combat_immed_mage_cast(short current_pc, eSpell spell_num, bool freebie) { break; } for(i = 0; i < univ.town.monst.size(); i++) { - if((univ.town.monst[i].active != 0) && (univ.town.monst[i].attitude % 2 == 1) + if(univ.town.monst[i].active != 0 && !univ.town.monst[i].is_friendly() && (dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= (*spell_num).range) && (can_see_light(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) { which_m = &univ.town.monst[i]; @@ -5053,7 +5038,7 @@ void combat_immed_priest_cast(short current_pc, eSpell spell_num, bool freebie) univ.party[current_pc].cur_sp -= (*spell_num).cost; store_sound = 24; for(i = 0; i < univ.town.monst.size(); i++) { - if((univ.town.monst[i].active != 0) &&(univ.town.monst[i].attitude % 2 == 1) && + if(univ.town.monst[i].active != 0 && !univ.town.monst[i].is_friendly() && (dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= (*spell_num).range)) { // TODO: Should this ^ also check that you can see each target? ie can_see_light(...) < 5 // --> can_see_light(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc,sight_obscurity) @@ -5288,7 +5273,7 @@ void process_force_cage(location loc, short i, short adjust) { 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) { + if(!which_m.is_friendly() && 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); diff --git a/src/boe.dlgutil.cpp b/src/boe.dlgutil.cpp index 2635b083..ea47a75d 100644 --- a/src/boe.dlgutil.cpp +++ b/src/boe.dlgutil.cpp @@ -1010,7 +1010,7 @@ void handle_talk_event(location p) { break; case eTalkNode::END_FIGHT: if(store_m_num >= 0 && store_m_num < univ.town.monst.size()) { - univ.town.monst[store_m_num].attitude = 1; + univ.town.monst[store_m_num].attitude = eAttitude::HOSTILE_A; univ.town.monst[store_m_num].mobility = 1; } talk_end_forced = true; diff --git a/src/boe.items.cpp b/src/boe.items.cpp index 7c2c3e1b..dfa1f33c 100644 --- a/src/boe.items.cpp +++ b/src/boe.items.cpp @@ -268,7 +268,7 @@ short get_item(location place,short pc_num,bool check_container) { short mass_get = 1; for(i = 0; i < univ.town.monst.size(); i++) - if((univ.town.monst[i].active > 0) && (univ.town.monst[i].attitude == 1) + if(univ.town.monst[i].active > 0 && !univ.town.monst[i].is_friendly() && (can_see_light(place,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) mass_get = 0; @@ -288,7 +288,7 @@ short get_item(location place,short pc_num,bool check_container) { if(item_near) if(display_item(place,pc_num,mass_get,check_container)) { // if true, there was a theft for(i = 0; i < univ.town.monst.size(); i++) - if((univ.town.monst[i].active > 0) && (univ.town.monst[i].attitude % 2 != 1) + if(univ.town.monst[i].active > 0 && univ.town.monst[i].is_friendly() && (can_see_light(place,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) { make_town_hostile(); add_string_to_buf("Your crime was seen!"); @@ -309,20 +309,19 @@ short get_item(location place,short pc_num,bool check_container) { } void make_town_hostile() { - set_town_attitude(0, -1, 1); + set_town_attitude(0, -1, eAttitude::HOSTILE_A); return; } // Set Attitude node adapted from *i, meant to replace make_town_hostile node -// att is any valid monster attitude (so, 0..3) -void set_town_attitude(short lo,short hi,short att) { +void set_town_attitude(short lo,short hi,eAttitude att) { short i,num; short a[3] = {}; // Dummy values to pass to run_special. if(which_combat_type == 0) return; give_help(53,0); - univ.town.monst.friendly = 1; + univ.town.monst.hostile = true; long long num_monst = univ.town.monst.size(); // Nice smart indexing, like Python :D @@ -342,8 +341,8 @@ void set_town_attitude(short lo,short hi,short att) { univ.town.monst[i].attitude = att; num = univ.town.monst[i].number; // If made hostile, make mobile - if(att == 1 || att == 3) { - + if(!univ.town.monst[i].is_friendly()) { + univ.town.monst.hostile = true; univ.town.monst[i].mobility = 1; // If a "guard", give a power boost if(univ.scenario.scen_monsters[num].guard) { @@ -359,7 +358,7 @@ void set_town_attitude(short lo,short hi,short att) { // In some towns, doin' this'll getcha' killed. // (Or something else! Killing the party would be the responsibility of whatever special node is called.) - if((att == 1 || att == 3) && univ.town->spec_on_hostile >= 0) + if(univ.town.monst.hostile && univ.town->spec_on_hostile >= 0) run_special(eSpecCtx::TOWN_HOSTILE, 2, univ.town->spec_on_hostile, univ.party.p_loc, &a[0], &a[1], &a[2]); } diff --git a/src/boe.items.hpp b/src/boe.items.hpp index 0ca9117c..bca34419 100644 --- a/src/boe.items.hpp +++ b/src/boe.items.hpp @@ -17,7 +17,7 @@ void set_item_flag(cItem *item); short get_item(location place,short pc_num,bool check_container); void make_town_hostile(); -void set_town_attitude(short lo,short hi,short att); +void set_town_attitude(short lo,short hi,eAttitude att); bool show_get_items(std::string titleText, std::vector& itemRefs, short pc_getting, bool overload = false); bool display_item(location from_loc,short pc_num,short mode, bool check_container); short custom_choice_dialog(std::array& strs,short pic_num,ePicType pic_type,std::array& buttons) ; diff --git a/src/boe.locutils.cpp b/src/boe.locutils.cpp index 00d1b75e..442ecc93 100644 --- a/src/boe.locutils.cpp +++ b/src/boe.locutils.cpp @@ -526,7 +526,7 @@ bool party_sees_a_monst() { // Returns true is a hostile monster is in sight. for(i = 0; i < univ.town.monst.size(); i++) { if(univ.town.monst[i].active > 0) - if((univ.town.monst[i].attitude == 1) && + if(!univ.town.monst[i].is_friendly() && (party_can_see_monst(i))) return true; } diff --git a/src/boe.monster.cpp b/src/boe.monster.cpp index b21d0ec0..c9274ba0 100644 --- a/src/boe.monster.cpp +++ b/src/boe.monster.cpp @@ -163,15 +163,14 @@ void get_monst_dims(mon_num_t monst,short *width, short *height) { } // Used to set up monsters for outdoor wandering encounters. -//mode; // 0 - unfriendly 1 - friendly & fightin' -void set_up_monst(short mode,mon_num_t m_num) { +void set_up_monst(eAttitude mode,mon_num_t m_num) { short which = univ.town.monst.size(); cMonster& monst = m_num >= 10000 ? univ.party.summons[m_num - 10000] : univ.scenario.scen_monsters[m_num]; univ.town.monst.assign(which, cCreature(m_num), monst, PSD[SDF_EASY_MODE], univ.difficulty_adjust()); univ.town.monst[which].active = 2; univ.town.monst[which].summon_time = 0; - univ.town.monst[which].attitude = mode + 1; + univ.town.monst[which].attitude = mode; univ.town.monst[which].mobility = 1; } @@ -195,7 +194,7 @@ void do_monsters() { target = 6; else target = select_active_pc(); } - if((univ.town.monst[i].attitude % 2 != 1) && (target < 6)) + if(univ.town.monst[i].is_friendly() && target < 6) target = 6; } univ.town.monst[i].target = target; @@ -203,18 +202,19 @@ void do_monsters() { // add_string_to_buf((char *) debug); if((univ.town.monst[i].active == 2) - || ((univ.town.monst[i].active != 0) && (univ.town.monst[i].attitude % 2 != 1))) { + || (univ.town.monst[i].active != 0 && univ.town.monst[i].is_friendly())) { acted_yet = false; - if(((univ.town.monst[i].attitude == 0) || (univ.town.monst[i].target == 6)) && (univ.town.hostile == 0)) { + // TODO: I don't think this univ.town.hostile flag is ever actually set. + if((univ.town.monst[i].attitude == eAttitude::DOCILE || univ.town.monst[i].target == 6) && !univ.town.monst.hostile) { if(univ.town.monst[i].mobility == 1) { // OK, it doesn't see the party or // isn't nasty, and the town isn't totally hostile. - if((univ.town.monst[i].attitude % 2 != 1) || (get_ran(1,0,1) == 0)) { + if(univ.town.monst[i].is_friendly() || get_ran(1,0,1) == 0) { acted_yet = rand_move(i); } else acted_yet = seek_party(i,univ.town.monst[i].cur_loc,univ.town.p_loc); } } - if((univ.town.monst[i].attitude > 0) || (univ.town.hostile == 1)) { + if(univ.town.monst[i].attitude != eAttitude::DOCILE || univ.town.monst.hostile) { if((univ.town.monst[i].mobility == 1) && (univ.town.monst[i].target != 6)) { l1 = univ.town.monst[i].cur_loc; l2 = (univ.town.monst[i].target <= 6) ? univ.town.p_loc : univ.town.monst[target - 100].cur_loc; @@ -236,7 +236,7 @@ void do_monsters() { // Make hostile monsters active - if((univ.town.monst[i].active == 1) && (univ.town.monst[i].attitude % 2 == 1) + if(univ.town.monst[i].active == 1 && !univ.town.monst[i].is_friendly() && (dist(univ.town.monst[i].cur_loc,univ.town.p_loc) <= 8)) { r1 = get_ran(1,1,100); r1 += (univ.party.status[ePartyStatus::STEALTH] > 0) ? 46 : 0; @@ -343,9 +343,9 @@ short monst_pick_target(short which_m) { // First, any chance target is screwed? if(univ.town.monst[which_m].target >= 100) { - if(((cur_monst->attitude % 2 == 1) && - (univ.town.monst[univ.town.monst[which_m].target - 100].attitude == cur_monst->attitude)) || - ((cur_monst->attitude % 2 == 0) && (univ.town.monst[univ.town.monst[which_m].target - 100].attitude % 2 == 0))) + if((!cur_monst->is_friendly() && + (univ.town.monst[univ.town.monst[which_m].target - 100].attitude == cur_monst->attitude)) || + (cur_monst->is_friendly() && univ.town.monst[univ.town.monst[which_m].target - 100].is_friendly())) univ.town.monst[which_m].target = 6; else if(univ.town.monst[univ.town.monst[which_m].target - 100].active == 0) univ.town.monst[which_m].target = 6; @@ -354,7 +354,7 @@ short monst_pick_target(short which_m) { if(univ.party[univ.town.monst[which_m].target].main_status != eMainStatus::ALIVE || get_ran(1,0,3) == 1) univ.town.monst[which_m].target = 6; - if((is_combat()) && (cur_monst->attitude % 2 == 1)) { + if(is_combat() && !cur_monst->is_friendly()) { if(spell_caster < 6) if((get_ran(1,1,5) < 5) && (monst_can_see(which_m,univ.party[spell_caster].combat_pos)) && univ.party[spell_caster].main_status == eMainStatus::ALIVE) @@ -387,10 +387,10 @@ short monst_pick_target(short which_m) { return 6; if(is_town()) { - if(cur_monst->attitude % 2 == 0) { + if(cur_monst->is_friendly()) { return targ_m; } - if((targ_m == 6) && (cur_monst->attitude % 2 == 1)) + if(targ_m == 6 && !cur_monst->is_friendly()) return 0; if(dist(cur_monst->cur_loc,univ.town.monst[targ_m - 100].cur_loc) < dist(cur_monst->cur_loc,univ.town.p_loc)) @@ -413,10 +413,7 @@ short monst_pick_target_monst(cCreature *which_m) { short min_dist = 1000,i,cur_targ = 6; for(i = 0; i < univ.town.monst.size(); i++) { - if((univ.town.monst[i].active > 0) && // alive - (((which_m->attitude % 2 == 1) && (univ.town.monst[i].attitude % 2 == 0)) || - ((which_m->attitude % 2 == 0) && (univ.town.monst[i].attitude % 2 == 1)) || - ((which_m->attitude % 2 == 1) && (univ.town.monst[i].attitude != which_m->attitude))) && // they hate each other + if(univ.town.monst[i].active > 0 && !which_m->is_friendly(univ.town.monst[i]) && // allve + they hate each other ((dist(which_m->cur_loc,univ.town.monst[i].cur_loc) < min_dist) || ((dist(which_m->cur_loc,univ.town.monst[i].cur_loc) == min_dist) && (get_ran(1,0,7) < 4))) && (monst_can_see(i,univ.town.monst[i].cur_loc)) ) { @@ -431,7 +428,7 @@ short monst_pick_target_monst(cCreature *which_m) { short monst_pick_target_pc(short m_num,cCreature *which_m) { short num_tries = 0,r1,store_targ = 6; - if(which_m->attitude % 2 == 0) + if(which_m->is_friendly()) return 6; if(is_town()) return 0; @@ -483,12 +480,11 @@ short closest_pc(location where) { } //mode; // 1 - closest hostile to PCs 2 - closest friendly to PCs -short closest_monst(location where,short mode) { +short closest_monst(location where,bool friendly) { short how_close = 200,i,store = 6; for(i = 0; i < univ.town.monst.size(); i++) - if((((univ.town.monst[i].attitude % 2 == 1) && (mode == 1)) || - ((univ.town.monst[i].attitude % 2 == 0) && (mode == 2))) + if(univ.town.monst[i].is_friendly() == friendly && (dist(where,univ.town.monst[i].cur_loc) < how_close)) { store = i; how_close = dist(where,univ.town.monst[i].cur_loc); @@ -503,14 +499,14 @@ short switch_target_to_adjacent(short which_m,short orig_target) { monst_loc = univ.town.monst[which_m].cur_loc; // First, take care of friendly monsters. - if(univ.town.monst[which_m].attitude % 2 == 0) { + if(univ.town.monst[which_m].is_friendly()) { if(orig_target >= 100) if((univ.town.monst[orig_target - 100].active > 0) && (monst_adjacent(univ.town.monst[orig_target - 100].cur_loc,which_m))) return orig_target; for(i = 0; i < univ.town.monst.size(); i++) if((univ.town.monst[i].active > 0) && - (univ.town.monst[i].attitude % 2 == 1) && + !univ.town.monst[i].is_friendly() && (monst_adjacent(univ.town.monst[i].cur_loc,which_m))) return i + 100; return orig_target; @@ -541,7 +537,7 @@ short switch_target_to_adjacent(short which_m,short orig_target) { // Check for a nice, adjacent, friendly monster and maybe attack for(i = 0; i < univ.town.monst.size(); i++) if((univ.town.monst[i].active > 0) && - (univ.town.monst[i].attitude % 2 == 0) && + univ.town.monst[i].is_friendly() && (monst_adjacent(univ.town.monst[i].cur_loc,which_m)) && (get_ran(1,0,2) < 2)) return i + 100; @@ -782,8 +778,8 @@ bool town_move_monster(short num,location dest) { } bool monster_placid(short m_num) { - if((univ.town.monst[m_num].attitude == 0) || - ((univ.town.monst[m_num].attitude == 2) && (PSD[SDF_HOSTILES_PRESENT] == 0))) { + if(univ.town.monst[m_num].attitude == eAttitude::DOCILE || + (univ.town.monst[m_num].attitude == eAttitude::FRIENDLY && PSD[SDF_HOSTILES_PRESENT] == 0)) { return true; } else { return false; @@ -920,7 +916,7 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon guts += which_m->health / 20; if(mage) guts = guts / 2; - if(which_m->attitude == 0) + if(which_m->attitude == eAttitude::DOCILE) guts = guts / 2; if((univ.town.is_antimagic(where_check.x,where_check.y)) && (mage)) @@ -953,7 +949,7 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon if(guts < 3) return false; } if(univ.town.is_fire_barr(where_check.x,where_check.y)) { - if((which_m->attitude % 2 == 1) && (get_ran(1,1,100) < (which_m->mu * 10 + which_m->cl * 4))) { + if(!which_m->is_friendly() && get_ran(1,1,100) < which_m->mu * 10 + which_m->cl * 4) { // TODO: Are these barrier sounds right? play_sound(60); which_m->spell_note(49); @@ -967,7 +963,7 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon } } if(univ.town.is_force_barr(where_check.x,where_check.y)) { /// Not in big towns - if((which_m->attitude % 2 == 1) && (get_ran(1,1,100) < (which_m->mu * 10 + which_m->cl * 4)) + if(!which_m->is_friendly() && get_ran(1,1,100) < which_m->mu * 10 + which_m->cl * 4 && (!univ.town->strong_barriers)) { play_sound(60); which_m->spell_note(49); @@ -1123,8 +1119,9 @@ short place_monster(mon_num_t which,location where,bool forced) { // One effect is resetting max health to ignore difficulty_adjust() static_cast(univ.town.monst[i]) = monst; univ.town.monst[i].attitude = monst.default_attitude; - if(univ.town.monst[i].attitude % 2 == 0) - univ.town.monst[i].attitude = 1; + // TODO: Huh? Why does it disallow friendly monster placements? + if(univ.town.monst[i].is_friendly()) + univ.town.monst[i].attitude = eAttitude::HOSTILE_A; univ.town.monst[i].mobility = 1; univ.town.monst[i].active = 2; univ.town.monst[i].cur_loc = where; @@ -1141,7 +1138,7 @@ short place_monster(mon_num_t which,location where,bool forced) { // returns true if placement was successful //which; // if in town, this is caster loc., if in combat, this is where to try // to put monster -bool summon_monster(mon_num_t which,location where,short duration,short given_attitude,bool by_party) { +bool summon_monster(mon_num_t which,location where,short duration,eAttitude given_attitude,bool by_party) { location loc; short spot; diff --git a/src/boe.monster.hpp b/src/boe.monster.hpp index 59f2925b..920adf5f 100644 --- a/src/boe.monster.hpp +++ b/src/boe.monster.hpp @@ -9,7 +9,7 @@ location get_monst_head(short m_num); short get_monst_picnum(mon_num_t monst); ePicType get_monst_pictype(mon_num_t monst); void get_monst_dims(mon_num_t monst,short *width, short *height); -void set_up_monst(short mode,mon_num_t m_num); +void set_up_monst(eAttitude mode,mon_num_t m_num); void do_monsters(); bool monst_hate_spot(short which_m,location *good_loc); short monst_pick_target(short which_m); @@ -17,7 +17,7 @@ short monst_pick_target_monst(cCreature *which_m); short monst_pick_target_pc(short m_num,cCreature *which_m); short select_active_pc(); short closest_pc(location where); -short closest_monst(location where,short mode); +short closest_monst(location where,bool friendly); short switch_target_to_adjacent(short which_m,short orig_target); bool rand_move(mon_num_t i); bool seek_party(short i,location l1,location l2); @@ -33,7 +33,7 @@ void monst_inflict_fields(short which_monst); bool monst_check_special_terrain(location where_check,short mode,short which_monst); void record_monst(cCreature* which_m, bool forced=false); short place_monster(mon_num_t which,location where,bool forced=false); -bool summon_monster(mon_num_t which,location where,short duration,short given_attitude,bool by_party); +bool summon_monster(mon_num_t which,location where,short duration,eAttitude given_attitude,bool by_party); void activate_monsters(short code,short attitude); short get_encumberance(short pc_num); mon_num_t get_summon_monster(short summon_class); diff --git a/src/boe.party.cpp b/src/boe.party.cpp index b0e33274..9b9b482e 100644 --- a/src/boe.party.cpp +++ b/src/boe.party.cpp @@ -726,7 +726,7 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { if(!freebie) univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(3,1,4) + adj; - if(!summon_monster(r1,where,store,2,true)) + if(!summon_monster(r1,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_WEAK: @@ -738,7 +738,7 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(4,1,4) + adj; for(i = 0; i < j; i++) - if(!summon_monster(r1,where,store,2,true)) + if(!summon_monster(r1,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON: @@ -750,14 +750,14 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(5,1,4) + adj; for(i = 0; i < j; i++) - if(!summon_monster(r1,where,store,2,true)) + if(!summon_monster(r1,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_AID: r1 = get_summon_monster(2); if(r1 < 0) break; store = get_ran(5,1,4) + adj; - if(!summon_monster(r1,where,store,2,true)) + if(!summon_monster(r1,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_MAJOR: @@ -769,26 +769,26 @@ void do_mage_spell(short pc_num,eSpell spell_num,bool freebie) { univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(7,1,4) + adj; for(i = 0; i < j; i++) - if(!summon_monster(r1,where,store,2,true)) + if(!summon_monster(r1,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::SUMMON_AID_MAJOR: r1 = get_summon_monster(3); if(r1 < 0) break; store = get_ran(7,1,4) + adj; - if(!summon_monster(r1,where,store,2,true)) + if(!summon_monster(r1,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eSpell::DEMON: store = get_ran(5,1,4) + 2 * adj; - if(!summon_monster(85,where,store,2,true)) + if(!summon_monster(85,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); else if(!freebie) univ.party[pc_num].cur_sp -= (*spell_num).cost; break; case eSpell::SUMMON_RAT: store = get_ran(5,1,4) + 2 * adj; - if(!summon_monster(80,where,store,2,true)) + if(!summon_monster(80,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; @@ -947,7 +947,7 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) { break; case eSpell::SUMMON_SPIRIT: - if(!summon_monster(125,where,get_ran(2,1,4) + adj,2,true)) + if(!summon_monster(125,where,get_ran(2,1,4) + adj,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); else if(!freebie) univ.party[pc_num].cur_sp -= (*spell_num).cost; @@ -959,7 +959,7 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) { for(i = 0; i < r1; i++) { r2 = get_ran(1,0,7); store = get_ran(2,1,5) + adj; - if(!summon_monster((r2 == 1) ? 100 : 99,where,store,2,true)) + if(!summon_monster((r2 == 1) ? 100 : 99,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); } break; @@ -967,17 +967,17 @@ void do_priest_spell(short pc_num,eSpell spell_num,bool freebie) { if(!freebie) univ.party[pc_num].cur_sp -= (*spell_num).cost; store = get_ran(2,1,4) + adj; - if(!summon_monster(126,where,store,2,true)) + if(!summon_monster(126,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); for(i = 0; i < 4; i++) { store = get_ran(2,1,4) + adj; - if(!summon_monster(125,where,store,2,true)) + if(!summon_monster(125,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); } break; case eSpell::SUMMON_GUARDIAN: store = get_ran(6,1,4) + adj; - if(!summon_monster(122,where,store,2,true)) + if(!summon_monster(122,where,store,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); else if(!freebie) univ.party[pc_num].cur_sp -= (*spell_num).cost; @@ -1504,9 +1504,10 @@ void do_mindduel(short pc_num,cCreature *monst) { adjust = (univ.party[pc_num].level + univ.party[pc_num].skill(eSkill::INTELLIGENCE)) / 2 - monst->level * 2; adjust += univ.party[pc_num].get_prot_level(eItemAbil::WILL) * 5; - if(monst->attitude % 2 != 1) + if(monst->is_friendly()) { make_town_hostile(); - monst->attitude = 1; + monst->attitude = eAttitude::HOSTILE_A; + } std::ostringstream sout; add_string_to_buf("Mindduel!"); diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index e6441e9e..b98cfc1c 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -97,6 +97,7 @@ bool handle_wandering_specials (short /*which*/,short mode) { // (I'm pretty sure it is, but I should verify it somehow.) // (It's either that or univ.party.p_loc.) short s1 = 0,s2 = 0,s3 = 0; + // TODO: If s2 > 0, encounter is forced (monsters don't flee even if they're weak) if((mode == 0) && (store_wandering_special.spec_on_meet >= 0)) { // When encountering run_special(eSpecCtx::OUTDOOR_ENC,1,store_wandering_special.spec_on_meet,univ.party.loc_in_sec,&s1,&s2,&s3); @@ -1133,14 +1134,14 @@ void use_item(short pc,short item) { else do_mage_spell(current_pc, spell, true); break; case eItemAbil::SUMMONING: - if(!summon_monster(univ.party[pc].items[item].abil_data[1],user_loc,str,2,true)) + if(!summon_monster(univ.party[pc].items[item].abil_data[1],user_loc,str,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eItemAbil::MASS_SUMMONING: r1 = get_ran(str,1,4); j = get_ran(1,3,5); for(i = 0; i < j; i++) - if(!summon_monster(univ.party[pc].items[item].abil_data[1],user_loc,r1,2,true)) + if(!summon_monster(univ.party[pc].items[item].abil_data[1],user_loc,r1,eAttitude::FRIENDLY,true)) add_string_to_buf(" Summon failed."); break; case eItemAbil::QUICKFIRE: @@ -1549,10 +1550,10 @@ bool damage_monst(cCreature& victim, short who_hit, short how_much, eDamageType victim.morale = victim.morale - 2; } - if((victim.attitude % 2 != 1) && (who_hit < 7) && - ((processing_fields && !monsters_going) || (processing_fields && !PSD[SDF_HOSTILES_PRESENT]))) { + if(victim.is_friendly() && who_hit < 7 && + ((!processing_fields && !monsters_going) || (processing_fields && !PSD[SDF_HOSTILES_PRESENT]))) { add_string_to_buf("Damaged an innocent."); - victim.attitude = 1; + victim.attitude = eAttitude::HOSTILE_A; make_town_hostile(); } @@ -3793,7 +3794,11 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, return; switch(cur_node.type) { case eSpecType::MAKE_TOWN_HOSTILE: - set_town_attitude(spec.ex1a,spec.ex1b,spec.ex2a); + if(spec.ex2a < 0 || spec.ex2a > 3){ + giveError("Invalid attitude (0-Friendly Docile, 1-Hostile A, 2-Friendly Will Fight, 3-Hostile B)."); + break; + } + set_town_attitude(spec.ex1a,spec.ex1b,eAttitude(spec.ex2a)); break; case eSpecType::TOWN_MOVE_PARTY: if(is_combat()) { @@ -3862,8 +3867,8 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, for(i = 0; i < univ.town.monst.size(); i++) if(univ.town.monst[i].active > 0 && (univ.town.monst[i].number == spec.ex1a || spec.ex1a == 0 || - (spec.ex1a == -1 && univ.town.monst[i].attitude % 2 == 0) || - (spec.ex1a == -2 && univ.town.monst[i].attitude % 2 == 1))) { + (spec.ex1a == -1 && univ.town.monst[i].is_friendly()) || + (spec.ex1a == -2 && !univ.town.monst[i].is_friendly()))) { univ.town.monst[i].active = 0; } *redraw = 1; @@ -4148,11 +4153,11 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, giveError("Tried to change the attitude of a nonexistent monster (should be 0...59)."); break; } - if((spec.ex1b < 0) || (spec.ex1b > 3)){ + if(spec.ex1b < 0 || spec.ex1b > 3){ giveError("Invalid attitude (0-Friendly Docile, 1-Hostile A, 2-Friendly Will Fight, 3-Hostile B)."); break; } - univ.town.monst[spec.ex1a].attitude = spec.ex1b; + univ.town.monst[spec.ex1a].attitude = eAttitude(spec.ex1b); break; case eSpecType::TOWN_RUN_MISSILE: if(which_mode == eSpecCtx::TALK) diff --git a/src/boe.text.cpp b/src/boe.text.cpp index 191a8028..6850c11d 100644 --- a/src/boe.text.cpp +++ b/src/boe.text.cpp @@ -707,12 +707,12 @@ short do_look(location space) { msg = get_m_name(univ.town.monst[i].number); if(univ.town.monst[i].health < univ.town.monst[i].m_health) { - if(univ.town.monst[i].attitude % 2 == 1) + if(!univ.town.monst[i].is_friendly()) msg = " Wounded " + msg + " (H)"; else msg = " Wounded " + msg + " (F)"; } else { - if(univ.town.monst[i].attitude % 2 == 1) + if(!univ.town.monst[i].is_friendly()) msg = " " + msg + " (H)"; else msg = " " + msg + " (F)"; } diff --git a/src/boe.town.cpp b/src/boe.town.cpp index 7d7eefa6..0c9f4b85 100644 --- a/src/boe.town.cpp +++ b/src/boe.town.cpp @@ -172,9 +172,8 @@ void start_town_mode(short which_town, short entry_dir) { univ.town.belt_present = true; } - univ.town.hostile = 0; univ.town.monst.which_town = town_number; - univ.town.monst.friendly = 0; + univ.town.monst.hostile = false; at_which_save_slot = univ.party.at_which_save_slot; @@ -361,7 +360,7 @@ void start_town_mode(short which_town, short entry_dir) { add_string_to_buf("Area has been abandoned."); for(i = 0; i < univ.town.monst.size(); i++) if((univ.town.monst[i].active > 0) && (univ.town.monst[i].active < 10) && - (univ.town.monst[i].attitude % 2 == 1)) + !univ.town.monst[i].is_friendly()) univ.town.monst[i].active += 10; town_toast = true; } diff --git a/src/classes/creatlist.cpp b/src/classes/creatlist.cpp index de231bd0..38fe18d6 100644 --- a/src/classes/creatlist.cpp +++ b/src/classes/creatlist.cpp @@ -20,7 +20,7 @@ void cPopulation::append(legacy::creature_list_type old){ for(int i = 0; i < 60; i++) dudes[i].append(old.dudes[i]); which_town = old.which_town; - friendly = old.friendly; + hostile = old.hostile; } const cCreature& cPopulation::operator[](size_t n) const { diff --git a/src/classes/creatlist.hpp b/src/classes/creatlist.hpp index 89048bb9..7de56eb7 100644 --- a/src/classes/creatlist.hpp +++ b/src/classes/creatlist.hpp @@ -34,7 +34,7 @@ class cPopulation { // formerly creature_list_type std::vector dudes; public: short which_town; - short friendly; + bool hostile; void append(legacy::creature_list_type old); void init(size_t n); diff --git a/src/classes/creature.cpp b/src/classes/creature.cpp index 12ab7228..fcc84877 100644 --- a/src/classes/creature.cpp +++ b/src/classes/creature.cpp @@ -18,7 +18,8 @@ const short cCreature::charm_odds[21] = {90,90,85,80,78, 75,73,60,40,30, 20,10,4,1,0, 0,0,0,0,0, 0}; cCreature::cCreature(){ - number = active = attitude = start_attitude = 0; + number = active = 0; + attitude = start_attitude = eAttitude::DOCILE; start_loc.x = start_loc.y = cur_loc.x = cur_loc.y = targ_loc.x = targ_loc.y = 80; mobility = 1; summon_time = 0; @@ -34,7 +35,7 @@ cCreature::cCreature(int num) : cCreature() { void cCreature::append(legacy::creature_data_type old){ active = old.active; - attitude = old.attitude; + attitude = eAttitude(old.attitude); number = old.number; cur_loc.x = old.m_loc.x; cur_loc.y = old.m_loc.y; @@ -46,7 +47,7 @@ void cCreature::append(legacy::creature_data_type old){ summon_time -= 100; } else party_summoned = true; number = old.monst_start.number; - start_attitude = old.monst_start.start_attitude; + start_attitude = eAttitude(old.monst_start.start_attitude); start_loc.x = old.monst_start.start_loc.x; start_loc.y = old.monst_start.start_loc.y; mobility = old.monst_start.mobile; @@ -245,8 +246,8 @@ void cCreature::sleep(eStatus which_status,int amount,int penalty) { } else { if(which_status == eStatus::CHARM) { - if(amount == 0 || amount > 3) amount = 2; - attitude = amount; + if(amount <= 0 || amount > 3) amount = 2; + attitude = eAttitude(amount); spell_note(23); } else if(which_status == eStatus::FORCECAGE) { status[eStatus::FORCECAGE] = amount; @@ -269,7 +270,18 @@ bool cCreature::is_alive() const { } bool cCreature::is_friendly() const { - return attitude % 2 == 0; + return attitude == eAttitude::DOCILE || attitude == eAttitude::FRIENDLY; +} + +bool cCreature::is_friendly(const iLiving& other) const { + if(is_friendly() != other.is_friendly()) + return false; + if(const cCreature* monst = dynamic_cast(&other)) { + if(!is_friendly()) return attitude == monst->attitude; + } + // If we get this far, both monsters are friendly to the player. + // (Or, maybe the other is a player rather than a monster.) + return true; } location cCreature::get_loc() const { @@ -366,9 +378,7 @@ void cCreature::readFrom(std::istream& file) { else if(cur == "ATTITUDE") line >> attitude; else if(cur == "STARTATT") { - unsigned int i; - line >> i; - start_attitude = i; + line >> start_attitude; } else if(cur == "STARTLOC") line >> start_loc.x >> start_loc.y; else if(cur == "LOCATION") diff --git a/src/classes/creature.hpp b/src/classes/creature.hpp index 1ba0e8b7..842cdd7e 100644 --- a/src/classes/creature.hpp +++ b/src/classes/creature.hpp @@ -18,7 +18,8 @@ class cCreature : public cMonster, public cTownperson, public iLiving { public: static const short charm_odds[21]; - short active, attitude; + short active; + eAttitude attitude; location cur_loc; short summon_time; bool party_summoned; @@ -53,6 +54,7 @@ public: bool is_alive() const; bool is_friendly() const; + bool is_friendly(const iLiving& other) const; bool is_shielded() const; int get_shared_dmg(int base_dmg) const; location get_loc() const; diff --git a/src/classes/estreams.cpp b/src/classes/estreams.cpp index a8b692b2..6e508c0c 100644 --- a/src/classes/estreams.cpp +++ b/src/classes/estreams.cpp @@ -616,3 +616,18 @@ std::istream& operator>> (std::istream& in, eTalkNode& node) { in.setstate(std::ios::failbit); return in; } + +// MARK: eAttitude + +cEnumLookup attitude_strs = {"docile", "hostile-a", "friendly", "hostile-b"}; + +std::ostream& operator<< (std::ostream& out, eAttitude att) { + writeEnum(out, att, attitude_strs, "docile"); + return out; +} + +std::istream& operator>> (std::istream& in, eAttitude& att) { + if(!readEnum(in, att, attitude_strs, eAttitude::DOCILE)) + in.setstate(std::ios::failbit); + return in; +} diff --git a/src/classes/living.hpp b/src/classes/living.hpp index c30a3a2a..249c8185 100644 --- a/src/classes/living.hpp +++ b/src/classes/living.hpp @@ -24,7 +24,8 @@ public: short marked_damage = 0; // for use during animations virtual bool is_alive() const = 0; - virtual bool is_friendly() const = 0; + virtual bool is_friendly() const = 0; // Return true if friendly to the party. + virtual bool is_friendly(const iLiving& other) const = 0; // Return true if friendly to living entity virtual bool is_shielded() const = 0; // Checks for martyr's shield in any form - status, monster abil, item abil virtual int get_shared_dmg(int base_dmg) const = 0; // And this goes with the above. diff --git a/src/classes/monster.cpp b/src/classes/monster.cpp index e3fa4e13..aed0097c 100644 --- a/src/classes/monster.cpp +++ b/src/classes/monster.cpp @@ -126,7 +126,7 @@ void cMonster::append(legacy::monster_record_type& old){ else poison_res = 100; x_width = old.x_width; y_width = old.y_width; - default_attitude = old.default_attitude; + default_attitude = eAttitude(old.default_attitude); summon_type = old.summon_type; default_facial_pic = old.default_facial_pic; if(default_facial_pic == 0) @@ -384,7 +384,8 @@ cMonster::cMonster(){ mindless = invuln = guard = invisible = false; level = m_health = armor = skill = 0; speed = 4; - default_facial_pic = default_attitude = 0; + default_facial_pic = 0; + default_attitude = eAttitude::DOCILE; ambient_sound = 0; corpse_item = corpse_item_chance = treasure = 0; mu = cl = 0; @@ -419,7 +420,7 @@ cTownperson::cTownperson(location loc, mon_num_t num, const cMonster& monst) : c void cTownperson::append(legacy::creature_start_type old){ number = old.number; - start_attitude = old.start_attitude; + start_attitude = eAttitude(old.start_attitude); start_loc.x = old.start_loc.x; start_loc.y = old.start_loc.y; mobility = old.mobile; @@ -851,6 +852,8 @@ void cMonster::readFrom(std::istream& file) { line >> picture_num; else if(cur == "SOUND") line >> ambient_sound; + else if(cur == "ATTITUDE") + line >> default_attitude; else { line >> temp1; if(cur == "LEVEL") @@ -869,8 +872,6 @@ void cMonster::readFrom(std::istream& file) { cl = temp1; else if(cur == "TREASURE") treasure = temp1; - else if(cur == "ATTITUDE") - default_attitude = temp1; else if(cur == "SUMMON") summon_type = temp1; } diff --git a/src/classes/monster.hpp b/src/classes/monster.hpp index f69613da..9cb26277 100644 --- a/src/classes/monster.hpp +++ b/src/classes/monster.hpp @@ -115,7 +115,7 @@ public: unsigned int poison_res; bool mindless, invuln, invisible, guard; unsigned int x_width,y_width; - unsigned int default_attitude; + eAttitude default_attitude; unsigned int summon_type; pic_num_t default_facial_pic; pic_num_t picture_num; @@ -133,7 +133,7 @@ public: class cTownperson { public: mon_num_t number; - unsigned int start_attitude; + eAttitude start_attitude; location start_loc; unsigned short mobility; eMonstTime time_flag; @@ -170,5 +170,7 @@ std::ostream& operator << (std::ostream& out, eFieldType e); std::istream& operator >> (std::istream& in, eFieldType& e); std::ostream& operator << (std::ostream& out, eMonstTime e); std::istream& operator >> (std::istream& in, eMonstTime& e); +std::ostream& operator<< (std::ostream& out, eAttitude node); +std::istream& operator>> (std::istream& in, eAttitude& node); std::ostream& operator<<(std::ostream& out, const cMonster::cAttack& att); #endif diff --git a/src/classes/party.cpp b/src/classes/party.cpp index 084c415c..ecd535c3 100644 --- a/src/classes/party.cpp +++ b/src/classes/party.cpp @@ -320,6 +320,10 @@ bool cParty::is_friendly() const { return true; } +bool cParty::is_friendly(const iLiving& other) const { + return other.is_friendly(); +} + bool cParty::is_shielded() const { return false; } @@ -582,6 +586,11 @@ void cParty::writeTo(std::ostream& file) const { file << "SOULCRYSTAL " << i << ' ' << imprisoned_monst[i] << '\n'; file << "DIRECTION " << direction << '\n'; file << "WHICHSLOT " << at_which_save_slot << '\n'; + for(int i = 0; i < 4; i++) { + file << "TOWNSAVE " << i << ' ' << creature_save[i].which_town; + if(creature_save[i].hostile) file << " HOSTILE"; + file << '\n'; + } for(int i = 0; i < 20; i++) if(alchemy[i]) file << "ALCHEMY " << i << '\n'; @@ -809,6 +818,11 @@ void cParty::readFrom(std::istream& file){ int i; sin >> i; alchemy[i] = true; + } else if(cur == "TOWNSAVE") { + int i; + std::string str; + bin >> i >> creature_save[i].which_town >> str; + creature_save[i].hostile = str == "HOSTILE"; } else if(cur == "TOWNVISIBLE") { int i; sin >> i; diff --git a/src/classes/party.hpp b/src/classes/party.hpp index 99a3aa16..b0a31ab7 100644 --- a/src/classes/party.hpp +++ b/src/classes/party.hpp @@ -142,6 +142,7 @@ public: bool is_alive() const; bool is_friendly() const; + bool is_friendly(const iLiving& other) const; bool is_shielded() const; int get_shared_dmg(int base_dmg) const; location get_loc() const; diff --git a/src/classes/pc.cpp b/src/classes/pc.cpp index 809e63f7..4a23cdf9 100644 --- a/src/classes/pc.cpp +++ b/src/classes/pc.cpp @@ -357,6 +357,18 @@ bool cPlayer::is_friendly() const { return true; } +bool cPlayer::is_friendly(const iLiving& other) const { + if(status[eStatus::CHARM] > 0) { + if(other.is_friendly()) return false; + // TODO: If charmed players becomes a thing, they should match the attitude (A or B) of whoever charmed them + if(const cCreature* monst = dynamic_cast(&other)) + return monst->attitude == eAttitude::HOSTILE_A; + // If we get here, the other is also a charmed player. + return true; + } + return other.is_friendly(); +} + bool cPlayer::is_shielded() const { if(status[eStatus::MARTYRS_SHIELD] > 0) return true; diff --git a/src/classes/pc.hpp b/src/classes/pc.hpp index 37cc3f94..99155c18 100644 --- a/src/classes/pc.hpp +++ b/src/classes/pc.hpp @@ -66,6 +66,7 @@ public: iLiving* last_attacked = nullptr; // Note: Currently this is assigned but never read bool is_alive() const; + bool is_friendly(const iLiving& other) const; bool is_friendly() const; bool is_shielded() const; int get_shared_dmg(int base_dmg) const; diff --git a/src/classes/simpletypes.hpp b/src/classes/simpletypes.hpp index 54e67f00..b1eeba52 100644 --- a/src/classes/simpletypes.hpp +++ b/src/classes/simpletypes.hpp @@ -171,6 +171,10 @@ enum class ePartyStatus { FIREWALK, }; +enum class eAttitude { + DOCILE, HOSTILE_A, FRIENDLY, HOSTILE_B +}; + enum class eMonstAbil { NO_ABIL, MISSILE, diff --git a/src/classes/universe.cpp b/src/classes/universe.cpp index 82c8e490..c3e94bb5 100644 --- a/src/classes/universe.cpp +++ b/src/classes/universe.cpp @@ -32,7 +32,6 @@ void cCurTown::append(legacy::current_town_type& old){ for(int i = 0; i < 64; i++) for(int j = 0; j < 64; j++) fields[i][j] = old.explored[i][j]; - hostile = old.hostile; monst.append(old.monst); in_boat = old.in_boat; p_loc.x = old.p_loc.x; @@ -775,7 +774,7 @@ void cCurOut::readFrom(std::istream& file) { void cCurTown::writeTo(std::ostream& file) const { file << "TOWN " << num << '\n'; file << "DIFFICULTY " << difficulty << '\n'; - if(hostile) file << "HOSTILE" << '\n'; + if(monst.hostile) file << "HOSTILE" << '\n'; file << "INBOAT " << in_boat << '\n'; file << "AT " << p_loc.x << ' ' << p_loc.y << '\n'; file << '\f'; @@ -817,7 +816,7 @@ void cCurTown::readFrom(std::istream& file){ else if(cur == "DIFFICULTY") sin >> difficulty; else if(cur == "HOSTILE") - hostile = true; + monst.hostile = true; else if(cur == "INBOAT") sin >> in_boat; else if(cur == "AT") diff --git a/src/classes/universe.hpp b/src/classes/universe.hpp index 2dbf76e1..da2e8e3e 100644 --- a/src/classes/universe.hpp +++ b/src/classes/universe.hpp @@ -45,7 +45,6 @@ public: // formerly current_town_type size_t num; // 200 if outdoors (my addition) short difficulty; - bool hostile; cPopulation monst; bool in_boat; // is this really needed? location p_loc; diff --git a/src/oldstructs.cpp b/src/oldstructs.cpp index 3ae1972a..c9932753 100644 --- a/src/oldstructs.cpp +++ b/src/oldstructs.cpp @@ -319,7 +319,7 @@ void debug_oldstructs() { STRUCT_INFO(creature_list_type); MEM_INFO(creature_list_type,dudes); MEM_INFO(creature_list_type,which_town); - MEM_INFO(creature_list_type,friendly); + MEM_INFO(creature_list_type,hostile); STRUCT_INFO(outdoor_creature_type); MEM_INFO(outdoor_creature_type,exists); MEM_INFO(outdoor_creature_type,direction); diff --git a/src/oldstructs.hpp b/src/oldstructs.hpp index 8aa97634..f5908404 100644 --- a/src/oldstructs.hpp +++ b/src/oldstructs.hpp @@ -245,7 +245,7 @@ namespace legacy { struct creature_list_type { creature_data_type dudes[60]; int16_t which_town; - int16_t friendly; + int16_t hostile; }; struct outdoor_creature_type { diff --git a/src/tools/porting.cpp b/src/tools/porting.cpp index 99ec6fe4..385bb36a 100644 --- a/src/tools/porting.cpp +++ b/src/tools/porting.cpp @@ -242,7 +242,7 @@ void port_party(legacy::party_record_type* old){ } for(i = 0; i < 4; i++){ flip_short(&old->creature_save[i].which_town); - flip_short(&old->creature_save[i].friendly); + flip_short(&old->creature_save[i].hostile); for(j = 0; j < 60; j++){ flip_short(&old->creature_save[i].dudes[j].active); flip_short(&old->creature_save[i].dudes[j].attitude); @@ -342,7 +342,7 @@ void port_c_town(legacy::current_town_type* old){ flip_short(&old->difficulty); port_town(&old->town); flip_short(&old->monst.which_town); - flip_short(&old->monst.friendly); + flip_short(&old->monst.hostile); for(j = 0; j < 60; j++){ flip_short(&old->monst.dudes[j].active); flip_short(&old->monst.dudes[j].attitude);