#include "_sh_inc_list" #include "events_inc" //A function to spawn enemies void SpawnRandomEnemies(object oPC, string sAreaString, int nEncounterType) { //Identify a region by the area string string sRegionString = GetStringLeft(sAreaString, 1); //Get PC's level to determine enemies to spawn int nLevel = GetHitDice(oPC); //Modify the level to spawn weaker enemies for encounters other than with a "mini-boss" if (nEncounterType != ENCOUNTER_TYPE_MINIBOSS) { if (nLevel == 1) nLevel = 0; else if (nLevel <= 3) nLevel = 1; else if (nLevel <= 20) nLevel = nLevel - 2; else nLevel = nLevel - 3; //30% of the time (unless it's an encounter with a mini-boss) the PC will find weaker enemies if (Random(100) <= 29 && nLevel >= 4) { if (nLevel <= 20) nLevel = Random(nLevel-1); else nLevel = Random(21); } } //Select possible enemies SelectEnemies(sRegionString, nLevel, oPC, nEncounterType); string sEnemy1 = GetLocalString(oPC, "sEnemy1"); string sEnemy2 = GetLocalString(oPC, "sEnemy2"); string sEnemy3 = GetLocalString(oPC, "sEnemy3"); string sEnemy4 = GetLocalString(oPC, "sEnemy4"); string sEnemy5 = GetLocalString(oPC, "sEnemy5"); string sEnemy6 = GetLocalString(oPC, "sEnemy6"); string sAdd1 = GetLocalString(oPC, "sAdd1"); string sAdd2 = GetLocalString(oPC, "sAdd2"); string sAdd3 = GetLocalString(oPC, "sAdd3"); string sAdd4 = GetLocalString(oPC, "sAdd4"); DeleteEnemyVar(oPC); //Get the number of players in a party and spawn up to 5 times the number of enemies int nPlayers = 0; object oParty = GetFirstFactionMember(oPC); while (GetIsObjectValid(oParty)) { nPlayers++; //include henchmen in the count object oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oParty, 1); int k; for (k = 2; GetIsObjectValid(oAssociate); k++) { if (GetLocalInt(oAssociate, "HenchPosition") > 0) nPlayers++; oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oParty, k); } oParty = GetNextFactionMember(oPC); } if (nPlayers > 5) nPlayers = 5; int j; //Spawn 2 enemies for ENCOUNTER_TYPE_FEW, 3 for ENCOUNTER_TYPE_NORMAL and 4 for ENCOUNTER_TYPE_MANY if there are no adds location lWaypoint; string sWayTag; int nWayNumber; int i; object oCreature; if (sAdd1 == "") { //Spawning main enemies if (nEncounterType == ENCOUNTER_TYPE_FEW) nWayNumber = 2; if (nEncounterType == ENCOUNTER_TYPE_NORMAL) nWayNumber = 3; if (nEncounterType == ENCOUNTER_TYPE_MANY) nWayNumber = 4; if (nEncounterType == ENCOUNTER_TYPE_MINIBOSS) nWayNumber = 1; for (i = 1; i <= nWayNumber; i++) { sWayTag = "rand_wp_" + sAreaString + "_" + IntToString(i); lWaypoint = GetLocation(GetWaypointByTag(sWayTag)); for (j = 1; j <= nPlayers; j++) { oCreature = CreateObject(OBJECT_TYPE_CREATURE, SelectMain(sEnemy1, sEnemy2, sEnemy3, sEnemy4, sEnemy5, sEnemy6), lWaypoint); ScaleCreature(oCreature, nLevel); AssignCommand(oCreature, ActionRandomWalk()); } } } //Spawn 4 enemies (3 adds) for ENCOUNTER_TYPE_FEW, 5 (3 adds) for ENCOUNTER_TYPE_NORMAL and 6 (4 adds) for ENCOUNTER_TYPE_MANY if there are no adds else { //Spawning main enemies if (nEncounterType == ENCOUNTER_TYPE_FEW) nWayNumber = 1; if (nEncounterType == ENCOUNTER_TYPE_NORMAL) nWayNumber = 2; if (nEncounterType == ENCOUNTER_TYPE_MANY) nWayNumber = 2; if (nEncounterType == ENCOUNTER_TYPE_MINIBOSS) nWayNumber = 1; for (i = 1; i <= nWayNumber; i++) { sWayTag = "rand_wp_" + sAreaString + "_" + IntToString(i); lWaypoint = GetLocation(GetWaypointByTag(sWayTag)); for (j = 1; j <= nPlayers; j++) { oCreature = CreateObject(OBJECT_TYPE_CREATURE, SelectMain(sEnemy1, sEnemy2, sEnemy3, sEnemy4, sEnemy5, sEnemy6), lWaypoint); AssignCommand(oCreature, ActionRandomWalk()); } } //Spawning adds if (nEncounterType == ENCOUNTER_TYPE_FEW) nWayNumber = i+3; if (nEncounterType == ENCOUNTER_TYPE_NORMAL) nWayNumber = i+3; if (nEncounterType == ENCOUNTER_TYPE_MANY) nWayNumber = i+4; if (nEncounterType == ENCOUNTER_TYPE_MINIBOSS) nWayNumber = 0; for (i = i; i <= nWayNumber; i++) { if (nWayNumber == 0) break; //Nothing to do here if it's a mini-boss encounter sWayTag = "rand_wp_" + sAreaString + "_" + IntToString(i); lWaypoint = GetLocation(GetWaypointByTag(sWayTag)); for (j = 1; j <= nPlayers; j++) { oCreature = CreateObject(OBJECT_TYPE_CREATURE, SelectAdd(sAdd1, sAdd2, sAdd3, sAdd4), lWaypoint); AssignCommand(oCreature, ActionRandomWalk()); } } } } void main() { //Let's set this area's pseudo OnExit event (as a workaround for players quitting the game) SetLocalString(OBJECT_SELF, "OnExit", "pseudo_onexit"); //A workaround to enable selecting this area in the OnClientLeave event, since GetArea returns OBJECT_INVALID there SetLocalObject(GetEnteringObject(), "StoredArea", OBJECT_SELF); //A workaround to prevent the OnEnter event body from firing after loading a saved game if (GetLocalInt(GetModule(), "LoadCooldown") == TRUE) return; //Get the area we're in from the area variable, get the PC and declare some variables string sAreaString = GetLocalString(OBJECT_SELF, "AreaString"); object oPC = GetEnteringObject(); int nEncounterType; //Sanity check if (!GetIsPC(oPC)) return; //Add to the counter of PCs in the area SetLocalInt(OBJECT_SELF, "Players", GetLocalInt(OBJECT_SELF, "Players")+1); //TEST //FloatingTextStringOnCreature(IntToString(GetLocalInt(OBJECT_SELF, "Players")), oPC); //Determine whether a/chestvillage/dungeon should be possible to discover int nCanDiscover = GetLocalInt(oPC, "CanDiscover"); //Spawn an escorted client RetrieveClient(oPC); //Do nothing if there is already another PC in the area (and the area integer is TRUE) or try a spot/listen check if an ambush is prepared if (GetLocalInt(OBJECT_SELF, "IsPopulated") == TRUE) { int nDC = GetLocalInt(OBJECT_SELF, "Level"); if (nDC == 0) return; int nSkillRoll; string sString; nSkillRoll = GetSkillRank(SKILL_SPOT, oPC) + d20(); if (nSkillRoll >= nDC) sString = "[Spot] Ambush detected!"; else { nSkillRoll = GetSkillRank(SKILL_LISTEN, oPC) + d20(); if (nSkillRoll >= nDC) sString = "[Listen] Ambush detected!"; } FloatingTextStringOnCreature(sString, oPC, FALSE); WriteTimestampedLogEntry(GetName(oPC)+": populated, OnEnter stops"); //TEST return; } //Otherwise flag this area as populated, so that no event is generated for next players entering it SetLocalInt(OBJECT_SELF, "IsPopulated", TRUE); //Now select the leader of the entering party, stored earlied - we can delete this variable afterwards oPC = GetLocalObject(OBJECT_SELF, "PartyLeader"); DeleteLocalObject(OBJECT_SELF, "PartyLeader"); //TEST //FloatingTextStringOnCreature(GetName(oPC), oPC); //Delete this area from the list of free areas string sRegionString = GetStringLeft(sAreaString, 1); string sList = "sList"+sRegionString; object oList = GetLocalObject(GetModule(), sList); int i; for (i = 1; i <= ListGetElementCount(oList); i++) { if (ListGetString(oList, i) == sAreaString) ListRemoveElement(oList, i); } //TEST for (i = 1; i <= ListGetElementCount(oList); i++) { WriteTimestampedLogEntry(GetName(oPC)+": "+ListGetString(oList, i)); } int nRandom = Random(100)+1; WriteTimestampedLogEntry("Area event number (1-25: nothing, 26-80: monsters, 81-100: event): "+IntToString(nRandom)); //TEST //FloatingTextStringOnCreature("Wylosowano: "+IntToString(nRandom), oPC); //TEST //Spawn a random chest 5%+(players in party)*5% (max 25%) of the time on one of three chest waypoints (only if CanDiscover is TRUE) int nChestWaypoint; string sChestWaypoint; object oChestWp; int nPCs; object oPartyMember = GetFirstFactionMember(oPC, TRUE); while (oPartyMember != OBJECT_INVALID) { nPCs++; oPartyMember = GetNextFactionMember(oPC, TRUE); } int nPercentage = 5*nPCs+5; if (nPercentage >= 25) nPercentage = 25; int nTestRand = Random(100)+1; WriteTimestampedLogEntry("Chest: "+IntToString(nTestRand)+" | "+IntToString(nPercentage)); //TEST if (nTestRand <= nPercentage && nCanDiscover > 1) { WriteTimestampedLogEntry("Chest spawned"); //TEST nChestWaypoint = Random(3)+1; sChestWaypoint = IntToString(nChestWaypoint); oChestWp = GetWaypointByTag("chest_wp_"+sAreaString+"_"+sChestWaypoint); SpawnTreasureChest(oPC, oChestWp); } //Determine whether a PC encounters a village/dungeon at the end of the area if (Random(10) == 0) SetLocalInt(OBJECT_SELF, "DiscoveredVillage", TRUE); if (nCanDiscover > 1) { if (Random(20) == 0) SetLocalInt(OBJECT_SELF, "DiscoveredDungeon", TRUE); } //Nothing in particular happens 25% of the time if (nRandom <= 25) return; //Regular enemies encountered 55% of the time if (nRandom <= 80) { nRandom = Random(100)+1; //A single strong opponent is encountered 20% of the time if (nRandom <= 20) nEncounterType = ENCOUNTER_TYPE_MINIBOSS; //A few regular enemies are encountered 20% of the time else if (nRandom <= 40) nEncounterType = ENCOUNTER_TYPE_FEW; //Many regular enemies are encountered 20% of the time else if (nRandom <= 60) nEncounterType = ENCOUNTER_TYPE_MANY; //An average number of regular enemies is encountered 40% of the time else nEncounterType = ENCOUNTER_TYPE_NORMAL; } //An event occurs 20% of the time else { switch (Random(10)) { case 0: SpawnNemesisEvent(oPC); break; case 1: SpawnEscortEvent(oPC); break; case 2: SpawnTributeEvent(oPC); break; case 3: SpawnAmbushEvent(oPC); break; case 4: SpawnAdventurerEvent(oPC); break; case 5: SpawnDemonEvent(oPC); break; case 6: SpawnBulliedEvent(oPC); break; case 7: SpawnMerchantEvent(oPC); break; case 8: SpawnBossEvent(oPC); break; case 9: SpawnFriendlyEvent(oPC); break; } } //If it's a regular enemy encounter, spawn them SpawnRandomEnemies(oPC, sAreaString, nEncounterType); }