Zone Customization - Scenery Objects
I have made great progress in my personal project of trying to add static scenery objects to customize my zones.
Adding a static scenery object to a zone is very easy once you know how. Step 1: Know the model name for the object you're adding (look for per-zone object model inventories in an upcoming new version of my EQ Model Inventory program) Step 2: Add a record to the "doors" table in your EQ database with the following field settings: id = Unique number for this record doorid = Unique number within the doors list for this zone zone = Zone nick version = Instance version number (0 for default) name = Model name from Step 1 pos_x/y/z = Location to place object heading = Direction object faces (0 - 511) opentype = 9 (makes object non-solid) or 31 (makes object solid) dest_zone = NONE size = 100 Everything else = 0 Step 3: Leave and re-enter your zone and see your new object(s)! Note: If you make changes to the database and the zone is still running in the zone server, you'll have to do a "#zoneshutdown ZoneNick" before zoning back in, to clear the cache, or your changes won't take effect. A hotkey works well. This technically adds the object to the zone as a door, but opentypes 9 and 31 contain no animation or sound effect, so nothing happens when the user clicks on them. They're just like static scenery objects, except you can place them wherever you want! Opentype 9 actually makes objects -mostly- non-solid. I tried looking for a fully non-solid opentype, but the partial that 9 gives appears to be the best we can get. Each object will have a few surfaces that are solid, but most become walk-through. Opentype 31 makes objects fully solid unless the object was specifically designed to be walk-through, which I've mostly seen in chairs. I plan to make plenty of use of this ability in my own customized server. :) Now, if anyone can figure out a way to repop a zone's door list without requiring a zone-out, #zoneshutdown, and zone-in, that'd be way cool. |
If you're eager to see it in action real quick, I've generated a .txt file that lists all of the models available in all of the *_obj.s3d and *.eqg files as of SoF.
http://www.jondjackson.net/eq/emu/fi...lInventory.zip NOTE 1: If you want to add objects to a zone for use, simply add the model source filename to ZoneNick_assets.txt. For example, to add 'obj_ctent' from 'live_event_objects.eqg' to the zone 'felwithea', create a file in your EQ directory called 'felwithea_assets.txt' containing the line 'live_event_objects.eqg' (omitting the single quotes wherever they're shown here for emphasis). NOTE 2: Global object models are those in files listed in Resources\GlobalLoad.txt. NOTE 3: Object names are always in upper case (e.g., IT66, OBJ_CTENT). NOTE 4: Do NOT use the _assets.txt file to import an actual zone S3D or EQG file! This will load the actual zone geometry into your zone, not the objects! Stick to *_obj.s3d and any EQG files that are not race model sources or zone files. Example: 1. Create felwithea_assets.txt with the line "live_event_objects.eqg" in it. 2. Add the following record to your doors table: Code:
INSERT INTO doors VALUES (61001, 101, 'felwithea', 0, 'OBJ_CTENT', -5, -5, 0, 256, 31, 0, 0, 0, 0, 0, 0, 0, 0, 'NONE', 0, 0, 0, 0, 0, 0, 0, 150, 0); Enjoy. :) |
Of course, you don't have to import any object models to add a customized object to your zone. Just ignore the ZoneNick_assets.txt step and use the model name for an object already inside your zone, such as 'FELBED' in felwithea in the example above.
|
Technically, the best thing to do to utilize this availability without cluttering up the doors table with non-door objects would be to create a new table:
Code:
CREATE TABLE `static_objects` ( File: zone.h, Line 111 - Class Zone Code:
... Code:
void Zone::LoadZoneStaticObjects(int32 zoneid, const char* shortname, int16 version) Code:
... http://www.jondjackson.net/eq/emu/im...ticObjects.png If you're wondering why I'm sticking it in doors, instead of the zone objects table, it's because I couldn't figure out any way to use the zone objects system that didn't involve an openable tradeskill container or pickable ground spawn item. Anyway, forget adding records to the doors table. Much better to use this new static_objects table! |
Another stunning customization miracle you've worked up! Bravo! I can definitely see some potential in using this to help build future zones as well, marvelous.
|
I played with static objects back in March and had very good luck with them as well. There we're a few instances where models took extra modifications to get working, as they created invisible walls of death.
I definitely don't recommend using the doors table however. |
Yeah, using a different table is definitely the way to go.
What methods were you using to play with static objects? |
I have used this method in the past to add objects to zones. Has always worked well, just a pita searching through the files and figuring out what everything is.
|
Shendare, this is brilliant!
I am playing with this now, and I'll see what I can do with this. GeorgeS |
Instead of having a 3rd table for objects in zones, couldn't we just add 1 more field to the current zone objects table that would let us toggle if we want it to be sent as a normal object or as a non-clickable door? Then, just adjust the object code to send those type of objects as a door instead with some default values? The zone objects table is already under-utilized due to this limitation, so it would be nice to have the option for this without having to use the misleading doors table for it. For the cases of static objects like this, you should just need the name, the zone, the loc and then set the toggle field to 0 or 1 (whichever means non-clickable) to finalize it and the rest would get ignored. Instead of adding a new field for it, we could even define a certain setting in one of the existing fields to cause it to make the switch. Something like setting the type to 999 should work fine.
I think the reason why that stuff is currently done in the doors table is because that is how they send it on Live. So, we try to duplicate that as close as possible. For the purpose of the packet collectors, it is easiest to handle the DB the same way that Live sends the information. It wouldn't be bad to have another table for it for adding custom stuff in and making it a little easier, but the existing tables already work for that so it isn't really a requirement. If you really wanted to make adding static objects easier, you could make a command that lets you add them in-game by simply typing something like "#objectadd static|door|tradeskill <Object Name>" at the location you want to add the item. Then, after re-zoning, you just adjust the table as needed to make sure it is placed exactly how you wanted it. I am not really apposed to another table for it, though. |
Utilizing the existing objects table with an update to support static objects is a very good idea, and I'd love to try out making an in-game command.
I'll get to work on it. |
Okay, thanks for the sanity check, Trev.
Ignore all of the code posted in the thread up to this point. After some real trial and error work, I was able to whittle things down to the point that static object loading now works perfectly straight out of the existing object table and with only the single following code addition: File: zone.cpp, Line 181 - LoadZoneObjects() Code:
... - To classify an object record as a static object, set the type to 0. The client doesn't consider 0 a valid object type, so we'll never use it for a valid tradeskill object anyway. - itemid, charges, and icon are ignored for static objects - unknown08 serves as the optional Size field, a percentage from 1 to 32767%. Leaving it at 0 renders at normal size (100%). - unknown10 serves as the optional Request_NonSolid field. If set to 1, the object is rendered as opentype 9, which for some smaller models (e.g., chest1) makes them mostly walk-through. Mileage will vary on a per-model basis. - unknown20 serves as the optional Incline field, tilting the rendered model along the y axis. Not likely to be used... ever, really, but it's there if someone wants it. - all other unknown## fields are ignored I don't know about any limit on the number of static objects the client is willing to take. It lets us use the same door_id value for all of the static objects, so we're not limited by the fact that door_id is a signed 8-bit field, which was my initial concern with adding the objects as doors. I tried everything I could to get the static objects to send to the client as actual objects, sending all sorts of different values for various fields in the Object struct being sent to the client, but no matter what, the client would always open the player's inventory when the object was clicked on. It's just the way it's wired for objects. It doesn't seem to matter much, though, since they seem to work quite nicely as nonfunctional doors. Anyway, give it a try and see if it looks ready for committing, Trev. Seems to work very well now. Thanks again for steering me back to the objects table. It really works out better this way. Now to work on the in-game command for it. - Shendare |
One thing that might be a possibility would be to see if we can get objects like that to spawn in real-time when they are created. I know for sure that any model can be set on an item and dropped on the ground to simulate a static object. It should just be sending an object packet to do that. With that being the case, we should be able to use a command to place a fake item on the ground where ever we want just by having the command send the packet for it. Now, that alone might not sound too useful, but by using a little trick, we might be able to make good use of it. Since any object would be clickable as you said, we could probably set those objects so when you click them, they disappear. This would allow you to move around adjusting the position of an object until you are at the perfect positioning for it. Then, once you have the perfect position, you add something to the end of the command like "perm" to make the next one get added to the database. Any of the ones added without perm would just be temporary and used only for finding the right positioning. By using the perm option, it would still generate a fake object in the zone like it was previously doing, but this time it would create an actual entry in the database. Instead of having it enter the object into the objects table, it could put them into the doors table. There could even be an option to chose which table you want it to be permanently added to, so you could place tradeskill containers this way. If this idea worked like I think it should, this would make adding/placing doors, tradeskill containers and any static object a piece of cake.
Another nice use for a command like this would be that you could examine each object/door type that a zone has available without having to zone each time to do so. The zoning part is the biggest pain when manually populating zones with objects and doors. I have never tried it, but it may be possible to spawn a door in real-time without having to zone as well. I don't see why it wouldn't work, but I have just never tried it. The main reason I suggest spawning them as an object is because it is easy to be able to remove them by clicking on them. The client thinks that the object was picked up if it is set correctly. I don't know of any way to remove a door in a zone without having to zone first. That is something to think about while expanding the possibilities with objects and doors, anyway. I will try to look the code over that you posted, Shendare. Hopefully I can get it added tomorrow night if I have time to check it out and stuff. |
Coding doors and objects as a ground spawn item for moving them around until the user is satisfied with their placement is an intriguing idea.
My first thought had been to be to look into sending the DoorSpawn or ObjectSpawn packets in real-time when using a #door create or #object create command. The command would send the client a message containing the ID of the door/object it created, and the user could use a #door move or #object move command, passing the ID. However, this hinged on the ability to send a DoorDespawn and ObjectDespawn packet as well, and I haven't looked yet to see if it looks like a DoorDespawn opcode and struct are available, or whether it's simply a toggled switch in the DoorSpawn opcode and struct. Further investigation is needed, but there's certainly promise. |
I have actualy been playing with this for a while. I mean original Doors table is full of things which not actualy doors. It has tents, barrels etc.
Of course geting them to sit just right was a little bit a pain =) Here some of my tents and fires in West Karana http://herbhealernet.web.siteprotect...estkarana2.jpg |
Awesome!
Once the object table based placement goes live, you would be able to migrate your objects out of the doors table with a couple of SQL commands if you wanted. Code:
INSERT INTO object (zoneid, xpos, ypos, zpos, heading, objectname, `type`, unknown08, unknown10, unknown20) Code:
DELETE FROM doors WHERE opentype IN (9, 31); |
Quote:
Code:
#object List All|(radius) |
Oh sweet lol. I was just making something very basic and wasn't very far along just yet. I figured it could always be expanded on later, but wanted to get something working to start off with. Yours is much more in-depth than what I was doing, so I'll just leave it up to you :)
The only thing I would add to what you have so far would be a way to despawn them. We should be able to use the ClickObject packet to handle that part. That isn't really too important though, and can always be added in later. Unless your delete option already does that. I like the move option you have. It sounds like a good idea and not hard to do. |
Quote:
The only caveat is that Doors do not have a Despawn opcode that I could find. They appear to be super-static by design. Unless we just haven't found the Opcode yet (since it's not likely to be found with packet collection from Live servers), I assume at this point that the client expects them to be sent once upon zone-in and never change. This also means that there will not be a Despawn packet for static objects. The best way I can figure as to how to handle this issue is to spawn static objects and doors as tradeskill objects at first, allowing the user to make any model, location, and rotation changes they want to it, then change them into full-on static door objects when the "#object save" command is used to commit them to the database. I could also look into utilizing an #object command to temporarily change an already-committed door or static object into a changeable tradeskill object upon the next zone-in, allowing the user to make any changes in-game and then re-commit with "#object save". Should be entirely doable. |
Note that I moved these posts from the SoF Development thread to here, since this is a better place to discuss it.
That sounds great, Shendare. Using temporary objects (from object packets) for placement and testing and then doors for actually saving them is exactly what I was thinking. I think that is probably the best option and should work out great and should be fairly seamless. I don't think people realize yet just how useful this command will be. It is good for adding custom stuff to zones, but it is also extremely good for adding in doors and such to newer zones that are currently completely empty. Until the packet collectors are working again, there is no easy way to do that, so this command will help a lot. The only way to do it better would be to manually pull the data from the Live packets and put them into the table. That is what I did for Crescent Reach, but not all zones are quite so accessible to collect from. |
Shendare, how did you know that the tent you used is a global object (normaly its only found in Dranik)?
Is it because its located in live_event_objects.eqg? does all other thing in live_event_objects.eqg are global? Are there any other files? are there any global doors? I am desperately trying to get a door into Guk Ldon |
The particular tent I used isn't global. I had to add an entry in felwithea_assets.txt to pull in the models from live_event_objects.eqg.
To know what models are available globally, make a list of the files referenced in your EQClient\Resources\GlobalLoad.txt file. Then check those files in the Model Inventory I linked to earlier, and any model names in that file's entry will be available globally. http://www.jondjackson.net/eq/emu/fi...lInventory.zip You can search the above inventory file for non-global objects available in your particular zone by searching for ZoneNick_obj.s3d or ZoneNick.eqg. The available model names will all be listed there. For guka, for example, I see a model called GUKDOOR700 that looks promising, as well as GUKMETGATE700 and GUKMETGATE701. I have not found any global door objects via a quick skim of the global model files. However, you can easily add objects from other zones using the ZoneNick_assets.txt feature. Note that this is a client change that has to be done on each client that's going to be playing on your server. |
Good progress in this project. I'm probably 80% done coding up the #object command. Hopefully by Tuesday or Wednesday night it'll be 100% functional and I'll post the code for it here.
I've now mostly got objects listing with List, spawning with Add, moving with Move, rotating with Rotate, saving with Save, and deleting with Delete. Only Edit and Copy really remain, then a bit of debugging, and I'll post the code for Trev to try out before submission to the SVN. This one will include a pretty hefty new code block in command.cpp, but it should be easy to copy/paste. It will continue to use the objects table as-is for static objects (type 0) as well as ground spawns (type 1) and tradeskill objects (type 2+). No database structure changes required. There will be #door commands similar to those for #object that I listed earlier, and they will work pretty much the same. Once I finish up #object, it'll probably be mostly a copy/paste to get #door working properly. Of course there will be no database structure changes for the doors table, either. |
Wheeeeeeeew, this has been an educational endeavour! LOL.
Alright, I've gotten the #object command 110% functional. I've tested every part of its functionality, but it could most definitely stand to undergo additional testing from more users. Note: Nothing in the commands is case sensitive. First the breakdown on the new commands: ---------------- 1. #object List - Lists all objects in the zone or within Radius units of you Usage: #object List All|(Radius) Examples: - #object List All - #object List 500 ---------------- 2. #object Add - Spawns a new object at your present location Usage: Static Object: #object Add [ObjectID] 0 Model [SizePercent] [SolidType] [Incline] Tradeskill Object: #object Add [ObjectID] TypeNum Model Icon Examples: - #object Add 0 CHEST1 50 - #object Add 17 IT66_ACTORDEF 1115 - #object Add 161001 0 FELBED 125 1 0 ---------------- 3. #object Edit - Changes a property of an object within the zone Usage: #object Edit (ObjectID) (PropertyName) (Value) - Static Object Properties: model, type, size, solidtype, incline - Tradeskill Object Properties: model, type, icon Examples: - #object Edit 30005 model BARSTOOL1 - #object Edit 141073 icon 1116 ---------------- 4. #object Move - Moves an object to a new location Usage: #object Move (ObjectID) ToMe|(x y z [h]) Examples: - #object Move 34106 ToMe - #object Move 1519 -25 50 0 256 ---------------- 5. #object Rotate - Changes an object's heading without changing its location Usage: #object Rotate (ObjectID) (NewHeading) Example: #object Rotate 5029 128 ---------------- 6. #object Save - Saves an object to the database. Note: New objects spawned and changes made to existing objects are not saved to the database until #object Save is called on them. Usage: #object Save (ObjectID) Example: #object Save 51202 ---------------- 7. #object Copy - Copies object(s) from the current instance version into another Usage: #object Copy All|(ObjectID) (InstanceVersion) Examples: - #object Copy All 1 - #object Copy 76209 5 ---------------- 8. #object Delete - Despawns an object and permanently removes it from the database Usage: #object Delete (ObjectID) Example: #object Delete 219538 ---------------- 9. #object Undo - Cancels changes made to an object and respawns it from the database Note: Objects that have been spawned with '#object Add' but have not yet been saved to the database are deleted when '#object Undo' is used on them. Usage: #object Undo (ObjectID) Example: #object Undo 32109 ---------------- Okay, that's how they work. Here are all of the code changes required to make the magic happen. Line numbers are based on Rev 781 (7/15/2009). File: object.h - Line 172 Code:
... Code:
... Code:
... Code:
... Code:
... Code:
... Code:
... Code:
... Best thing now would be for people to bash the heck out of it and find any glitches, memory leaks, unexpected scenarios, etc. that I may have missed (e.g., did I forget a 'mysql_free_result(result)' anywhere and create a memory leak?). Next things I could work on would be #door and maybe #groundspawn, utilizing similar functionality. - Shendare |
/emote cheer
Bravo, Shendare, bravo! That is just awesome! I will get this tested out right now and up on the SVN ASAP unless someone is already working on getting it put in. This is a HUGE help to anyone wanting to mess with objects and the related doors stuff will help just as much. Even before similar door commands are in, I am sure this could help to make adding doors much easier too. Great work! And yeah, that command is a doozy! |
Awwe, compile error:
Code:
entity.cpp: In member function ‘Object* EntityList::FindNearbyObject(float, float, float, float)’: Here is the bit of code it is referring to: Code:
while(iterator.MoreElements()) |
Commented that while out for now to try to get around it and now getting these compile errors:
Code:
command.cpp:13397: error: ‘_strlwr’ was not declared in this scope |
I've hit this in my "trying to learn C++ adventure". The *_s functions are Microsoft only. For sprintf_s you might try the POSIX snprintf:
Code:
int snprintf(char *str, size_t size, const char *format, ...); |
Note that I moved the code related portion of this thread into the Server Code Submissions section here:
http://www.eqemulator.net/forums/showthread.php?t=28916 Code related discussion should go there and any other comments on it should go here to keep the code thread as clean as possible. P.S. I can't wait to try this command out once it compiles properly in Linux! |
hey guys can you only use objects from .eqg files or can you use em from .s3d files also. If so how do you add from an s3d file as they never show up do you add the whole line like CAMPFIRE_DMSPRITEDEF in the doors table
or do you just add CAMPFIRE or do you skip the DM and do CAMPFIRE_SPRITEDEF |
For most objects (maybe all of them), you need to add _ACTORDEF to the end of the name for them to work. So, "CAMPFIRE" becomes "CAMPFIRE_ACTORDEF".
Also, you might want to use s3dspy or the global model viewer to find your models for objects. The .mod files are the ones you want. Another thing is to make sure you always add the name in all capitals or they won't work. |
so for CAMPFIRE its CAMPFIRE_ACTORDEF not SPRITEDEF right and only .mod files can be used?
And you can use objects from s3d and eqg right not just eqg? |
You can only use the object from the zone that is loaded. It will be either the eqg or the s3d of the zone you are in. If the zone you are in has both file types, it always loads the eqg version and ignores the s3d version. If you want to use the old version, you can rename your eqg version and restart EQ.
And no, if the full name is CAMPFIRE_SPRITEDEF.mod, then you will need to add CAMPFIRE_SPRITEDEF_ACTORDEF, I think. You can test this out in-game fairly easily by using the #object command. Just type the following: #object add 12345 0 CAMPFIRE_SPRITEDEF_ACTORDEF Of course that is only going to work if that model name really exists in the zone you are wanting to use it in. It doesn't look right to me, but I don't know where you got that from. |
I got the sprite def from ecommons_obj.s3d or everfrost_obj.s3d
|
I am not sure about those files. The ones I pull object names from is <zonename>.eqg or <zonename>.s3d, not <zonename>_obj.s3d.
|
Yeah, some objects only pull by putting '_ACTORDEF' in front of them. Although usually when you try to write it or '#save object' to the database, it gives a write error because the string is too long apparently. But that is the only way some objects pull, especially the 101, 100, type objects.
|
Quote:
|
Oh wait do I have to use S3D spy, extract the models i want and put it in my zones model file or do i just put a line for the file in the shortname_assets.txt like was stated in the beginning of this thread.
Also it seems that all the "Models" i was trying to use werent .mod files. Using S3D spy it seems like only the Newer zones contain . mod files not the older ones. Like Northro does but nro doesnt. |
Figured id post this here, this is what ive learned
I only know for a fact that you can use files that were originally in the zone. They will only show up correct after you save the object. Its also easier to use the database with navicat or whatever to add the object than the in game command. The command seems buggy at setting the height ect... If the name is OBJ_ROCKS_D in s3dspy that is what you need to put it, you dont need to add _ACTORDEF or _SPRITEDEF after it unless its in the name. |
Looking for a 3D editor
I want to try my hand at creating 3D content for EQEmulator.
Can someome please head me in the right direction. I have searched for programs to open or edit .s3d files but I have not had much luck. Any help would be much appreciated. Joe |
All times are GMT -4. The time now is 04:59 PM. |
Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.