[How-To] Improved vehicle spawn functions

Doc

Valued Member!
HOTFIX** 01/01/2013 - UPDATE YOUR CODE
There was an issue with the function creating vehicles with a NULL ObjectUID/Worldspace in some circumstances. I have updated line 53 of the code to fix this - please reapply the pSpawnVehicles function below

The Current System
The way vehicles are currently spawned is a bit strange and is better suited for low vehicle counts (<10). If you set your maximum cars to say, 40, and there are 40 or more spawn points available, then there will always be exactly 40 vehicles on the map. This is a bit silly, because it kind of nullifies the spawn chances - if the spawn chance is 0.1 (10%) and there are only 40 spawn locations then that car will always be in the map. This is why I have completely rescripted the vehicle spawn function.

My New System
In my new system, vehicles are spawned entirely based on their spawn chance - this is better for large vehicle counts. The old way of setting 40 or whatever vehicles as your iSpawnNumVeh in pMain is messy. It's more efficient to attempt to spawn every vehicle in your object_classes and see if their spawn chance let's them spawn or not. Welcome to the world of dynamic vehicle spawns! Some restarts they may be 20 vehicles on your server, on others there may be 40. This is a great way to stop all vehicles spawning at once and prevent people having a headstart on vehicle hoarding. You will no longer need to edit pMain, as it is obsolete (as is pSpawn). Instead all vehicle spawns are taken from object_classes. Every vehicle allowed to spawn in object_classes could appear on the map, but it all depends on it's chance parameter.

RECOMMENDED
Follow this HotFix ASAP (Even if you don't use this function)
Follow this guide afterwards [How-To] Prevent persistent vehicles hacked into database

Setup
You do not need to delete/edit existing functions. Just create a new procedure called pSpawnVehicles (you can name it what you like) with the following code:

pSpawnVehicles
Code:
BEGIN
 
    DECLARE iCurrentVehicles INT(4) DEFAULT 0;
    DECLARE loopCounter INT(4) DEFAULT 0;
    DECLARE iSpawnAttempts INT(4) DEFAULT 0;
    DECLARE iTotalClasses INT(4) DEFAULT 0;                 
 
    CALL pCleanup();
    CALL pFixMaxNum;
 
    SET iCurrentVehicles = fGetVehCount();
 
    SELECT COUNT(*)
                    INTO iTotalClasses
                    FROM object_classes;
 
    SELECT SUM(MaxNum) FROM object_classes INTO @iMaxNumTotal;
    IF (iCurrentVehicles > @iMaxNumTotal) THEN
        SET iSpawnAttempts = @iMaxNumTotal - iCurrentVehicles;
    END IF;
 
 
 
 
    WHILE (loopCounter < iTotalClasses) DO
 
            SELECT Classname, Chance, MaxNum, Damage, MaxFuel, Inventory, Hitpoints
                        INTO @rsClassname, @rsChance, @rsMaxNum, @rsDamage, @rsMaxFuel, @rsInventory, @rsHitpoints
                        FROM object_classes
                        LIMIT loopCounter, 1;
 
            SET @initialClassCount = fGetClassCount(@rsClassname);
 
            IF (@initialClassCount < @rsMaxNum) THEN
 
                SET @classloop = 0;
                SET @attemptsAtClass = @rsMaxNum - @initialClassCount;
 
                WHILE (@classloop < @attemptsAtClass) DO
                    SET @luckyDip = fGetSpawnFromChance(@rsChance + (RAND() / 10000));
 
                    IF (@luckyDip = 1) THEN
 
                                SELECT ObjectUID, Worldspace
                                                INTO @spawnUID, @spawnWorldspace
                                                FROM object_spawns o
                                                WHERE Classname = @rsClassname AND NOT EXISTS (SELECT 1 FROM object_data e WHERE e.ObjectUID = o.ObjectUID)
                                                ORDER BY RAND()
                                                LIMIT 1;
 
                                SET @fuel = (FLOOR( (RAND()*@rsMaxFuel) * 10000) / 10000);
 
                                INSERT INTO object_data (ObjectUID, Instance, Landmark, Classname, Damage, CharacterID, Worldspace, Inventory, Hitpoints, Fuel, Datestamp)
                                                    VALUES (@spawnUID, '1', '0', @rsClassname, @rsDamage, '0', @spawnWorldspace, @rsInventory, @rsHitpoints, @fuel, SYSDATE());
                                                                                                                     
 
                    END IF;
                    SET @classloop = @classloop + 1;
                END WHILE;
            END IF;
        SET loopCounter = loopCounter + 1;
    END WHILE;
 
END
fGetVehCount
A small change can be made to fGetVehCount to utilize the Landmark parameter (read below)
Code:
BEGIN
 
    DECLARE iVehCount    SMALLINT(3) DEFAULT 0;
 
    SELECT COUNT(*)
        INTO iVehCount
        FROM object_data
            WHERE
                Landmark = '0'
                AND Classname != 'dummy'
                AND Classname != 'TentStorage'
                AND Classname != 'Hedgehog_DZ'
                AND Classname != 'Wire_cat1'
                AND Classname != 'Sandbag1_DZ'
                AND Classname != 'TrapBear';
 
    RETURN iVehCount;
END

fGetSpawnFromChance
If (like me) your fGetSpawnFromChance is empty, you will need to paste the code below in, and set the parameter to `chance` DECIMAL(6,5). Using Navicat, you may have trouble saving this function. To fix this, set the Data Access (in advanced tab) to READS SQL - paste your function/parameter - save the function - now set Data Access to NO SQL and it should save. No idea why it does that, but the method to save it works... somehow.
Code:
BEGIN
 
DECLARE bspawn TINYINT(1) DEFAULT 0;
 
IF (RAND() <= chance) THEN
SET bspawn = 1;
END IF;
 
RETURN bspawn;
 
END

database structure changes (*Important)
Nearly forgot this part...
New column to objects_classes.
MaxFuel - Varchar(10) DEFAULT 0
(Reason for this should be self-explanatory - fuel on spawn is a random number up to MaxFuel)
Inventory - Text(0) DEFAULT []
(Allows you to set certain vehicles to spawn with a given inventory loadout)

New column to object_data
Landmark - TinyINT(1) DEFAULT 0
(The reason for this is because if you use my guide to add land objects to the database, it interferes with the fGetVehCount. Instead of adding an exception for each new object, it's easier to classify all vehicles as landmark 0 and give all objects landmark 1 when you add them in. If you don't add objects in via object_data, just add this column anyway)

Change to Chance column of object_classes
make the chance column type DECIMAL with the length 6 and decimals 5. This is for consistency.


If you are using pwnoz0rs launcher, now you just need to change pMain() to pSpawnVehicles by editing the .bat file.

You are now good to go! Now you can add more vehicle spawn points to your object_spawns and object_classes and enjoy the new spawning method. If you ever decide to revert to the old pMain system, then just change which function you are calling.

I will soon make an SQL script to add this all in and make the changes for you, but once I have time.
-Doc

EXTRA COURTESY FILES:
- object_spawns.sql* - 92 vehicle spawn locations (comments in description say location)
- object_classes.sql* - object_classes to complement the object_spawns
You may have to tweak object_classes to work with any custom spawn points you added.
I cant remember if description is a field in pwnaz0rs version, if it isn't just add a description text column
*saved as .txt to bypass forum filter
 

Attachments

Doc

Valued Member!
Changelog
Added: Changes you are required to make to the database
Added: fGetVehCount and fGetSpawnFromChance
Added: object_classes and object_spawns for 92 vehicle spawn points
Removed: My testing line from the code :3
 

Forgotten

Well-Known Member
I get a MySQL error 1064- You have an error in your SQL syntax '@rsClassname, @rsDamage,'0',@spawnWorldspace,@rsInventory,@rsHitpoints,@fue' at line 52.

Happens when I try to save the new pSpawnVehicles function in navicat.
 

Doc

Valued Member!
I get a MySQL error 1064- You have an error in your SQL syntax '@rsClassname, @rsDamage,'0',@spawnWorldspace,@rsInventory,@rsHitpoints,@fue' at line 52.

Happens when I try to save the new pSpawnVehicles function in navicat.
Sorry, that was because I removed my debug code without testing it - it should work now. By the way this is a procedure not a function :)
 

Ekot

New Member
Nice work :)
Is it possible to ask for 2 features?
1. Random Inventory in vehicle (based on chance and after a table), like in real life some cars would have a spare tire and other maybe a empty Jerrycan or even a full.
2. Random Damage on vehicle upon spawn, like some cars maybe have a flat tire, some missing some windows and so on.
that would make it alot more dynamic ;)
 

Doc

Valued Member!
Nice work :)
Is it possible to ask for 2 features?
1. Random Inventory in vehicle (based on chance and after a table), like in real life some cars would have a spare tire and other maybe a empty Jerrycan or even a full.
2. Random Damage on vehicle upon spawn, like some cars maybe have a flat tire, some missing some windows and so on.
that would make it alot more dynamic ;)
Good ideas - it's something I've thought about doing and planned to do but I need to learn more about what the car damage variables mean. I'm investing my free time into BanZ at the moment but I'll come back to this project soon :)
 

Forgotten

Well-Known Member
Thanks for working on this, got it working, and it's a great feature to have for something that should have been included for server admins in the first place! Hopefully Rocket will either pay attention or be told about features like this.
 

Doc

Valued Member!
UPDATE:

Important typo spotted by myself:

This line
Code:
  SET iSpawnAttempts = @iMaxNumTotal - currentVehicles;
Should be
Code:
 SET iSpawnAttempts = @iMaxNumTotal - iCurrentVehicles;
 
Also u might want to change
Inventory - Text(0) DEFAULT []
to
Inventory - Varchar(999) DEFAULT []
since Text with default is not possible in mysql on windows (proboably because strict mode)
 

Doc

Valued Member!
Also u might want to change
Inventory - Text(0) DEFAULT []
to
Inventory - Varchar(999) DEFAULT []
since Text with default is not possible in mysql on windows (proboably because strict mode)
I don't know about that, I mean I know what you are saying but varchar 999 is not long enough for a full URAL ;( infact that was why I changed it from varchar to text. Also varchar forces the data usage but text is variable. Up to each individual I guess
 

Forgotten

Well-Known Member
I don't know about that, I mean I know what you are saying but varchar 999 is not long enough for a full URAL ;( infact that was why I changed it from varchar to text. Also varchar forces the data usage but text is variable. Up to each individual I guess
I see what Ekot was saying, though, Doc. I also could not set any Default when I had Inventory set to text. However, I had already run into the not enough lines in inventory issue, so mine is set at 3000.
 
Alors Moi j'arive pâs a Maitre plus de Vehicule ! Commant Faire est se que il peux faire une Vidéo ou meme toi si tu veux. moi Je suis pâs trop cfg DayZ Mes Plus cfg Call of duty C est pas la méme Chose !

est se que tu peux faire une Vidéo mot de ! stp
 

Doc

Valued Member!
:) Video Plz. because I Dunno How To Master the Vehicle :( . is the Master File Changes in the Cfg download the Boc ;) :( ! Plz The Video ! <3
What?

I see what Ekot was saying, though, Doc. I also could not set any Default when I had Inventory set to text. However, I had already run into the not enough lines in inventory issue, so mine is set at 3000.
Whatever way works for your server is best :) Personally all my vehicles spawn empty, so I would rather save on data storage by using Text opposed to varchar (as varchar uses all of the data allowance even if it's empty). This is especially important as I have buildings in my object_data and I would end up using a lot of storage capacity purely by using varchar instead of Text. If you want loot in there though, you have to use varchar. :)
 
I get this error "1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')
BEGIN

DECLARE iCurrentVehicles INT(4) DEFAULT 0;
DECLARE loopCount' at line 1"

whe I try to save a new procedure :s Please help xD
 

Doc

Valued Member!
I get this error "1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')
BEGIN

DECLARE iCurrentVehicles INT(4) DEFAULT 0;
DECLARE loopCount' at line 1"

whe I try to save a new procedure :s Please help xD
I just tried saving the code I wrote above as a new procedure in navicat and it worked fine. What I did was open hivemind and expand functions. I clicked on new function and selected the "procedure" option. I then pressed next all the way until it finished. I then pasted the pSpawnVehicles code above into it, making sure it deleted the crap Navicat puts in there by default. Pressing save asked me to name the function and it saved with no problems... :S I don't know what's the problem, but I messaged you my skype name. Message me with yours and I will try help you.
 
Top