(WIP) Lands of Intrigue development thread

Spawn System:

  • NESS
    A heavily modified version of NESS is being used in the module to achieve local and ambient spawns.

To those of you who are familiarized with NESS, this version ignores the original tag based systems and relies heavily on a couple of custom flag that makes the system look for variables set on the spawn waypoints.

Some of those variables make up for the original system just telling it the usual spawning option (mainly cause I prefer to see the waypoints listed as “anim_badger_cete_3a5” or “ambient_woods” than “SP_RWR20_SR40_SN2M0_…”).

I realized taht to achieve real ambient spawns I need to use some kind of array system to sort out wich kind of creature spawn when asked by the system.

So if the Spawn Waypoint has a variable CR, the spawned creature CR is “adjusted” acording to the PC triggering the spawn; if the variables says it’s RS, the creature is sorted out from a randomization list (Like the church members spawn in Darromar Cloister of Ilmater, or the inn’s henchmen spawning waypoints); if the variable says CRS, it gets the type of creature from one 2da, and sorts out the creature according to the PC level from a creature list 2da. This is the case of humanoids (ie. bandits) or ambient spawns.

So, this is an example of an ambient farmlands list

amb_farmlands
0 15
1 anim_boar
2 anim_boar
3 anim_boar
4 anim_boar
5 anim_boar
6 anim_badger
7 anim_badger
8 anim_badger
9 anim_badger
10 anim_falcon
11 anim_falcon
12 anim_bear
13 anim_cougar
14 anim_wolf
15 verm_bt_stag

And this would be an example of a type list:

ANIMALS anim_bear
0 **** an_bear02
1 **** an_bear02
2 **** an_bear02
3 **** an_bear03
4 **** an_bear03
5 **** an_bear05
6 **** an_bear06
7 **** an_bear07
8 **** an_bear07
9 **** an_bear_dire09
10 **** an_bear07
11 **** an_bear_dire11
12 **** an_bear07
13 **** an_bear07
14 **** an_bear_dire14
15 **** an_bear07
16 **** an_bear_dire16
17 **** an_bear07
18 **** an_bear07
19 **** an_bear_dire19
20 **** an_bear_dire21

to do:
Upgrade the system to achieve a better CR adjustment. Right now when a PC triggers an ambient encounter, it first resolve the type of creature, and then it uses PC level to get a random option from the creature type list. So a lvl 6 PC triggers an ambient farmland encounter, the system gets a random (15) = 12, so a random (6+1) creature is spawned. You have 28.57% chance to face a lvl2 brown bear, 28.57% chance to face a lvl3 brown bear 14.28% chance to face a lvl4 brown bear, and the same chance for lvl 5 or 6.

You can also face a Bullette or a Hill Giant with a lvl 3 PC.

It’s not a really bad system for ambient spawning, so I’m using a “trigger triggered” vanilla system for specialized areas.

  • Vanilla Spawn System

The vanilla spawn system is being used to spawn specialized areas, like bandit occupied barns. To give it some kind of surprise and randomization effects, I use generic trigger to activate a random encounter at a time.

Barns from the farmlands or Dock warehouses use this type of spawn system.

  • All encounters starts deactivated.
  • All Darromar Dock Warehouses (ie.) are mapped in the same area (which minimaps get blacked out each time a PC enters).
  • Each Darromar Dock Warehouse has its own vanilla encounter trigger.
  • When a PC enters the area, an encounter is sorted out randomly and is set active. It remains active until exhausted.
  • After exhaustion, it waits until area is empty and a restart counter is started.
  • The system restarts and wait until a PC enters the area, and a random encounter is activated again.

This one has a beter CR adjustment but lacks the ambient side of NESS.

B.

1 Like

Some early pictures from Darromar:




Krimmevol Court

The Royal Palace of Faerntarn

B.

A ride to Masamount in Rivershire

And the village itself

B.

4 Likes

I decided it was time to move to County Varyth and Tor Arcana surroundings to do some finishing…

The farmlands around Tor Arcana:

Count Riiklas tower as background:

Tor Arcana quay (to cross Sulduskoon river):

And as I thought I was a little short of cave variants I decided it was time to add Merricksdad Winspear Hills placeables:

I think they look better in the toolset than ingame… (don’t know if it’s a shadows/lighting thing or what)

B.

2 Likes

I’ve been thinking of adding some kind of time persistency between sessions.

The general idea I’ve been working on is to store the time variables in campaign db (globally) each time a PC starts resting and on module load retrieve and set those time variables.

Has anybody tried this aproach? Is it the right way of doing it?

Time persistency

Finally I decided with this aproach to ensure time persistency between sessions.

OnPlayerRest:

after

case:         REST_EVENTTYPE_REST_FINISHED:
            // TDLI - Time persistency
            int iYear = GetCalendarYear();
            int iMonth = GetCalendarMonth();
            int iDay = GetCalendarDay();
            int iHour = GetTimeHour();
            int iMinute = GetTimeMinute();
            int iSecond = GetTimeSecond();
            SetCampaignInt ("DB_name","iYear",  iYear);
            SetCampaignInt ("DB_name","iMonth", iMonth);
            SetCampaignInt ("DB_name","iDay",   iDay);
            SetCampaignInt ("DB_name","iHour",  iHour);
            SetCampaignInt ("DB_name","iMinute",iMinute);
            SetCampaignInt ("DB_name","iSecond",iSecond);

and onModuleLoad (so it has nothing to do with players login in a cooperative scenario)

    //TDLI - Time persistency
    if (GetCampaignInt ("DB_name","iMonth") != 0) { //I needed to check against a value that could not be 0 if it was not null
        SetTime (
            GetCampaignInt ("DB_name","iHour"),
            GetCampaignInt ("DB_name","iMinute"),
            GetCampaignInt ("DB_name","iSecond"),
            0 //Don't give a **** about milliseconds
        );
        SetCalendar (
            GetCampaignInt ("DB_name","iYear"),
            GetCampaignInt ("DB_name","iMonth"),
            GetCampaignInt ("DB_name","iDay")
        );
    }

A small tour on County Varyth farmlands, rolling plains and hills…

…and a small teaser on followers.

1 Like

I found out that the plain ugly textures I was seeing were an error caused by NWNhak (at least my version) that did not recognize the .mtr extension when I repacked them. I used the merge option and it got solved.

There are only a couple of reasons why NwHak won’t recognise the .mtr extension. Either you used the NwHak that came with 1.69/Diamond or you haven’t updated your version of NwN EE in a very long time.

TR

1 Like

I think you’re right TR, I didn’t notice I was using a NWHak version sitting on my docs, last updated on 2014 :man_facepalming:t2:

After finishing up County Varyth hills…

… I’m turning back to County Rivershire and the Seminary of St. Ostus and its surrounding village.

Meanwhile I’m doing some planning around rumor & information, and generic task & quest systems.

B.

2 Likes

About the task system…

I’m thinking a “witcheresque” aproach with notice boards outside inns, tavers or some other key locations like the entrance to the Seminary of St. Ostus (image below).

The board will sort a random quest out of a prefixed table for the local area and send the PC to look after some NPC who will ask the PC to fullfill a task.

  • Some NPC might not have a task when PC reaches them (PC arrived “too late”).
  • Some NPC might reward the PC even if they have no task to offer
  • Some errands might be near, some might encourage travelling.

By now I’m testing tasks that involve carrying/gathering.

  • Once a task has been setup in a notice board, it will remain until accepted. Then it can re-roll.
  • The more boards in a location (more inns/taverns = more boards), more options to choose from.
  • Once taken from the board tasks can be rejected when the task giver NPC is reached.

The system uses a 2da table with the task data that is setup using custom tokens into NPC dialogs and PC journal.

It also stores and retrieves all the task data using the built-in database function.

Once I’m satisfied with the outcome I’ll start new variations.

More to come…

B.

(?) Simulated walkmesh helper

As I have been playing with some placeables that a proper walkmesh I decided to see if I could simulate a movement blocker that could be adjusted to any form.

So I draw a trigger around the placeable and added this onEnter script…

#include "x0_i0_position"

void no_walk_jump(object oPC, object NWA)
{
    if (GetIsInSubArea (oPC, NWA)) {
//      location locPCnew = GetBehindLocation (oPC);
        AssignCommand (oPC, ClearAllActions (TRUE));
//      AssignCommand (oPC, JumpToLocation (locPCnew));
        AssignCommand (oPC, ActionMoveAwayFromLocation (GetLocation (oPC), FALSE, 1.0));
        AssignCommand (oPC, ActionDoCommand (SetCommandable (TRUE, oPC)));
        AssignCommand (oPC, ActionDoCommand (no_walk_jump (oPC, NWA)));
        AssignCommand (oPC, SetCommandable (FALSE, oPC));

    }
}

void main()
{
    object oPC = GetEnteringObject ();
    if (GetIsInSubArea (oPC, OBJECT_SELF)) {
        no_walk_jump (oPC, OBJECT_SELF);
    }
}

I think it works wather well…

B.

1 Like

Here’s how the triggers are placed…

…and this is how it’s working.

AI won’t be able to find its path wisely (well…like 50% of the time), but I don’t dislike how it works.

B.

Well, now that last update is live I can move the spawning table from 2da to JSON arrays like this:

    //SCALING VARIABLES
    // Initialize Variables
    int nPC_count;
    int nPC_total_lvl;
    int nPC_Average_lvl;
    object oPC_Area = GetArea(OBJECT_SELF);
    // Cycle through PCs in Area
    object oPC_temp = GetFirstObjectInArea(oPC_Area);
    while (oPC_temp != OBJECT_INVALID)
    {
        if (GetIsPC(oPC_temp) == TRUE)
        {
            nPC_count++;
            nPC_total_lvl = nPC_total_lvl + GetHitDice(oPC_temp);
        }
        oPC_temp = GetNextObjectInArea(oPC_Area);
    }
    if (nPC_count == 0)
    {
        nPC_Average_lvl = 0;
    }
    else
    {
        nPC_Average_lvl = nPC_total_lvl / nPC_count;
    }

    /////////////////////////////////////
    //TABLA SPAWN ///////////////////////
    /////////////////////////////////////
    if (sTemplate == "tabla_spawn")
    {
        json spawnArray;
        string eModel =         GetLocalString (oSpawn, "eModel");
        string eModelCR =       GetLocalString (oSpawn, "eModelTabla");
        int iCRmax =            nPC_Average_lvl;
        if (eModelCR == "CR" || eModelCR == "CRS") {
            iCRmax = Random(iCRmax)+1;
        } else {
            iCRmax = 0;
        }
		/*************************************/
		/*************************************/
		/********* AMBIENT SPAWNS ************/
		/*************************************/
		/*************************************/
        if (eModel == "amb_farmlands") {		// FARMLANDS 		
            switch (20) {
                case 0:  { eModel = "mbst_worg"; break; }
                default: {
					switch (10) {
						case 0:  { eModel = "anim_boar"; break; }
						case 1:  { eModel = "anim_bear"; break; }
						case 2:  { eModel = "anim_cougar"; break; }
						case 3:  { eModel = "anim_falcon"; break; }
						case 4:  { eModel = "anim_wolf"; break; }
						default:  { eModel = "anim_badger"; break; }
					}
					break;
				}
            }
        }		
        if (eModel == "amb_hills") {			// HILLS 		
            switch (20) {
                case 0:  { eModel = "ogre_common"; break; }
				case 1:  { eModel = "ettin_common"; break; }
                case 2:  { eModel = "giant_hill_common"; break; }
				case 3:  { eModel = "giant_hill_common"; break; }				
                default: {
					switch (10) {
						case 0:  { eModel = "anim_badger"; break; }
						case 1:  { eModel = "anim_boar"; break; }
						case 2:  { eModel = "anim_cougar"; break; }
						case 3:  { eModel = "anim_falcon"; break; }
						case 4:  { eModel = "anim_wolf"; break; }
						default:  { eModel = "anim_bear"; break; }
					}
					break;
				}
            }
        }		
        if (eModel == "amb_plains") {			// PLAINS 		
            switch (20) {
                case 0:  { eModel = "ogre_common"; break; }
				case 1:  { eModel = "ettin_common"; break; }
                default: {
					switch (10) {
						case 0:  { eModel = "anim_badger"; break; }
						case 1:  { eModel = "anim_bear"; break; }
						case 2:  { eModel = "anim_cougar"; break; }
						case 3:  { eModel = "anim_falcon"; break; }
						case 4:  { eModel = "anim_wolf"; break; }
						default:  { eModel = "anim_boar"; break; }
					}
					break;
				}
            }
        }
        if (eModel == "amb_swamp") {			// SWAMP
            switch (20) {
                case 0:  { eModel = "ogre_common"; break; }
                default: { eModel = "anim_snake"; break; }
            }
        }			
        if (eModel == "amb_woods") {			// WOODS
            switch (20) {
                case 0:  { eModel = "ogre_common"; break; }
				case 1:  { eModel = "ettin_common"; break; }
                default: {
					switch (10) {
						case 0:  { eModel = "anim_badger"; break; }
						case 1:  { eModel = "anim_boar"; break; }
						case 2:  { eModel = "anim_cougar"; break; }
						case 3:  { eModel = "anim_falcon"; break; }
						case 4:  { eModel = "anim_bear"; break; }
						default:  { eModel = "anim_wolf"; break; }
					}
					break;
				}
            }
        }				
        /****************************/
		/****************************/
		/********* ANIMALS **********/
		/****************************/
		/****************************/
		if (eModel == "anim_badger") {		// BADGER - CR
            spawnArray = JsonParse (
                "[\"an_badger_01\",\"an_badger_02\",\"an_badger_dire03\",\"an_badger_dire05\",\"an_badger_dire05\","
                +"\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\","
                +"\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\","
                +"\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\",\"an_badger_dire06\"]"
            );
        }
		if (eModel == "anim_bat") {			// BAT - CR
            spawnArray = JsonParse (
                "[\"an_bat_01\",\"an_bat_01\",\"an_bat_dire_04\",\"an_bat_dire_04\",\"an_bat_dire_08\","
                +"\"an_bat_dire_08\",\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\","
                +"\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\","
                +"\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\",\"an_bat_dire_12\"]"
            );
        }		
        if (eModel == "anim_bear") {		// BEAR - CR
            spawnArray = JsonParse (
                "[\"an_bear02\",\"an_bear02\",\"an_bear03\",\"an_bear05\",\"an_bear05\","
                +"\"an_bear06\",\"an_bear06\",\"an_bear07\",\"an_bear_dire09\",\"an_bear_dire09\","
                +"\"an_bear_dire11\",\"an_bear_dire11\",\"an_bear_dire14\",\"an_bear_dire14\",\"an_bear_dire16\","
                +"\"an_bear_dire16\",\"an_bear_dire19\",\"an_bear_dire19\",\"an_bear_dire21\",\"an_bear_dire21\"]"
            );
        }
        if (eModel == "anim_boar") {		// BOAR - CR
            spawnArray = JsonParse (
                "[\"an_boar_03\",\"an_boar_03b\",\"an_boar_03c\",\"an_boar_04\",\"an_boar_04\","
                +"\"an_boar_dire5\",\"an_boar_dire5\",\"an_boar_dire7\",\"an_boar_dire7\",\"an_boar_dire9\","
                +"\"an_boar_dire11\",\"an_boar_dire11\",\"an_boar_dire11\",\"an_boar_dire11\",\"an_boar_dire11\","
                +"\"an_boar_dire11\",\"an_boar_dire11\",\"an_boar_dire11\",\"an_boar_dire11\",\"an_boar_dire11\"]"
            );
        }
        if (eModel == "anim_cougar") {		// COUGAR - CR ยก?
            spawnArray = JsonParse (
                "[\"an_cougar_02\",\"an_cougar_02\",\"an_cougar_02\",\"an_cougar_04\",\"an_cougar_04\","
                +"\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\","
                +"\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\","
                +"\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\",\"an_cougar_04\"]"
            );
        }		
        if (eModel == "anim_falcon") {		// FALCON - CR 
            spawnArray = JsonParse (
                "[\"an_falcon_01\",\"an_falcon_01\",\"an_falcon_04\",\"an_falcon_04\",\"an_falcon_07\","
                +"\"an_falcon_07\",\"an_falcon_07\",\"an_falcon_11\",\"an_falcon_11\",\"an_falcon_15\","
                +"\"an_falcon_15\",\"an_falcon_15\",\"an_falcon_15\",\"an_falcon_15\",\"an_falcon_15\","
                +"\"an_falcon_15\",\"an_falcon_15\",\"an_falcon_15\",\"an_falcon_15\",\"an_falcon_15\"]"
            );
		}			
        if (eModel == "anim_horse") {		// HORSE - Random
            spawnArray = JsonParse (
                "[\"an_horse_w\",\"an_horse_b\"]"
            );
        }
        if (eModel == "anim_snake") {		// SNAKE - CR
            spawnArray = JsonParse (
                "[\"an_snake_3\",\"an_snake_3\",\"an_snake_5\",\"an_snake_5\",\"an_snake_6\","
                +"\"an_snake_6\",\"an_snake_9\",\"an_snake_9\",\"an_snake_12\",\"an_snake_12\","
                +"\"an_snake_15\",\"an_snake_15\",\"an_snake_15\",\"an_snake_18\",\"an_snake_18\","
                +"\"an_snake_18\",\"an_snake_18\",\"an_snake_18\",\"an_snake_18\",\"an_snake_18\"]"
            );
		}		
        if (eModel == "anim_wolf") {		// WOLF - CR
            spawnArray = JsonParse (
                "[\"an_wolf_01\",\"an_wolf_02\",\"an_wolf_03\",\"an_wolf_04\",\"an_wolf_04\","
                +"\"an_wolf_dire05\",\"an_wolf_dire05\",\"an_wolf_dire07\",\"an_wolf_dire07\",\"an_wolf_dire07\","
                +"\"an_wolf_dire10\",\"an_wolf_dire10\",\"an_wolf_dire12\",\"an_wolf_dire12\",\"an_wolf_dire12\","
                +"\"an_wolf_dire12\",\"an_wolf_dire12\",\"an_wolf_dire12\",\"an_wolf_dire12\",\"an_wolf_dire12\"]"
            );
		}	
		/****************************/
		/****************************/
		/********** GIANTS **********/
		/****************************/
		/****************************/
		if (eModel == "ettin_common") {		// ETTIN - CR
            spawnArray = JsonParse (
                "[\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\","
                +"\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\","
                +"\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\","
                +"\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\",\"ettin_10\"]"
            );
        }				
		if (eModel == "giant_hill_common") {		// GIANT HILL - CR
            spawnArray = JsonParse (
                "[\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\","
                +"\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\","
                +"\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\","
                +"\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\",\"gnt_hill_12\"]"
            );
        }		
		if (eModel == "ogre_common") {		// OGRE - CR
            spawnArray = JsonParse (
                "[\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\","
                +"\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\","
                +"\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\","
                +"\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\",\"ogr_04\"]"
            );
        }				
        /****************************/
		/****************************/
		/******** HUMANOIDS *********/
		/****************************/
		/****************************/
		if (eModel == "kobold") {		// KOBOLDS - Sort 
            switch (10) {
                case 0:  { eModel = "kobold_caster"; break; }
				case 1:  { eModel = "kobold_boss"; break; }
				case 2:  { eModel = "kobold_boss"; break; }				
                default: { eModel = "kobold_melee"; break; }				
            }
        }	
		if (eModel == "kobold_boss") {		// KOBOLDS - Sort - BOSS
            switch (5) {
				case 0:  { eModel = "kobold_boss_hero"; break; }				
				case 1:  { eModel = "kobold_boss_trap"; break; }								
                default: { eModel = "kobold_boss_comm"; break; }				
            }
        }				
		if (eModel == "kobold_boss_comm") {		// KOBOLD - BOSS - Commander - CR
            spawnArray = JsonParse (
                "[\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\","
                +"\"kob_comm_07\",\"kob_comm_07\",\"kob_comm_07\",\"kob_comm_07\",\"kob_comm_07\","
                +"\"kob_comm_14\",\"kob_comm_14\",\"kob_comm_14\",\"kob_comm_14\",\"kob_comm_14\","
                +"\"kob_comm_20\",\"kob_comm_20\",\"kob_comm_20\",\"kob_comm_20\",\"kob_comm_20\"]"
            );
        }				
		if (eModel == "kobold_boss_hero") {		// KOBOLD - BOSS - Hero - CR
            spawnArray = JsonParse (
                "[\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\","
                +"\"kob_hero_06\",\"kob_hero_06\",\"kob_hero_06\",\"kob_hero_06\",\"kob_hero_06\","
                +"\"kob_hero_12\",\"kob_hero_12\",\"kob_hero_12\",\"kob_hero_12\",\"kob_hero_12\","
                +"\"kob_hero_18\",\"kob_hero_18\",\"kob_hero_18\",\"kob_hero_18\",\"kob_hero_18\"]"
            );
        }				
		if (eModel == "kobold_boss_comm") {		// KOBOLD - BOSS - Trapper - CR
            spawnArray = JsonParse (
                "[\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\",\"kob_comm_02\","
                +"\"kob_trap_08\",\"kob_trap_08\",\"kob_trap_08\",\"kob_trap_08\",\"kob_trap_08\","
                +"\"kob_trap_14\",\"kob_trap_14\",\"kob_trap_14\",\"kob_trap_14\",\"kob_trap_14\","
                +"\"kob_trap_20\",\"kob_trap_20\",\"kob_trap_20\",\"kob_trap_20\",\"kob_trap_20\"]"
            );
        }						
		if (eModel == "kobold_caster") {		// KOBOLDS - Sort - CASTER
            switch (5) {
				case 0:  { eModel = "kobold_caster_sham"; break; }				
				case 1:  { eModel = "kobold_caster_rdd"; break; }								
                default: { eModel = "kobold_caster_sorc"; break; }				
            }
        }						
		if (eModel == "kobold_caster_rdd") {	// KOBOLD - CASTER - Scale Sorcerer - CR
            spawnArray = JsonParse (
                "[\"kob_sor_05\",\"kob_sor_05\",\"kob_sor_05\",\"kob_rdd_06\",\"kob_rdd_06\","
                +"\"kob_rdd_06\",\"kob_rdd_06\",\"kob_rdd_10\",\"kob_rdd_10\",\"kob_rdd_10\","
                +"\"kob_rdd_10\",\"kob_rdd_15\",\"kob_rdd_15\",\"kob_rdd_15\",\"kob_rdd_15\","
                +"\"kob_rdd_20\",\"kob_rdd_20\",\"kob_rdd_20\",\"kob_rdd_20\",\"kob_rdd_20\"]"
            );
        }								
		if (eModel == "kobold_caster_sham") {	// KOBOLD - CASTER - Shaman - CR
            spawnArray = JsonParse (
                "[\"kob_sham_06\",\"kob_sham_06\",\"kob_sham_06\",\"kob_sham_06\",\"kob_sham_06\","
                +"\"kob_sham_06\",\"kob_sham_06\",\"kob_sham_06\",\"kob_sham_13\",\"kob_sham_13\","
                +"\"kob_sham_13\",\"kob_sham_13\",\"kob_sham_13\",\"kob_sham_13\",\"kob_sham_13\","
                +"\"kob_sham_20\",\"kob_sham_20\",\"kob_sham_20\",\"kob_sham_20\",\"kob_sham_20\"]"
            );
        }										
		if (eModel == "kobold_caster_sorc") {	// KOBOLD - CASTER - Sorcerer - CR
            spawnArray = JsonParse (
                "[\"kob_sor_05\",\"kob_sor_05\",\"kob_sor_05\",\"kob_sor_05\",\"kob_sor_05\","
                +"\"kob_sor_10\",\"kob_sor_10\",\"kob_sor_10\",\"kob_sor_10\",\"kob_sor_10\","
                +"\"kob_sor_15\",\"kob_sor_15\",\"kob_sor_15\",\"kob_sor_15\",\"kob_sor_15\","
                +"\"kob_sor_20\",\"kob_sor_20\",\"kob_sor_20\",\"kob_sor_20\",\"kob_sor_20\"]"
            );
        }								
		
		if (eModel == "kobold_melee") {		// KOBOLDS - Sort - MELEE
            switch (5) {
				case 0:  { eModel = "kobold_melee_dragon"; break; }				
				case 1:  { eModel = "kobold_melee_bully"; break; }
                default: { eModel = "kobold_melee_brute"; break; }				
            }
        }		
		if (eModel == "kobold_melee_brute") {		// KOBOLD - MELEE - Brute - CR
            spawnArray = JsonParse (
                "[\"kob_1\",\"kob_1\",\"kob_1\",\"kob_brute_05\",\"kob_brute_05\","
                +"\"kob_brute_05\",\"kob_brute_05\",\"kob_brute_10\",\"kob_brute_10\",\"kob_brute_10\","
                +"\"kob_brute_15\",\"kob_brute_15\",\"kob_brute_15\",\"kob_brute_15\",\"kob_brute_15\","
                +"\"kob_brute_20\",\"kob_brute_20\",\"kob_brute_20\",\"kob_brute_20\",\"kob_brute_20\"]"
            );
        }		
		if (eModel == "kobold_melee_bully") {		// KOBOLD - MELEE - Bully - CR
            spawnArray = JsonParse (
                "[\"kob_1\",\"kob_1\",\"kob_1\",\"kob_1\",\"kob_bully_06\","
                +"\"kob_bully_06\",\"kob_bully_06\",\"kob_bully_10\",\"kob_bully_10\",\"kob_bully_10\","
                +"\"kob_bully_15\",\"kob_bully_15\",\"kob_bully_15\",\"kob_bully_15\",\"kob_bully_15\","
                +"\"kob_bully_20\",\"kob_bully_20\",\"kob_bully_20\",\"kob_bully_20\",\"kob_bully_20\"]"
            );
        }				
		if (eModel == "kobold_melee_dragon") {		// KOBOLD - MELEE - Dragonshield - CR
            spawnArray = JsonParse (
                "[\"kob_1\",\"kob_1\",\"kob_1\",\"kob_1\",\"kob_1\","
                +"\"kob_1\",\"kob_1\",\"kob_drgs_08\",\"kob_drgs_08\",\"kob_drgs_12\","
                +"\"kob_drgs_12\",\"kob_drgs_16\",\"kob_drgs_16\",\"kob_drgs_16\",\"kob_drgs_16\","
                +"\"kob_drgs_20\",\"kob_drgs_20\",\"kob_drgs_20\",\"kob_drgs_20\",\"kob_drgs_20\"]"
            );
        }						
		/****************************/
		/****************************/
		/****** MAGICAL BEASTS ******/
		/****************************/
		/****************************/
		if (eModel == "mbst_worg") {		// WORG - CR
            spawnArray = JsonParse (
                "[\"mb_worg_04\",\"mb_worg_04\",\"mb_worg_04\",\"mb_worg_04\",\"mb_worg_06\","
                +"\"mb_worg_06\",\"mb_worg_09\",\"mb_worg_09\",\"mb_worg_09\",\"mb_worg_12\","
                +"\"mb_worg_12\",\"mb_worg_12\",\"mb_worg_12\",\"mb_worg_12\",\"mb_worg_12\","
                +"\"mb_worg_12\",\"mb_worg_12\",\"mb_worg_12\",\"mb_worg_12\",\"mb_worg_12\"]"
            );
        }
		/****************************/
		/****************************/		
        /********* CHURCHES *********/
		/****************************/
		/****************************/
        if (eModel == "chu_ilmater") {
            spawnArray = JsonParse (
                "[\"ilma_cle_hum04\",\"ilma_cle_huf04\","
                +"\"ilma_pal_hum04\",\"ilma_cle_huf04\","
                +"\"ilma_cle_hum04\",\"ilma_cle_huf04\"]"
            );
        }
		////////////////////////// Prepare sRetTemplate //////////////////////////
        string sRetTemplate;
        if (iCRmax == 0) {  //////// no CR adjustement
            sRetTemplate = JsonGetString(JsonArrayGet(spawnArray,Random(JsonGetLength(spawnArray))));
        } else {            //////// apply CR adjustement
            sRetTemplate = JsonGetString(JsonArrayGet(spawnArray,Random(iCRmax)));
        }
        return sRetTemplate;
    }

Of course the arrays showed are a WIP.

Two days and a lot of switches later I find out I missed the Random(x) in them.

:man_facepalming:t2:

Just building up some experience in building JSON data for future uses:

void main()
{
    object oPC = GetEnteringObject();
    json test = JsonParse (
        "{\"name\": \"nombre\", \"num\": 1}"
    );

    string test_string =
        IntToString (JsonGetLength(test))
        + "_" + JsonGetString(JsonPointer (test, "/name"))
        + "_" + IntToString(JsonGetInt(JsonPointer (test, "/num")));

    FloatingTextStringOnCreature (
        test_string,
        oPC,
        TRUE
    );
}

So far so good…

But, it gets better:

void main()
{
    object oPC = GetEnteringObject();
    json test = JsonParse (
        "{\"name\": \"nombre\", \"num\": 1}"
    );

    string sPCname = GetName (oPC);
    int    iPCgold = GetGold (oPC);

    json  jPatch = JsonParse (
         "[{ \"op\": \"replace\", \"path\": \"/name\", \"value\": \"" + sPCname              + "\" },"
        +" { \"op\": \"replace\", \"path\": \"/num\",  \"value\": "   + IntToString(iPCgold) + "}]"
    );
    test = JsonPatch ( test, jPatch);

    string test_string =
        IntToString (JsonGetLength(test))
        + "_" + JsonGetString(JsonPointer (test, "/name"))
        + "_" + IntToString(JsonGetInt(JsonPointer (test, "/num")));

    FloatingTextStringOnCreature (
        test_string,
        oPC,
        TRUE
    );
}

I think it’s a good starting point.

B.

1 Like

Thanks to @Proleric guidance i’m almost there on pre-spawn customizing npcs.

In this case using NESS.

The modification goes in the spawn_main.nss.

#include “nw_inc_gff” is needed on top.

    // Validate sSpawnTag
    if (sTemplate != "")
    {
        // Spawn
        if (nSpawnCamp == TRUE)
        {
            oSpawned = CampSpawn(oSpawn, sTemplate, lSpawnLocation);
            RecordSpawned(oSpawn, oSpawned, lHome, lEntranceExit, fSpawnFacing);
        }
        else
        {
            //This is original code replaced by the "if (nObjectType == 1) {..." secuence
            //We don't want any customization if oSpawned is not creature.
            //oSpawned = CreateObject(nObjectType, sTemplate, lSpawnLocation);
            if (nObjectType == 1) {
                json jTemplate = TemplateToJson (sTemplate, RESTYPE_UTC);
                int  iGender = JsonGetInt (GffGetByte (jTemplate, "Gender"));
                int  iApp    = JsonGetInt (GffGetWord (jTemplate, "Appearance_Type"));
                if ((iApp > -1 && iApp < 7) && iGender == 2 ) {
                    jTemplate = GffReplaceByte(jTemplate, "Gender", GENDER_FEMALE);
                }
                oSpawned = JsonToObject (jTemplate, lSpawnLocation, OBJECT_INVALID, TRUE);
            } else {
                oSpawned = CreateObject(nObjectType, sTemplate, lSpawnLocation);
            }
            SpawnDelayDebug(oSpawn, "spawned " + ObjectToString(oSpawned));
            RecordSpawned(oSpawn, oSpawned, lHome, lEntranceExit,
               fSpawnFacing);
            SetupSpawned(oSpawn, oSpawned, lHome, nTimeNow, nWalkToHome);
        }
    }
}

If Gender isn’t set to Both on the UTC and the appearance isn’t a playable race it won’t change anything.

To do:
Set up a random chance for gender 0/1
Set up a proper soundset & make a workaround for the vanilla spawn system.

B.

PS.:

                json voiceSet;
                int voiceFile;
                json jTemplate = TemplateToJson (sTemplate, RESTYPE_UTC);
                int  iGender = JsonGetInt (GffGetByte (jTemplate, "Gender"));
                int  iApp    = JsonGetInt (GffGetWord (jTemplate, "Appearance_Type"));
                if ((iApp > -1 && iApp < 7) && iGender == 2 ) {
                    if (d3(1) == 1) {
                        //female
                        iGender = 1;
                        voiceSet = JsonParse ("[357,358,359,360,361]");
					} else {
                        //male
                        iGender = 0;
                        voiceSet = JsonParse ("[363,364,365,366,367,368]");
                    }
                    //Set up voice set
                    jTemplate = GffReplaceByte(jTemplate, "Gender", iGender);
                    voiceFile = JsonGetInt(JsonArrayGet(voiceSet,Random(JsonGetLength(voiceSet))));
                    jTemplate = GffReplaceWord(jTemplate, "SoundSetFile", voiceFile);
                }

To Do
Workaround a solution for the vanilla spawn system.

B.

1 Like

Finally, I said goodbye to the Seminary of St. Ostus to move on to County Rivershire’s River Ith areas.

St. Ostus surrounding village:


Guest rooms:

Library:



Mess hall:

Temple:

B.

While refurbishing the task system to comply with a more JSONized style, I’m doing some finishing on areas, especially Inns & Taverns:

Asdefk’s Inn in Darromar, located west side of Star Street in the Temple Quarter, south of the River Ith, directly across the Wheel Market.

PC can find a couple of randomly generated adventurer followers, and Jozan (at least for now).

B.

1 Like