diff --git a/src/bestiary/bestiary.ws b/src/bestiary/bestiary.ws index 027ac07f..3b988ca4 100644 --- a/src/bestiary/bestiary.ws +++ b/src/bestiary/bestiary.ws @@ -18,7 +18,7 @@ class RER_Bestiary { } } - public latent function getRandomEntryFromBestiary(master: CRandomEncounters, encounter_type: EncounterType, optional for_bounty: bool, optional left_offset: CreatureType, optional right_offset: CreatureType, optional offset_multiplier: float): RER_BestiaryEntry { + public latent function getRandomEntryFromBestiary(master: CRandomEncounters, encounter_type: EncounterType, optional flags: RER_BestiaryRandomBestiaryEntryFlag, optional filter: RER_SpawnRollerFilter): RER_BestiaryEntry { var creatures_preferences: RER_CreaturePreferences; var spawn_roll: SpawnRoller_Roll; var manager : CWitcherJournalManager; @@ -32,15 +32,26 @@ class RER_Bestiary { creatures_preferences .setCurrentRegion(AreaTypeToName(theGame.GetCommonMapManager().GetCurrentArea())); - if (!for_bounty) { + if ((flags & RER_BREF_IGNORE_BIOMES) == 0) { creatures_preferences .setIsNight(theGame.envMgr.IsNight()) .setExternalFactorsCoefficient(master.settings.external_factors_coefficient) .setIsNearWater(master.rExtra.IsPlayerNearWater()) .setIsInForest(master.rExtra.IsPlayerInForest()) - .setIsInSwamp(master.rExtra.IsPlayerInSwamp()) + .setIsInSwamp(master.rExtra.IsPlayerInSwamp()); + } + else { + NLOG("getRandomEntryFromBestiary - ignore biomes"); + } + + if ((flags & RER_BREF_IGNORE_SETTLEMENT) == 0) { + + creatures_preferences .setIsInCity(master.rExtra.isPlayerInSettlement() || master.rExtra.getCustomZone(thePlayer.GetWorldPosition()) == REZ_CITY); } + else { + NLOG("getRandomEntryFromBestiary - ignore settlement"); + } for (i = 0; i < CreatureMAX; i += 1) { this.entries[i] @@ -68,7 +79,9 @@ class RER_Bestiary { } } - master.spawn_roller.setOffsets(left_offset, right_offset, offset_multiplier); + if (filter) { + master.spawn_roller.applyFilter(filter); + } spawn_roll = master.spawn_roller.rollCreatures( master.ecosystem_manager, @@ -256,4 +269,10 @@ class RER_Bestiary { -} \ No newline at end of file +} + +enum RER_BestiaryRandomBestiaryEntryFlag { + RER_BREF_NONE = 0, + RER_BREF_IGNORE_SETTLEMENT = 10, + RER_BREF_IGNORE_BIOMES = 01 +}; \ No newline at end of file diff --git a/src/bounty/bounty_manager.ws b/src/bounty/bounty_manager.ws index ac91a0e6..c12b67c4 100644 --- a/src/bounty/bounty_manager.ws +++ b/src/bounty/bounty_manager.ws @@ -161,10 +161,14 @@ statemachine class RER_BountyManager extends CEntity { current_bestiary_entry = this.master.bestiary.getRandomEntryFromBestiary( this.master, EncounterType_CONTRACT, - true, - CreatureDRACOLIZARD, - CreatureMAX, - 0.1 // creature outside the offset have -90% chance to appear + RER_BREF_IGNORE_BIOMES | RER_BREF_IGNORE_SETTLEMENT, + (new RER_SpawnRollerFilter in this) + .init() + .setOffsets( + CreatureDRACOLIZARD, + CreatureMAX, + 0.1 // creature outside the offset have -90% chance to appear + ) ); current_group_data.type = current_bestiary_entry.type; diff --git a/src/compositions/contract.ws b/src/compositions/contract.ws index 36e8ab5e..9b985657 100644 --- a/src/compositions/contract.ws +++ b/src/compositions/contract.ws @@ -42,8 +42,9 @@ class CreatureContractComposition extends CompositionSpawner { this.master, EncounterType_CONTRACT, , // for bounty - CreatureDRACOLIZARD, // left offset - CreatureMAX // right offset + (new RER_SpawnRollerFilter in this) + .init() + .setOffsets(CreatureDRACOLIZARD, CreatureMAX) ); this diff --git a/src/entities/contract/states/phases/ambush.ws b/src/entities/contract/states/phases/ambush.ws index bda7df48..76187229 100644 --- a/src/entities/contract/states/phases/ambush.ws +++ b/src/entities/contract/states/phases/ambush.ws @@ -31,8 +31,9 @@ state Ambush in RandomEncountersReworkedContractEntity { parent.master, EncounterType_CONTRACT, , // for bounty - CreatureHUMAN, // left offset - CreatureDRACOLIZARD // right offset + (new RER_SpawnRollerFilter in parent) + .init() + .setOffsets(CreatureDRACOLIZARD, CreatureMAX) ); parent.entities = bestiary_entry.spawn( diff --git a/src/entities/contract/states/phases/npc_rescue.ws b/src/entities/contract/states/phases/npc_rescue.ws index c1abae31..2ade6781 100644 --- a/src/entities/contract/states/phases/npc_rescue.ws +++ b/src/entities/contract/states/phases/npc_rescue.ws @@ -58,8 +58,9 @@ state NpcRescue in RandomEncountersReworkedContractEntity { parent.master, EncounterType_CONTRACT, , // for bounty - CreatureHUMAN, // left offset - CreatureDRACOLIZARD // right offset + (new RER_SpawnRollerFilter in parent) + .init() + .setOffsets(CreatureDRACOLIZARD, CreatureMAX) ); parent.entities = bestiary_entry.spawn( diff --git a/src/entities/contract/states/phases/trail_combat.ws b/src/entities/contract/states/phases/trail_combat.ws index 6522c683..9241542b 100644 --- a/src/entities/contract/states/phases/trail_combat.ws +++ b/src/entities/contract/states/phases/trail_combat.ws @@ -67,8 +67,9 @@ state TrailCombat in RandomEncountersReworkedContractEntity extends TrailPhase { parent.master, EncounterType_CONTRACT, , // for bounty - CreatureHUMAN, // left offset - CreatureDRACOLIZARD // right offset + (new RER_SpawnRollerFilter in parent) + .init() + .setOffsets(CreatureDRACOLIZARD, CreatureMAX) ); parent.entities = bestiary_entry.spawn( diff --git a/src/entities/nest/nest_entity.ws b/src/entities/nest/nest_entity.ws index 49769fcd..6dff3bb1 100644 --- a/src/entities/nest/nest_entity.ws +++ b/src/entities/nest/nest_entity.ws @@ -92,7 +92,17 @@ statemachine class RER_MonsterNest extends CMonsterNestEntity { SUH_makeEntitiesTargetPlayer(this.entities); } - event OnFireHit(source: CGameplayEntity) {} + event OnFireHit(source: CGameplayEntity) { + if (this.monsters_spawned_count > this.monsters_spawned_limit * 0.75) { + GetEncounter(); + wasExploded = true; + + interactionComponent.SetEnabled( false ); + airDmg = false; + + this.GotoState('Explosion'); + } + } event OnAardHit(sign: W3AardProjectile) {} event OnInteraction(actionName: string, activator: CEntity) { @@ -123,101 +133,37 @@ statemachine class RER_MonsterNest extends CMonsterNestEntity { return true; } - private latent function getRandomNestCreatureType(master: CRandomEncounters): CreatureType { - var spawn_roller: SpawnRoller; - var creatures_preferences: RER_CreaturePreferences; - var i: int; - var can_spawn_creature: bool; - var manager : CWitcherJournalManager; - var roll: SpawnRoller_Roll; - - spawn_roller = new SpawnRoller in this; - spawn_roller.fill_arrays(); - - creatures_preferences = new RER_CreaturePreferences in this; - creatures_preferences - .setIsNight(theGame.envMgr.IsNight()) - .setExternalFactorsCoefficient(master.settings.external_factors_coefficient) - .setIsNearWater(master.rExtra.IsPlayerNearWater()) - .setIsInForest(master.rExtra.IsPlayerInForest()) - .setIsInSwamp(master.rExtra.IsPlayerInSwamp()) - .setIsInCity(master.rExtra.isPlayerInSettlement() || master.rExtra.getCustomZone(thePlayer.GetWorldPosition()) == REZ_CITY) - .setCurrentRegion(AreaTypeToName(theGame.GetCommonMapManager().GetCurrentArea())); - - creatures_preferences - .reset(); - - master.bestiary.entries[CreatureGHOUL] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureALGHOUL] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureDROWNER] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureDROWNERDLC] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureROTFIEND] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureARACHAS] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureENDREGA] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureNEKKER] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureHARPY] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureSPIDER] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureCENTIPEDE] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureECHINOPS] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureKIKIMORE] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - master.bestiary.entries[CreatureSIREN] - .setCreaturePreferences(creatures_preferences, EncounterType_DEFAULT) - .fillSpawnRoller(spawn_roller); - - // we remove every unknown creatures from the spawning pool - if (master.settings.only_known_bestiary_creatures) { - manager = theGame.GetJournalManager(); - - for (i = 0; i < CreatureMAX; i += 1) { - can_spawn_creature = bestiaryCanSpawnEnemyTemplateList(master.bestiary.entries[i].template_list, manager); - - if (!can_spawn_creature) { - spawn_roller.setCreatureCounter(i, 0); - } - } - } + latent function getRandomNestCreatureType(master: CRandomEncounters): RER_BestiaryEntry { + var bentry: RER_BestiaryEntry; + + bentry = master.bestiary.getRandomEntryFromBestiary( + master, + EncounterType_CONTRACT, + RER_BREF_IGNORE_SETTLEMENT, + (new RER_SpawnRollerFilter in this) + .init() + .removeEveryone() + .allowCreature(CreatureARACHAS) + .allowCreature(CreatureENDREGA) + .allowCreature(CreatureGHOUL) + .allowCreature(CreatureALGHOUL) + .allowCreature(CreatureNEKKER) + .allowCreature(CreatureDROWNER) + .allowCreature(CreatureROTFIEND) + .allowCreature(CreatureWOLF) + .allowCreature(CreatureHARPY) + .allowCreature(CreatureSPIDER) + .allowCreature(CreatureCENTIPEDE) + .allowCreature(CreatureDROWNERDLC) + .allowCreature(CreatureBOAR) + .allowCreature(CreatureECHINOPS) + .allowCreature(CreatureKIKIMORE) + .allowCreature(CreatureSKELWOLF) + .allowCreature(CreatureSIREN) + .allowCreature(CreatureWRAITH) + ); - roll = spawn_roller.rollCreatures(master.ecosystem_manager); - return roll.roll; + return bentry; } timer function intervalLifeCheck(optional dt : float, optional id : Int32) { diff --git a/src/entities/nest/states/loading.ws b/src/entities/nest/states/loading.ws index 6d2fea77..ff3a75f6 100644 --- a/src/entities/nest/states/loading.ws +++ b/src/entities/nest/states/loading.ws @@ -9,13 +9,14 @@ state Loading in RER_MonsterNest { } entry function Loading_main() { - parent.bestiary_entry = parent.master.bestiary.getRandomEntryFromBestiary( - parent.master, - EncounterType_HUNTINGGROUND, - false, - CreatureARACHAS, // left offset - CreatureDRACOLIZARD // right offset - ); + parent.bestiary_entry = parent.getRandomNestCreatureType(parent.master); + + if (parent.bestiary_entry.type == CreatureARACHAS ) { + parent.monsters_spawned_limit /= 3; + } + if (parent.bestiary_entry.type == CreatureWRAITH) { + parent.monsters_spawned_limit /= 2; + } this.placeMarker(); diff --git a/src/entities/nest/states/spawning.ws b/src/entities/nest/states/spawning.ws index ad5d607f..820a3d07 100644 --- a/src/entities/nest/states/spawning.ws +++ b/src/entities/nest/states/spawning.ws @@ -7,13 +7,7 @@ state Spawning in RER_MonsterNest { entry function Spawning_main() { if (!parent.bestiary_entry) { - parent.bestiary_entry = parent.master.bestiary.getRandomEntryFromBestiary( - parent.master, - EncounterType_HUNTINGGROUND, - false, - CreatureARACHAS, // left offset - CreatureDRACOLIZARD // right offset - ); + parent.bestiary_entry = parent.getRandomNestCreatureType(parent.master); } // start by spawning a few monsters around the nest diff --git a/src/spawn_roller.ws b/src/spawn_roller.ws index 9ce2039d..5faadda8 100644 --- a/src/spawn_roller.ws +++ b/src/spawn_roller.ws @@ -60,38 +60,25 @@ class SpawnRoller { this.third_party_creatures_counters[type] = count; } - /** - * remove the creatures that are outside the range set by the offsets. - * The creatures passed for the offsets are NOT reset, it's everything OUTSIDE - * the range that is reset. - */ - public function setOffsets(optional left_offset: CreatureType, optional right_offset: CreatureType, optional multiplier: float) { - var can_apply_offset: bool; + public function applyFilter(filter: RER_SpawnRollerFilter) { + var can_apply_filter: bool; var i: int; - if ((int)right_offset == 0) { - right_offset = CreatureMAX - 1; - } - - // first we check if any creature IN the range has a spawn rate of 0 or more + // first we check if any creature IN the filter has a spawn rate of 1 or more // otherwise when we'll roll the SpawnRoller it will default to humans. - for (i = left_offset; i <= right_offset; i += 1) { - if (this.creatures_counters[i] > 0) { - can_apply_offset = true; + for (i = 0; i < CreatureMAX; i += 1) { + if ((int)(this.creatures_counters[i] * filter.multipliers[i]) > 0) { + can_apply_filter = true; break; } } - if (!can_apply_offset) { + if (!can_apply_filter) { return; } - for (i = 0; i < left_offset; i += 1) { - this.creatures_counters[i] = (int)(this.creatures_counters[i] * multiplier); - } - - for (i = right_offset + 1; i < CreatureMAX; i += 1) { - this.creatures_counters[i] = (int)(this.creatures_counters[i] * multiplier); + for (i = 0; i < CreatureMAX; i += 1) { + this.creatures_counters[i] = (int)(this.creatures_counters[i] * filter.multipliers[i]); } } @@ -216,3 +203,85 @@ struct SpawnRoller_Roll { var type: SpawnRoller_RollType; var roll: CreatureType; } + +/** + * This class, when applied to a spawn roller will filter the spawning pool + * from the creatures you configured. + * + * It offers a few methods to define different kinds of filters, such as: + * - left offset + * - right offset + * - remove a specific creature from the pool + */ +class RER_SpawnRollerFilter { + /** + * The index represents the CreatureType. + * The value is a multiplier that will be applied to the value the creature + * has in the spawn_roller. + * + * For example, to remove a creature from the pool the multiplier should be + * set at 0. A multiplier of 1 won't change anything. + */ + public var multipliers: array; + + /** + * MUST be called before doing anything or else the array will be empty and + * will filter out everyone. + */ + public function init(): RER_SpawnRollerFilter { + var i: int; + + for (i = 0; i < CreatureMAX; i += 1) { + this.multipliers.PushBack(1); + } + + return this; + } + + public function allowCreature(type: CreatureType): RER_SpawnRollerFilter { + this.multipliers[(int)type] = 1; + + return this; + } + + public function removeCreature(type: CreatureType): RER_SpawnRollerFilter { + this.multipliers[(int)type] = 0; + + return this; + } + + public function removeEveryone(): RER_SpawnRollerFilter { + var i: int; + + for (i = 0; i < CreatureMAX; i += 1) { + this.multipliers[i] = 0; + } + + return this; + } + + /** + * remove the creatures that are outside the range set by the offsets. + * The creatures passed for the offsets are NOT reset, it's everything OUTSIDE + * the range that is reset. + */ + public function setOffsets(optional left_offset: CreatureType, optional right_offset: CreatureType, optional multiplier: float): RER_SpawnRollerFilter { + var can_apply_offset: bool; + var i: int; + + if ((int)right_offset == 0) { + right_offset = CreatureMAX - 1; + } + + for (i = 0; i < left_offset; i += 1) { + this.multipliers[i] = multiplier; + } + + for (i = right_offset + 1; i < CreatureMAX; i += 1) { + this.multipliers[i] = multiplier; + } + + return this; + } + +} \ No newline at end of file