Hi there,
I wanted to have a go at writing my own function that would allow me to more easily populate a module with NPCs since it can get a bit annoying typing in all the info everytime in the .git file. So I had a go and came up with this:
#include "k_inc_generic"
#include "k_inc_utility"
int Populate(int Amount, string Tag[], Location loc[]);
int Populate(int Amount, string Tag[], Location loc[])
{
int AutoInc = 0;
while(AutoInc < Amount)
{
CreateObject(OBJECT_TYPE_CREATURE, Tag[AutoInc], Location(Vector(Location loc[AutoInc]));
AssignCommand(Tag[AutoInc],ActionRandomWalk());
AutoInc++;
}
return 0;
}
int main() {
string Tags[3];
location loca[3]
float Orient[3];
Tags[0] = "npc_tag1";
Tags[1] = "npc_tag2";
Tags[2] = "npc_tag3";
loca[0] = Location(Vector(0.0, 0.0, 0.0), 0.0);
loca[1] = Location(Vector(0.0, 0.0, 0.0), 0.0);
loca[2] = Location(Vector(0.0, 0.0, 0.0), 0.0);
Populate(3, Tags, loca);
return 0;
}
I then found out that arrays aren't supported. So is there any other way of doing this? I have learnt about structs as an alternative but cannot find a way to apply it to the situation.
Any help would be appreciated.
Thanks
TB12
I think you're going to have to use a lot of repetition and some random number generation, I'm afraid.
Could you possibly post an example?
Since I'm not exactly sure what you're trying to do, no.
I meant could you post an example of how to imitate arrays with random number generation.
No.
To make this script work, you're going to have to create a series of custom generic citizens, and assign to each one a particular value. Then, you need to have the script randomly select a value, and randomly generate its own co-ordinates. Since all NPCs snap to walkmesh, it doesn't matter if the Z co-ordinate is wrong.
The only problem with this technique is that you're going to end up with a lot of NPCs outside of your area model.
Most interesting....
Random number generation is simple enough of course, but the array way of doing things would be so much better :(
As far as NPCs outside of the area model, that would be problematic if you would see their tag bubbles through walls... I wonder if that would be the case; if not, not really a big deal.
I was not aware of the "snapping" to walkmesh, that is a helpful bit of info... Nice to see ya on Holowan DI, been a while :)
Actually, the bigger problem is that more NPCs means more for the PC to process, meaning slowdown. Whether or not it also effects the engine, I couldn't say, but given KotOR II's somewhat delicate constitution, it's probably best not to risk it.
And nice to see you, too. :)
Here is an idea that someone came up with. No idea if it is possible
You always can use list.
Your environment supports structures:
typdef struct {
NODEtype*Previous;
NODEtype *Next;
int Datatype;
unsigned long int DabaBlockSize;
void *DataBlock;
}NODEtype;
You an store any object in this node, and concatenate nodes to from a list.
by marking the first and last elements, you can traverse the list ack and forth using pointer arithmetics. Pass the list to any funciton by passing a pointer to any element of the list.Preferable, use the first element... but it is not a must.
Is this of any use?
Another problem. Random number generation does not support negative numbers. :(
int RandomNegative(int i){
return (Random(2*i)-i);
}
If you enter the int i, this will return you a random int between i and -i.
int RandomInt(int iMax=1, int iMin=0){
int Malo = iMin;
if(iMin>iMax) Malo=iMax;
int iRandom = Malo + Random(abs(iMax-iMin));
return iRandom;
}
This will get you a random int between the two integers you enter, works with negative ints as well.
Hope this helps ya.
As for what you're trying to do, I don't think it's that practical. Compare:
string Tags[3];
location loca[3]
float Orient[3];
Tags[0] = "npc_tag1";
Tags[1] = "npc_tag2";
Tags[2] = "npc_tag3";
loca[0] = Location(Vector(0.0, 0.0, 0.0), 0.0);
loca[1] = Location(Vector(0.0, 0.0, 0.0), 0.0);
loca[2] = Location(Vector(0.0, 0.0, 0.0), 0.0);
Populate(3, Tags, loca);
CreateObject(1, "npc_tag1", Location(Vector(0.0, 0.0, 0.0), 0.0));
CreateObject(1, "npc_tag2", Location(Vector(0.0, 0.0, 0.0), 0.0));
CreateObject(1, "npc_tag3", Location(Vector(0.0, 0.0, 0.0), 0.0));
Which one is more typing?
And if you want some other things to be executed on the spawned creatures you can just define a new function:
void SpawnCreatur(string sResref, location lLoc){
object oCreature = CreateObject(1, sResref, lLoc);
AssignCommand(oCreature, ActionRandomWalk());
}
void main(){
SpawnCreatur("npc_tag1", Location(Vector(0.0, 0.0, 0.0), 0.0));
SpawnCreatur("npc_tag2", Location(Vector(0.0, 0.0, 0.0), 0.0));
SpawnCreatur("npc_tag3", Location(Vector(0.0, 0.0, 0.0), 0.0));
}
As for populating the area without getting any coords, I don't think there's any practical way to do it, if you wanted them all to spawn inside an area model you would have to define where that is, or a little more simple would be defining squares in which they can spawn, but you need 2 coords for that (2 x and 2 y I mean).
And that would only be practical if you're spawning more than 2 creatures in the same square. So this would only be useful in modules where there are big open areas where walking is possible everywhere. In closed areas with lots of small rooms and corridors it would prove not so useful.
Thanks for that, I am getting a script that I think might work. It is still based on random number generation so I can see how it turns out. Will try it later as just opened my new G500 mouse :D
Ok, I have this code but its throwing up all sorts of type mismatches and missing arguements. Am stuck now as all seem to have correct arguements.
#include "k_inc_generic"
#include "k_inc_utility"
int RandomInt(int iMax=1, int iMin=0) //Created by bead-v. Thank You
{
int Malo = iMin;
if(iMin>iMax) Malo=iMax;
int iRandom = Malo + Random(abs(iMax-iMin));
return iRandom;
}
void Populate(int Amount, string Tag1, string Tag2, string Tag3)
{
int Amount = 5;
int AutoInc = 0;
while(AutoInc < Amount)
{
object oPC=GetFirstPC();
int Select = Random(3);
if(Select == 1)
{
int x = RandomInt(150);
int y = RandomInt(150);
int z = 0;
int Orient = Random(360);
object NPC1 = CreateObject(OBJECT_TYPE_CREATURE, Tag1, Location(Vector(x,y,z),Orient));
AssignCommand(Tag1,ActionRandomWalk());
}
if(Select == 2)
{
int x = RandomInt(150);
int y = RandomInt(150);
int z = 0;
int Orient = Random(360);
object NPC1 = CreateObject(OBJECT_TYPE_CREATURE, Tag2, Location(Vector(x,y,z),Orient));
AssignCommand(Tag2,ActionRandomWalk());
}
if(Select == 3)
{
int x = RandomInt(150);
int y = RandomInt(150);
int z = 0;
int Orient = Random(360);
object NPC1 = CreateObject(OBJECT_TYPE_CREATURE, Tag3, Location(Vector(x,y,z),Orient));
AssignCommand(Tag3,ActionRandomWalk());
}
else
{
string cmMessage = "Random Spawning Failed...";
SendMessageToPC(oPC, cmMessage);
}
AutoInc++;
}
}
[HIDDEN]
[/HIDDEN}
#include "k_inc_generic"
#include "k_inc_utility"
int RandomInt(int iMax=1, int iMin=0) //Created by bead-v. Thank You
{
int Malo = iMin;
if(iMin>iMax) Malo=iMax;
int iRandom = Malo + Random(abs(iMax-iMin));
return iRandom;
}
void Populate(int Amount, string Tag1, string Tag2, string Tag3)
{
//int Amount = 5; you have already defined this as an argument of the function, you cannot define it again
int AutoInc = 0;
while(AutoInc < Amount)
{
object oPC=GetFirstPC();
int Select = Random(3);
if(Select == 1)
{
int x = RandomInt(150); //I hope you know this is exactly the same as Random(150)... if you want it to return between -150 and 150 you have to use the first function I posted.
int y = RandomInt(150);
int z = 0;
int Orient = Random(360);
object NPC1 = CreateObject(OBJECT_TYPE_CREATURE, Tag1, Location(Vector(x,y,z),Orient));
//problem here is that the Location() and Vector() functions need *floats* not ints as arugments, so you have to convert the ints to floats first using IntToFloat()
AssignCommand(Tag1,ActionRandomWalk()); //Can't assign a command to a tag =P
}
if(Select == 2)
{
int x = RandomInt(150);
int y = RandomInt(150);
int z = 0;
int Orient = Random(360);
object NPC1 = CreateObject(OBJECT_TYPE_CREATURE, Tag2, Location(Vector(x,y,z),Orient));
AssignCommand(Tag2,ActionRandomWalk());
}
if(Select == 3)
{
int x = RandomInt(150);
int y = RandomInt(150);
int z = 0;
int Orient = Random(360);
object NPC1 = CreateObject(OBJECT_TYPE_CREATURE, Tag3, Location(Vector(x,y,z),Orient));
AssignCommand(Tag3,ActionRandomWalk());
}
else
{
string cmMessage = "Random Spawning Failed...";
SendMessageToPC(oPC, cmMessage);
}
AutoInc++;
}
}
I still don't think this is a good method though.. for example, you wouldn't be able to use this for 905 and 906 because most creatures would just end up outside the area model cuz the areas are either far to the right or far to the left...
Here is an idea that someone came up with. No idea if it is possible
(snip!)
Is this of any use?
NWScript is not a programming language, it's a very limited game engine scripting language with a C-like syntax of what little it is capable of doing. You have no control over memory, there's no such thing as pointers in it.
You could probably fake array-like functionality by storing limited amounts of basic data as tokenized strings and have a Get and Set function to pack/unpack the data, but I doubt it would be worth the effort and overhead for most of what you'd usually have need of arrays for.
[code]
I still don't think this is a good method though.. for example, you wouldn't be able to use this for 905 and 906 because most creatures would just end up outside the area model cuz the areas are either far to the right or far to the left...
If you want to distribute NPCs semi-randomly throughout an area I'd just loop through all the waypoint and/or placeable objects in the area, pick out a few and spawn NPCs within a small radius around each. It would still be random-ish, though I think most of them would end up on the walkmesh that way.
If you want to distribute NPCs semi-randomly throughout an area I'd just loop through all the waypoint and/or placeable objects in the area, pick out a few and spawn NPCs within a small radius around each. It would still be random-ish, though I think most of them would end up on the walkmesh that way.
What if you made a function that generated a random vector (or the random coordinates for a vector) between the xmin, xmax and ymin and ymax values of the function?
You would of course have to define the xmin/max and ymin/max values in the script.
In my experience, if you spawn an npc outside of the walkmesh, the engine will correct its location to the nearest walkable area or sometimes some random places outside the walkmesh I like to call "sticky spots".
So say we generate a random vector [7.8, 6.0, 4.0] but the game corrects the NPC's position so it actually spawns at [9.0, 6.0, 4.0]. Could you not then create a function that generates a random vector, tries to spawn an NPC at that vector, then checks to see the actual position of the NPC to sort of guess if the NPC spawned some place it shouldn't, then either delete it or jump it somewhere else.
That may work but my other idea was to use NPC Perception. e.g I spawn one NPC using either .GIT or script and then create a loop that takes that NPC, finds somewhere in its radius and spawns a new NPC. Then it continues BUT with the next NPC. The loop will stop once the required amount has been reached. They can all be assigned the RandomWalk function. Would this be a better method of random generation.
Hmm, VP is right about the game correcting the positions, I've tested it. So his idea seems to be the best - getting a xmax, xmin, ymax, ymin, then just randomly spawning..
Should the xMax etc. be declared in the function itself and how would you find out what to set them as. Sorry about this. I get everything else. Just not how to get the values.
Nope, equip the whereami armband and hop into the module you want to populate... As I've said before, some models are far the left, some are positioned otherwise and so on.. if you want the creatures as evenly spread out as possible it's best you get the coords of the points farthest do the north/east/south/west, and spawn in that rectangle.
Ok then,
thanks for that.
Lets see what I can come up with.
Ok, after lots of attempts and one hell of a lot of school work I finally managed to come up with this
#include "k_inc_generic"
#include "k_inc_utility"
int RandomInt(int iMax=1, int iMin=0) //Created by bead-v. Thank You
{
int Malo = iMin;
if(iMin>iMax) Malo=iMax;
int iRandom = Malo + Random(abs(iMax-iMin));
return iRandom;
}
void Populate(int Amount, int xMin, int xMax, int yMin, int yMax, string Tag)
{
object oPC = GetFirstPC();
int AutoInc = 0;
while(AutoInc<Amount)
{
AutoInc++;
int x = RandomInt(xMax, xMin);
int y = RandomInt(yMax, yMin);
float z = 0.0;
int Orient = Random(360);
float x1 = IntToFloat(x);
float y1 = IntToFloat(y);
float Orient1 = IntToFloat(Orient);
object AutoInc = CreateObject(OBJECT_TYPE_CREATURE, Tag,
Location(Vector(x1,y1,z), Orient1));
AssignCommand(oPC, ActionRandomWalk());
}
}
And my main file
#include "populate"
void main() {
string Tag = "ch_guard";
Populate(20, -28, 21, 69, 121, Tag);
}
But when I attach it to a dialogue nothing happens. It all compiles ok and I am definately attaching the main file not the function file. Im stumped and frankly quite tired. Any ideas?
[QUOTE=TimBob12;2771722]Ok, after lots of attempts and one hell of a lot of school work I finally managed to come up with this
#include "k_inc_generic"
#include "k_inc_utility"
int RandomInt(int iMax=1, int iMin=0) //Created by bead-v. Thank You
{
int Malo = iMin;
if(iMin>iMax) Malo=iMax;
int iRandom = Malo + Random(abs(iMax-iMin));
return iRandom;
}
void Populate(int Amount, int xMin, int xMax, int yMin, int yMax, string Tag)
{
object oPC = GetFirstPC();
int AutoInc = 0;
while(AutoInc<Amount)
{
AutoInc++;
int x = RandomInt(xMax, xMin);
int y = RandomInt(yMax, yMin);
float z = 0.0;
int Orient = Random(360);
float x1 = IntToFloat(x);
float y1 = IntToFloat(y);
float Orient1 = IntToFloat(Orient);
object AutoInc = CreateObject(OBJECT_TYPE_CREATURE, Tag,
Location(Vector(x1,y1,z), Orient1));
AssignCommand(oPC, ActionRandomWalk());
}
}
Might have something to do with the code in orange.
TB, I think you're not including the script correctly.. if your script name is "example.nss", you have to place it in the override folder, and then compile the script that makes a reference to it, which is supposed to look like this:
#include "example.nss"
I see no '.nss' in your reference.. It might still work though..
Also, the thing you call a "tag" is actually a reference that is set in the advanced tab in kt. Just make sure you wrote down the correct name.
Oh, and by the way, why are you making the PC walk randomly?
... and I just noticed what VP was referring to... you declared the same variable two times, the Autoinc one.
Wow it must have been late when I wrote this.
Bead-v yes I have included it properly but you don't need the .nss extension. When I first wrote it a load of compiling errors came up through the main script referenced to populate.nss.
The reason I had that as AutoInc was that I was wondering if I had to have an individual variable name for each NPC cos otherwise wouldn't it keep respawning the same NPC in several different places?
Testing.......
No it doesn't and they spawn randomly HURRAH!
I have managed to get up to 200 spawned characters before the engine becomes Unplayable. :P
Thank you everyone for help on this. I will post screenshots and code ASAP.
Just noticed 300 posts :)
Great work TB and all those who helped.....look forward to the finished product
Just as an aside, you can avoid giving creatures, waypoints and triggers (except mines) a Z axis value, but you cannot do that for placeables. Placeables do not snap immediately to the walkmesh - you must do it manually. I usually do this simply by looking at the Z axis of nearby placeables to give me an idea.
Great job Timmy, congrats.. I'm interested in knowing how well this works in oddly shaped areas though.. =P
You could always break an area down into smaller squares although it doesn't have to be a square I guess.
Breaking it down would be more efficient but covering the whole area in one big square shape thing would mean less work.
Either way its still a lot less work than finding each individual coordinate for NPCs and putting it in the git file.