Announcement

Collapse
No announcement yet.

QuakeC Developer Tools?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • QuakeC Developer Tools?

    I'm having difficulties finding information online about QuakeC in general. Lots of links that go nowhere. Everytime I think I've found the jackpot, I get very little. It's been a bit discouraging.

    What I would like is a good Quake compiler, and instructions on how to use to compile the Open Source files (found here: https://github.com/id-Software/Quake) into a development environment to do stuffs like this for example: Inside3D (I'm using Windows 7 64 bit)

    Normally I wouldn't beg for help like this, but I've hit so many dead ends. Lurking on the forums you all seem helpful so I thought I'd ask.

    Keep Playing Quake!
    I'm still waiting for the official release of Qung Fu...

  • #2
    There is no repository like you would find with Java/python/anything made within 10 years, so it seems like everyone needs to learn the hard way. I don't mind answering questions, since it seems to be the only way to get quakec info passed on.

    The two main compilers I use are frikqcc and qccx. I'd recommend frik since it's newer, and explaining how qccx could be useful would be confusing right now. FTEqcc is also important if you want to get into fancy client-side qc (csqc). I have never worked with csqc, since it requires people to use a certain quake engine, but it gives many options that aren't practical or possible with regular qc. Anyway, with the original, vanilla .qc files, any compiler will work just fine.

    You will NOT be compiling ANY code at that github link if you are looking to just compile quakec. The code you linked is used to make a quake engine, which, in this case, is winquake.exe. Perhaps that is your goal?

    Comment


    • #3
      http://www.gamers.org/dEngine/quake/...c34/index2.htm

      and

      QuakeC basics - Quake Wiki
      http://www.aegidian.org/gqt/relics/qcrm.pdf
      MODSonline - Tutorials, Scripting for QuakeC Defined (part 1)
      https://gitlab.com/xonotic/xonotic/w...tion_to_QuakeC
      Quake-C Specificacions v1.0
      QuakeC Manual

      You don't need all of these links. In some cases there is direct duplication within those links. Just pick one or 2. The first one is as close as you are gonna get to something modern which explains the entire source. The rest are manuals, specs, cheatsheets, etc...

      I'm not really sure what info you couldn't find on QC, these links are a "shotglass" of QuakeC from the gallon jug that google provides you with upon searching.

      Of course if you are gonna mess with QC it helps to actually use a QC source. Click here and select the QuakeC tab/button. You will be presented with source code and compilers to choose from.

      Want to learn how to learn using Quake C as the focus? Click here and take my tutorial.
      Last edited by MadGypsy; 04-28-2015, 01:30 PM.
      http://www.nextgenquake.com

      Comment


      • #4
        FTEQCC is the compiler I'll recommend, it supports a lot of extensions like classes, arrays, struct arrays, and pointers (the latter only supported by FTE, the engine.)

        One thing to understand is that there have been a lot of extensions created since QuakeC/Quake was released, everything from string handling to file loading to client-side QC. The two standard engines that provide all those extensions are FTE and Darkplaces.

        But for simply starting out with QC, I recommend a good text editor (such as vim or notepad++), the FTEQCC compiler (from here) and the Clean QC source code.

        For QC questions, I recommend IRC: irc.anynet.org #qc That's where several active (and many not very active, but knowledgeable) QC programmers hang out. The community is so small that asking people personally is the best way to go.

        I'll gladly answer questions as well.
        Scout's Journey
        Rune of Earth Magic

        Comment


        • #5
          Originally posted by Zop View Post
          There is no repository like you would find with Java/python/anything made within 10 years, so it seems like everyone needs to learn the hard way. I don't mind answering questions, since it seems to be the only way to get quakec info passed on.

          The two main compilers I use are frikqcc and qccx. I'd recommend frik since it's newer, and explaining how qccx could be useful would be confusing right now. FTEqcc is also important if you want to get into fancy client-side qc (csqc). I have never worked with csqc, since it requires people to use a certain quake engine, but it gives many options that aren't practical or possible with regular qc. Anyway, with the original, vanilla .qc files, any compiler will work just fine.

          You will NOT be compiling ANY code at that github link if you are looking to just compile quakec. The code you linked is used to make a quake engine, which, in this case, is winquake.exe. Perhaps that is your goal?
          I was hoping to make a DOS flavored Quake so it could be used (without too much tweaking) on multiple platforms. So - barring a full source rebuild.

          How do I use frikqcc to compile a workable Progs.dat?
          For Example: listed here Inside3D - is a "Scratch Tutorial" it gives steps for making the files with the code/variables to make a very basic Progs.dat.
          But it fails to tell you where to get a compiler, or how to use it to make a working Progs.dat file.
          I'm still waiting for the official release of Qung Fu...

          Comment


          • #6
            Originally posted by MadGypsy View Post
            http://www.gamers.org/dEngine/quake/...c34/index2.htm

            and

            QuakeC basics - Quake Wiki
            http://www.aegidian.org/gqt/relics/qcrm.pdf
            MODSonline - Tutorials, Scripting for QuakeC Defined (part 1)
            https://gitlab.com/xonotic/xonotic/w...tion_to_QuakeC
            Quake-C Specificacions v1.0
            QuakeC Manual

            You don't need all of these links. In some cases there is direct duplication within those links. Just pick one or 2. The first one is as close as you are gonna get to something modern which explains the entire source. The rest are manuals, specs, cheatsheets, etc...

            I'm not really sure what info you couldn't find on QC, these links are a "shotglass" of QuakeC from the gallon jug that google provides you with upon searching.

            Of course if you are gonna mess with QC it helps to actually use a QC source. Click here and select the QuakeC tab/button. You will be presented with source code and compilers to choose from.

            Want to learn how to learn using Quake C as the focus? Click here and take my tutorial.
            Very nice. This is the jackpot I was looking for
            I'm going to download these resources. I'm sure it's only going to be a few months before some of them are defunct. I wonder how many others will wander by years from now and go,

            "Awwww! I was hoping those links were still active!"

            Heh. Thanks MadGypsy.
            I'm still waiting for the official release of Qung Fu...

            Comment


            • #7
              Originally posted by golden_boy View Post
              FTEQCC is the compiler I'll recommend, it supports a lot of extensions like classes, arrays, struct arrays, and pointers (the latter only supported by FTE, the engine.)

              One thing to understand is that there have been a lot of extensions created since QuakeC/Quake was released, everything from string handling to file loading to client-side QC. The two standard engines that provide all those extensions are FTE and Darkplaces.

              But for simply starting out with QC, I recommend a good text editor (such as vim or notepad++), the FTEQCC compiler (from here) and the Clean QC source code.

              For QC questions, I recommend IRC: irc.anynet.org #qc That's where several active (and many not very active, but knowledgeable) QC programmers hang out. The community is so small that asking people personally is the best way to go.

              I'll gladly answer questions as well.
              You guys are just GREAT. I've run into many communities where asking questions just attracted sharks and ridicule. This is a great site.

              Fantastic resources, thank you Golden Boy.
              I'll hop on that IRC channel soon.

              EDIT: The link to the "Clean QC source code" appears to be dead any other options to download it?
              Last edited by QuakeKid; 04-28-2015, 02:34 PM.
              I'm still waiting for the official release of Qung Fu...

              Comment


              • #8
                My mistake.

                https://gitorious.org/quakec-v1-01-clean

                Click on the repository, then Download.

                To make a progs.dat, put the QC files and the compiler binary in the same directory, then run the compiler. It looks for a file called progs.src that contains a list of the QC files to be compiled into the progs, and the output path for the progs.

                https://gitorious.org/cleanqc

                Here's the 1.06 source (which is not GPL, unlike the 1.01 Clean QC.) It's fine to make mods with though.

                https://gitorious.org/clean_csqc

                Here's a clean CSQC source, note that you need a CSQC capable engine and it will expect it as a "csprogs.dat." This is good for custom HUDs and the like.
                Last edited by golden_boy; 04-28-2015, 02:17 PM.
                Scout's Journey
                Rune of Earth Magic

                Comment


                • #9
                  for thread prosperity (gitorious is shutting down) here are those same links on gitlab.

                  https://gitlab.com/quakec-v1-01-clea...ec-v1-01-clean

                  https://gitlab.com/clean_csqc/cleancsqc
                  Gnounc's Project Graveyard Gnounc's git repo

                  Comment


                  • #10
                    OK, next question. This might seem a bit basic, but my brain will break if I don't understand it.

                    If I understand correctly, Quake requires 11 functions to be properly configured before spawning a map/level/world whatchamathing. These 11 functions need to be in the code and correctly configured, as the engine must reference each item when the world is in motion. These functions are the basic mechanics that make the engine run continuously.

                    I'm trying to define the following functions, as many of the places I'm looking do a half-assed job of it.

                    Worldspawn: Initiates the world spawning process.
                    Main: Called immediately after for base subroutines, basic variables and entities.
                    StartFrame: Called to start each frame.
                    ClientConnect: Called when players connect to the server, and when a new level loads (for each player playing in the new level). It’s used to announce to current players when a new player connects.
                    ClientKill: Called when a player suicides (this happens when players accidentally blow themselves up with rocket or grenade launcher – Fun Times!).
                    PutclientInServer: Called when the client player entity spawns in game.
                    PlayerPreThink: Called at every frame before physics.
                    PlayerPostThink: Called at every frame after physics.
                    SetNewParms: Called when level changes and the players in-game connect to the server, when the level loads.
                    SetChangeParms: Called when level changes to set parms for self to be restored.


                    I don't get the three definitions listed below ▼, that I yoinked from the list above ^ but I can't find resources to explain them:
                    StartFrame: Called to start each frame.
                    PlayerPreThink: Called at every frame before physics.
                    PlayerPostThink: Called at every frame after physics.

                    For StartFrame - what do they mean by frame? Animation? What is their reference for frame?
                    For PlayerPre & Post think - when frame is referenced here is it the same frame mentioned in StartFrame? If it is what is it? And how is the frame called before or after physics? What is the physics they are referring to?

                    I like physics... without it there'd be no Lava Lamps.
                    Last edited by QuakeKid; 04-29-2015, 02:45 PM.
                    I'm still waiting for the official release of Qung Fu...

                    Comment


                    • #11
                      A frame is a render frame. The engine renders x frames per second, and each frame these functions are run. (edit: I might actually have misremembered this, it might just be a server frame or something since the progs.dat runs on the server of course, not on the client. I hope Spike or someone like that will clarify it.)

                      Player Pre Think is a function called at the start of every frame, Player Post Think at the end.

                      That's all there is to it.

                      You can use these functions to do stuff that should be run every frame (i.e. continuously), such as checking whether the player is in water or whatever.

                      Animation in Quake is done at 10 frames per second, which is why interpolation is needed at higher frame rates to make the animations look smooth. All the animation macros in the monster code assume a speed of 10 fps. Ancient, I know.

                      Edit: IIRC main() is never actually run in qc.

                      Edit 2: Why are you trying to write a progs.dat from scratch? Isn't that kinda brutal?

                      Edit 3: And before you ask, the QC source is full of functions that are never called from another function - monster_ogre() and such. These functions contain monster spawn directions and similar stuff, they are spawn functions that get called when a map file is loaded. If the map has a monster_ogre entity, monster_ogre() in the qc gets called to set it up with the origin etc supplied by the map.
                      Last edited by golden_boy; 04-29-2015, 03:25 PM.
                      Scout's Journey
                      Rune of Earth Magic

                      Comment


                      • #12
                        Originally posted by golden_boy View Post
                        Edit 2: Why are you trying to write a progs.dat from scratch? Isn't that kinda brutal?
                        Thanks for the answer!

                        I'm following a Scratch tutorial because the idea of creation from almost nothing has always fascinated me. To bring Quake back to a primitive "scratch" state and then add everything back just seems epic, it works with my mind so it's a good way to learn. The tut is listed here: Inside3D

                        EDIT: Besides ID has self admittedly said that much of their code was slapped together, and inefficient. I'm not bagging on ID, I give them their props. I wouldn't have the QuakeC playground if not for them and they're talented to the core.

                        I'm also rewriting the tutorial as I go, it's really not written very well, but as slapped together as it is - it's still a better tut than many I've seen. After reading it several times, what it's describing doesn't seem overly complex. He does have the reader do a lot of stuff without explaining why, but with repetition, I've been learning a lot.
                        I'm still waiting for the official release of Qung Fu...

                        Comment


                        • #13
                          @QuakeKid & Progs from scratch

                          I did that...almost. If you would like I can find my code and you can finish/use/change it. My code is built entirely on inheritance. I removed a LOT of the repetitive code and rewrote it in a way that fakes class behavior.

                          There are a few problems though. They are all engine related and I don't remember exactly what. I do remember I was setting some var that spike told me you should never set. However, even though you maybe aren't supposed to...it was working (for what that's worth)

                          @tutorial for QC from scratch
                          It is an unfinished tutorial and it was the basis for my rewrite.

                          Just to give you some extra confidence - rewriting the QC source from scratch really isn't a big deal at all. It's like 20 pages of code or so. Let's put that in some kind of perspective. If you rewrote 3 pages a day (which isn't a whole lot), you could be done in a week or so. If you are one of these people (like myself) that can dedicate 15 hours a day to writing it, you could probably finish in about 3 days (or less).

                          This of course is assuming that you already have experience programming in some scripting language and you are a proficient typer.
                          Last edited by MadGypsy; 04-29-2015, 05:56 PM.
                          http://www.nextgenquake.com

                          Comment


                          • #14
                            Here is an example of how my code is very different. Note that I am doing my own error checking, which really isn't necessary and I am also forcing errors if my functions are used differently than I intended. This means that technically you could properly (in a qc way) call one of my functions and it will fail if it isn't how I decided it should be. Mostly the difference is calling a function from within a function versus being a func_entity. These IFunctions can't be entities.

                            Code:
                            /*
                            	Gypsy:Gnome - Interface Models | created 20130513-15:18cn
                            */
                            
                            // Gypsy - instantiate a model | build 20130514-00:29cn
                            void() IModelInstance =	//modelpath value could potentially come from somewhere other than self
                            {	//INTERFACE
                            	InstanceRules("IModelInstance",1); 	//ERR_INTERFACEONLY
                            	if(self.urgent > 0) return; 		//error
                            	/*	if model is a required field:
                            			check it in self and handle before calling IModelInstance
                            	*/
                            	if( check_string(self.model) && !(self.spawnflags & INVISIBLE) ) precache_model(self.model);
                            		
                            	setmodel(self, self.model);
                            	setorigin (self, self.origin);
                            	
                            	//SLIDEBOX size is mandatory
                            	if( self.solid == SOLID_SLIDEBOX )
                            	{	if( check_vector(self.neg_pnt) && check_vector(self.pos_pnt) )
                            			setsize(self, self.neg_pnt, self.pos_pnt); 
                            		else
                            		{	ThrowException(ERR_PNTS);
                            			return;
                            		}
                            	} else {
                            		if( check_vector(self.neg_pnt) && check_vector(self.pos_pnt) )
                            		{	setsize(self, self.neg_pnt, self.pos_pnt); 
                            			manage_notifications(WARN_OVERRIDE_PNTS);
                            		}
                            		else
                            		{	setsize(self, self.mins, self.maxs); 
                            			manage_notifications(WARN_ABS_PNTS);
                            		}
                            	}
                            	
                            	if(self.spawnflags & INVISIBLE)	self.model = "";
                            };
                            
                            // Gypsy - set the interaction type attributes and instantiate a model | build 20130514-00:29cn
                            void() ITypedInstance =
                            {	//INTERFACE
                            	InstanceRules("ITypedInstance",1); 	//ERR_INTERFACEONLY
                            	if(self.urgent > 0) return; 		//error
                            	/*	if any of the below is a required field:
                            			check it in self and handle before calling ITypedInstance
                            	*/
                            	if( self.spawnflags & FL_MAKESTATIC )
                            	{	if( !check_string(self.effectname) ) //can't apply effects to static entities
                            		{	if( check_string(self.model) )
                            			{	IModelInstance();
                            				makestatic(self);
                            				return;
                            			}
                            			ThrowException(ERR_STATIC_NOMODELPATH);
                            			return;
                            		} else manage_notifications(WARN_STATIC_EFFNAME);
                            	}
                            	
                            	if(!self.solid)
                            	{	if(self.health) manage_notifications(WARN_SOLIDNOT_HEALTH);
                            		manage_notifications(WARN_SOLID);
                            	}
                            	
                            	if(!self.movetype)
                            	{	self.movetype = MOVETYPE_NONE;
                            		manage_notifications(WARN_MOVETYPE);
                            	}
                            	
                            	if(!self.takedamage)
                            	{	self.takedamage = DAMAGE_NO;
                            		self.health = 0;
                            		manage_notifications(WARN_TAKEDAMAGE);
                            	}
                            	
                            	if(!self.health)
                            	{	if(self.takedamage) 
                            		{	ThrowException(ERR_NOHEALTH_DMG);
                            			return;
                            		}
                            	} else {
                            		self.max_health = self.health;
                            		if(!self.takedamage)
                            		{	manage_notifications(WARN_HEALTH_NODMG);
                            		}
                            	}
                            	
                            	IModelInstance(); 
                            	//no point in checking for an error before continuing it will be caught in self
                            };
                            
                            // Gypsy - instantiate a field | build 20130523-21:06cn
                            entity() IFieldInstance =
                            {	//INTERFACE
                            	InstanceRules("IFieldInstance",1); 			//ERR_INTERFACEONLY
                            	if(self.urgent > 0) return entity_null; 	//error
                            	
                            	local entity field;	
                            	field = spawn();
                            	field.movetype = MOVETYPE_NONE;
                            	field.solid = SOLID_TRIGGER;
                            	field.owner = self;								//set owner to the parentest entity of this call
                            	field.type = OBJ_FIELD; 						//set a friendly classname
                            	field.spawnflags = ( self.spawnflags & 256 );	//player only - force inheritance so movebrush_activate() doesn't have a zillion conditions 
                            	
                            	local vector u, v;				//calculate final dimensions
                            	u = (self.absmin-self.fieldspread);
                            	v = self.absmax+self.fieldspread;
                            	
                            	if(!u && !v)					//check final dimensions
                            	{	ThrowException(ERR_NODIMS);
                            		return entity_null;
                            	}
                            	
                            	setsize (field, u, v);			//apply dimensions
                            	
                            	u = make_multiplier(field_ofs, .5);
                            	setorigin(field, self.origin + self.field_ofs * fabs(u * field.size));
                            	
                            	return field;
                            };
                            
                            // Gypsy - attach physics properties to a model | build 20130515-14:02cn
                            void() ITypedPhysicsInstance =
                            {	//INTERFACE
                            	InstanceRules("ITypedPhysicsInstance",1); 	//ERR_INTERFACEONLY
                            	if(self.urgent > 0) return; 				//error
                            	/*	if any of the below is a required field:
                            			check it in self and handle before calling ITypedInstance
                            	*/
                            	
                            };
                            
                            // Gypsy - create an interactive brush | build 20130527-22:12cn
                            void() IMovebrushInstance =
                            {	//INTERFACE
                            	InstanceRules("IMovebrushInstance",1); 		//ERR_INTERFACEONLY
                            	if(self.urgent > 0) return; 				//error
                            		
                            	self.solid = SOLID_BSP;						//set necessary types
                            	self.movetype = MOVETYPE_PUSH;
                            	
                            	//fallback on defaults
                            	if (!self.speed) self.speed = 100;
                            	if (!self.wait) self.wait = 3;
                            	if (!self.dmg) self.dmg = 2;
                            
                            	//consider health a determinant of whether the entity can be damaged
                            	if(self.health) self.takedamage = DAMAGE_YES; else self.takedamage = DAMAGE_NO;
                            	
                            	ITypedInstance();				//make the brush a model
                            	if(self.urgent > 0) return;		//check for creamy goodness
                            	
                            	if( self.spawnflags & 8 )		//MB_FIELD	
                            	{	//if the trigger is not the brush - size, spawn and make active a trigger field
                            		if( !check_vector(self.fieldspread) ) self.fieldspread = '40 40 4';	//default
                            		self.trigger_field = IFieldInstance();								//create and store entity
                            	} 				
                            };
                            
                            // Gypsy - attach an ambient sound to a model | build 20130514-00:29cn
                            void() IAmbientSoundInstance =
                            {	//INTERFACE
                            	InstanceRules("IAmbientSoundInstance",1); 	//ERR_INTERFACEONLY
                            	if(self.urgent > 0) return; 				//error
                            	/*	if soundpath is a required field:
                            			check it in self and handle before calling IAmbientSoundInstance
                            	*/
                            	if( check_string(self.soundpath) )
                            	{	if( !self.vol )
                            		{	self.vol = 0.5;
                            			manage_notifications(WARN_VOLUME);
                            		}
                            		if( !self.attn )
                            		{	self.attn = ATTN_NONE;
                            			manage_notifications(WARN_ATTENUATION);
                            		}
                            
                            		precache_sound (self.soundpath);
                            		ambientsound (self.origin, self.soundpath, self.vol, self.attn);
                            	} else {
                            		manage_notifications(WARN_SOUNDPATH);
                            	}
                            };
                            
                            //spawn_*_effect can act as an interface model but is also a standalone entity
                            void() spawn_point_effect;
                            void() spawn_area_effect;
                            void(entity area) spawn_effect_particle;
                            note: ERR_INTERFACEONLY, would be better worded as ERR_SUBCLASSONLY. Cause essentially, these functions can only be called within another function. That function then inherits this functions processes, essentially faking a scenario where this function becomes the Super of the one that called it.... not really but it can be imagined that way. The function that calls the IFuncs will extend the possibilities of the IFunc, never the other way around.

                            Then you can utilize that in this way

                            Code:
                            //	Gypsy - custom entity | build 20130513-16:53cn
                            void() spawn_custom =
                            {	//FINAL
                            	InstanceRules("spawn_custom",0); 	//ERR_NOSUBCLASS
                            	if(self.urgent > 0) return; 		//error
                            	
                            	if(self.gnomename) self.classname = self.gnomename;	//changing a classname in a map messes up that entity - fake it
                            	else manage_notifications(WARN_GNOMENAME);
                            	
                            	ITypedInstance();			//abstract.qc - create the appropriate model
                            	if(self.urgent > 0) return;	//error
                            		
                            	if( check_vector(self.view_ofs) )
                            		manage_notifications(WARN_VIEW_OFS);
                            	
                            	
                            	if( check_string(self.animator) )
                            	{	//animator
                            	
                            	} 
                            	else manage_notifications(WARN_ANIMATOR);
                            	
                            	spawn_point_effect();			//this qc - add an effect from effectinfo.txt
                            	//won't throw an error here
                            	
                            	ThrowNotifications(); 	//output.qc - print all notifications to the console
                            };
                            notice how spawn_custom calls ITypedInstance which calls IModelInstance. This is the flow for creating any and all entities.

                            In the map you would use a spawn custom and fill in all the entity properties in the entity inspector. Those properties get cycled through the chain I just described and whatever your entity was "propertied" to be is what is spit out. However, some properties are done for you, like figuring out the solid and movetype of things.

                            of course you can't really rewrite the qc (in my way) without completely redifining the maps entities.ent file. Here is an example of the spawn_custom ent
                            Code:
                            	<point name="spawn_custom" color="0 0 0.5" box="-4 -4 -4 4 4 4">
                            		Fields
                            		<string key="target" name="target">target</string>
                            		<string key="targetname" name="targetname">targetname</string>
                            		<string key="grouptname" name="targetname">targetname</string>
                            		<string key="effectname" name="effectname">effectname</string>
                            		<string key="model" name="model">The path to your model</string>
                            		<integer key="health" name="health">Entity health</integer>
                            		<integer key="solid" name="solid">
                            		SOLID_NOT		= 0
                            		SOLID_TRIGGER	= 1
                            		SOLID_BBOX		= 2
                            		SOLID_SLIDEBOX	= 3
                            		SOLID_BSP		= 4
                            		SOLID_CORPSE 	= 5		-	/GQC/dpdefs/dpextensions.qc - line 1377: make sure you read that
                            		</integer>
                            		<integer key="movetype" name="movetype">
                            		MOVETYPE_NONE			= 0
                            		MOVETYPE_ANGLENOCLIP    = 1
                            		MOVETYPE_ANGLECLIP      = 2
                            		MOVETYPE_WALK			= 3
                            		MOVETYPE_STEP			= 4
                            		MOVETYPE_FLY			= 5
                            		MOVETYPE_TOSS			= 6
                            		MOVETYPE_PUSH			= 7
                            		MOVETYPE_NOCLIP			= 8
                            		MOVETYPE_FLYMISSILE		= 9
                            		MOVETYPE_BOUNCE			= 10
                            		MOVETYPE_BOUNCEMISSILE	= 11
                            		MOVETYPE_FOLLOW 		= 12	- 	/GQC/dpdefs/dpextensions.qc - line 552: make sure you read that
                            		MOVETYPE_PHYSICS 		= 32
                            		</integer>
                            		<integer key="takedamage" name="takedamage">
                            		DAMAGE_NO				= 0
                            		DAMAGE_YES				= 1
                            		DAMAGE_AIM				= 2
                            		</integer>
                            		<real3 key="neg_pnt" name="mins">bounding box extents reletive to origin</real3>
                            		<real3 key="pos_pnt" name="maxs">bounding box extents reletive to origin</real3>
                            		<real3 key="view_ofs" name="view_ofs">view offset from origin</real3>
                            		<string key="animator" name="animator">animator</string>
                            		<string key="physics" name="physics"></string>
                            		<real3 key="origin" name="origin"></real3>
                            	</point>
                            Note when I say the above chain is the method for spawning any and all entities I don't mean that you have to use a spawn_custom. I mean that you would call ITypedInstance in your version of "spawn_custom" (ie your more specific entity) after you have checked that any field your entity requires is assigned. This means you never have to keep retyping the basic code to instantiate and assign move/solid properties to your models. That code is done and all you have to do is call the "kick off" function. My spawn_custom is simply the most wide open model that can be made. It is nothing and everything...depending on the properties you select.
                            Last edited by MadGypsy; 04-29-2015, 06:58 PM.
                            http://www.nextgenquake.com

                            Comment


                            • #15
                              More fun stuff

                              Here was the "class" that forced errors and recorded warnings. Understand that these custom messages are intended to make sure that MY code is being used the way I intended and to spit out warnings that can help you track down missing properties in YOUR MAP.

                              In other words. A whole lot of this is stuff that a stack trace isn't going to report.

                              Code:
                              //	Gypsy:Gnome - Exceptions Manager | build 20130513-14:39cn
                              float ERR_GNOMENAME 			= 1;
                              float ERR_MODELPATH 			= 2;
                              float ERR_SOUNDPATH 			= 3;
                              float ERR_EFFECTNAME			= 4;
                              float ERR_ANIMATOR				= 5;
                              float ERR_ID					= 6;
                              float ERR_TARGET				= 7;
                              float ERR_TARGETNAME			= 8;
                              float ERR_STATIC_NOMODELPATH	= 9;
                              float ERR_NOHEALTH_DMG			= 10;
                              float ERR_HEALTH_NODMG			= 11;
                              float ERR_VIEW_OFS				= 12;
                              float ERR_URGENT				= 13;
                              float ERR_NOINTERFACE			= 14;
                              float ERR_INTERFACEONLY			= 15;
                              float ERR_PNTS					= 16;
                              float ERR_WEAPON				= 17;
                              float ERR_PASSEDFLOOR			= 18;
                              float ERR_NODATA2WRITE			= 19;
                              float ERR_MOVEDIR				= 20;
                              float ERR_SPEED					= 21;
                              float ERR_NODIMS				= 22;
                              float ERR_TYPE					= 23;
                              float ERR_NOTARGETS				= 24;
                              float ERR_GROUP					= 25;
                              float ERR_GROUPNAME				= 26;
                              
                              void(float err) ThrowException =
                              {	//hint to the object
                              	dprint("\n\n");
                              	dprint("GNOME PHUQUP: an exception was thrown by: ");
                              	dprint(self.classname);
                              	dprint(" at: ");
                              	dprint(vtos(self.origin));
                              	dprint("\n");
                              	
                              	switch(err)
                              	{	case ERR_GNOMENAME:
                              			dprint("ERR_GNOMENAME: It is mandatory to set the classname field.\n");
                              			break;
                              		case ERR_MODELPATH:
                              			dprint("ERR_MODELPATH: It is mandatory to set the model field.\n");
                              			break;
                              		case ERR_SOUNDPATH:
                              			dprint("ERR_SOUNDPATH: It is mandatory to set the sound field.\n");
                              			break;
                              		case ERR_EFFECTNAME:
                              			dprint("ERR_EFFECTNAME: It is mandatory to set the effectname field.\n");
                              			break;
                              		case ERR_ANIMATOR:
                              			dprint("ERR_ANIMATOR: It is mandatory to set the animator field.\n");
                              			break;
                              		case ERR_ID:
                              			dprint("ERR_ID: It is mandatory to set the id field.\n");
                              			break;
                              		case ERR_TARGET:
                              			dprint("ERR_TARGET: It is mandatory to set the target field.\n");
                              			break;
                              		case ERR_TARGETNAME:
                              			dprint("ERR_TARGETNAME: It is mandatory to set the targetname field.\n");
                              			break;
                              		case ERR_STATIC_NOMODELPATH:
                              			dprint("ERR_STATIC_NOMODELPATH: You tried to turn an empty into a static. set the model field.\n");
                              			break;
                              		case ERR_NOHEALTH_DMG:
                              			dprint("ERR_NOHEALTH_DMG: You gave a damagable entity no health.\n");
                              			break;
                              		case ERR_HEALTH_NODMG:
                              			dprint("ERR_HEALTH_NODMG: You gave health to a non-damagable entity.\n");
                              			break;
                              		case ERR_VIEW_OFS:
                              			dprint("ERR_VIEW_OFS: It is mandatory to set the view_ofs field.\n");
                              			break;
                              		case ERR_URGENT:
                              			dprint("ERR_URGENT: This entity is being instantiated by an incompatible entity.\n");
                              			break;
                              		case ERR_NOINTERFACE:
                              			dprint("ERR_NOINTERFACE: This entity cannot be subclassed in another entity.\n");
                              			break;
                              		case ERR_INTERFACEONLY:
                              			dprint("ERR_INTERFACEONLY: This entity must be subclassed in another entity.\n");
                              			break;
                              		case ERR_PNTS:
                              			dprint("ERR_PNTS: It is mandatory that both the mins and maxs fields are set.\n");
                              			break;
                              		case ERR_WEAPON:
                              			dprint("ERR_WEAPON: This entity must be subclassed in another entity.\n");
                              			break;
                              		case ERR_PASSEDFLOOR:
                              			dprint("ERR_PASSEDFLOOR: It is mandatory that both the mins and maxs fields are set.\n");
                              			break;
                              		case ERR_NODATA2WRITE:
                              			dprint("ERR_NODATA2WRITE: There was no data to write to the file.\n");
                              			break;
                              		case ERR_MOVEDIR:
                              			dprint("ERR_MOVEDIR: The movedir field was not set for this entity.\n");
                              			break;
                              		case ERR_SPEED:
                              			dprint("ERR_SPEED: There was no speed set for this entity.\n");
                              			break;
                              		case ERR_NODIMS:
                              			dprint("ERR_NODIMS: This entity has no dimensions.\n");
                              			break;
                              		case ERR_TYPE:
                              			dprint("ERR_TYPE: It is mandatory that the type field is set for this entity.\n");
                              			break;
                              		case ERR_NOTARGETS:
                              			dprint("ERR_NOTARGETS: It is mandatory that this entity have available targets.\n");
                              			break;
                              		case ERR_GROUP:
                              			dprint("ERR_GROUP: It is mandatory that the group field is set for this entity.\n");
                              			break;
                              		case ERR_GROUPNAME:
                              			dprint("ERR_GROUPNAME: It is mandatory that the groupname field is set for this entity.\n");
                              			break;
                              		default:
                              			dprint(ftos(err));
                              			dprint(" is not a recognized error number.\n");
                              			break;
                              	}
                              	
                              	self.urgent = err;
                              }
                              
                              // Gypsy - force a structure into the code
                              // factor = 0: The entity canNOT be included in another entity
                              // factor = 1: The entity can ONLY be included in another entity
                              void(string match, float factor) InstanceRules =
                              {	if(!factor)
                              	{	if(self.classname != match)
                              		{	ThrowException(ERR_NOINTERFACE);
                              			self.urgent = ERR_NOINTERFACE;
                              			return;
                              		}
                              	} else {
                              		if(self.classname == match)
                              		{	ThrowException(ERR_INTERFACEONLY);
                              			self.urgent = ERR_INTERFACEONLY;
                              			return;
                              		}
                              	}
                              }
                              
                              //	Gypsy:Gnome - Notification Manager | build 20130513-12:37cn
                              float WARN_GNOMENAME 		= 1;
                              float WARN_MODELPATH 		= 2;
                              float WARN_SOUNDPATH 		= 3;
                              float WARN_EFFECTNAME		= 4;
                              float WARN_ANIMATOR			= 5;
                              float WARN_ID				= 6;
                              float WARN_TARGET			= 7;
                              float WARN_TARGETNAME		= 8;
                              float WARN_STATIC_EFFNAME	= 9;
                              float WARN_SOLID			= 10;
                              float WARN_SOLIDNOT_HEALTH	= 11;
                              float WARN_HEALTH_NODMG		= 12;
                              float WARN_TAKEDAMAGE		= 13;
                              float WARN_MOVETYPE			= 14;
                              float WARN_VOLUME			= 15;
                              float WARN_ATTENUATION		= 16;
                              float WARN_VIEW_OFS			= 17;
                              float WARN_OVERRIDE_PNTS	= 18;
                              float WARN_ABS_PNTS			= 19;
                              float WARN_FILE				= 20;
                              float WARN_FILE_READ		= 21;
                              float WARN_FILE_COMPLETE	= 23;
                              float WARN_DMGSETNO			= 24;
                              float WARN_ORFIELDSPREAD	= 25;
                              
                              //returns the appropriatre string back to an array index that matches the warning number
                              string(float info) get_notice =
                              {	local string temp;
                              	temp = string_null;	//compiler
                              	switch(info)
                              	{	case WARN_GNOMENAME:
                              			temp = "gnomename field has not been set.\n";
                              			break;
                              		case WARN_MODELPATH:
                              			temp = "model field has not been set. this entity must be a brush or empty. \n";
                              			break;
                              		case WARN_SOUNDPATH:
                              			temp = "sound field has not been set.\n";
                              			break;
                              		case WARN_EFFECTNAME:
                              			temp = "effectname field has not been set.\n";
                              			break;
                              		case WARN_ANIMATOR:
                              			temp = "no animator was assigned.\n";
                              			break;
                              		case WARN_ID:
                              			temp = "id field has not been set.\n";
                              			break;
                              		case WARN_TARGET:
                              			temp = "target field has not been set.\n";
                              			break;
                              		case WARN_TARGETNAME:
                              			temp = "targetname field has not been set.\n";
                              			break;
                              		case WARN_MOVETYPE:
                              			temp = "movetype field was not set and defaulted to MOVETYPE_NONE.\n";
                              			break;
                              		case WARN_TAKEDAMAGE:
                              			temp = "takedamage field was not set and defaulted to DAMAGE_NO.\n";
                              			break;
                              		case WARN_SOLID:
                              			temp = "solid field was not set and defaulted to SOLID_NOT.\n";
                              			break;
                              		case WARN_VIEW_OFS:
                              			temp = "view_ofs has not been set. the eye level of your model is assigned to it's origin point.\n";
                              			break;
                              		case WARN_VOLUME:
                              			temp = "volume field was not set and defaulted to 0.5\n";
                              			break;
                              		case WARN_ATTENUATION:
                              			temp = "attenuation field was not set and defaulted to ATTN_NONE\n";
                              			break;
                              		case WARN_STATIC_EFFNAME:
                              			temp = "you cannot set an effect on static entities. your entity was defaulted to SOLID_NOT and MOVETYPE_NONE to allow the effect.\n";
                              			break;
                              		case WARN_SOLIDNOT_HEALTH:
                              			temp = "you have health on a SOLID_NOT\n";
                              			break;
                              		case WARN_OVERRIDE_PNTS:
                              			temp = "you are overriding the absmin/max properties.\n";
                              			break;
                              		case WARN_ABS_PNTS:
                              			temp = "you are using absmin/max values for your bbox. \n";
                              			break;
                              		case WARN_FILE:
                              			temp = "some file interaction didn't complete. \n";
                              			break;
                              		case WARN_FILE_READ:
                              			temp = "couldn't read the file data \n";
                              			break;
                              		case WARN_FILE_COMPLETE:
                              			temp = "the file interaction completed successfully. \n";
                              			break;
                              		case WARN_HEALTH_NODMG:
                              			temp = "you have health on a non-damageable entity. \n";
                              			break;
                              		case WARN_DMGSETNO:
                              			temp = "DAMAGE_NO was set for this entity. \n";
                              			break;
                              		case WARN_ORFIELDSPREAD:
                              			temp = "you are overriding the default .fieldspread. \n";
                              			break;
                              	}
                              	if(temp != string_null) self.notify[0] = "1";
                              	else temp = strcat("A notification number that does not exist [", ftos(info), "] was attempted here.\n");
                              	
                              	return temp;
                              }
                              
                              /*
                              	I use an array here because treating it like a flag made the number rack up too fast.
                              	Now I can count in succession and keep the value very low.
                              */
                              void(float info) manage_notifications = 
                              {	cfg_var = CFG_ALLOW_NOTIFY;
                              	if( self.cfg_notify && cfg_var && !check_string(self.notify[info]) )
                              	{	self.notify[info] = get_notice(info);
                              	}
                              };
                              
                              string(string file, float typ, string str) file_manager =
                              {	cfg_var = CFG_WRITE_LOGS; 	//constants can't be placed directly into a conditional without a compiler warning
                              								//so let's assign the value to a global float and use that instead
                              	if(!cfg_var)
                              	{	str = string_null;						
                              		FILES_WRITABLE = 0;
                              	} else {
                              		local float fhandle = fopen(file, typ);//open a file
                              		if(!fhandle)
                              		{	str = string_null;
                              			manage_notifications(WARN_FILE);  	//it's not a crisis so we just add it to notify[WARN_FILE]
                              												//notify[] should be empty - either it was wiped or never existed
                              												//this means WARN_FILE should now be the only notice
                              			FILES_WRITABLE = 0;					//turn off writing 
                              			ThrowNotifications();				//send the report to the console, which will not try to write again
                              												//ending any possible loop.
                              		} else {
                              			if(typ < 1)
                              			{	str = fgets(fhandle);
                              				if( !check_string(str) )
                              				{	manage_notifications(WARN_FILE_READ);
                              					FILES_WRITABLE = 0;	
                              					ThrowNotifications();
                              				}
                              			} else {
                              				if( check_string(str) )
                              				{	fputs(fhandle,str);
                              					manage_notifications(WARN_FILE_COMPLETE);
                              					FILES_WRITABLE = 0;	
                              					ThrowNotifications();
                              				}
                              				else ThrowException(ERR_NODATA2WRITE);
                              			}
                              			fclose(fhandle);					//close the file
                              			if(!FILES_WRITABLE) FILES_WRITABLE = 1;	//turn writing back on
                              		}
                              	}
                              	return str;
                              };
                              
                              //prints a header and then spits out every stored notification
                              void() ThrowNotifications = 
                              {	local string temp;
                              	temp = string_null;
                              	if ( check_string(self.notify[0]) )
                              	{	//hint to the object
                              		temp = strcat("\n\nGNOME NOTIFIER: accumulated field notifications for ",self.classname," at: ",vtos(self.origin),"\n");
                              		
                              		local float i;
                              		for (i=1; i<32; ++i)
                              		{	if( check_string(self.notify[i]) )
                              			{	temp = strcat(temp,self.notify[i]);		//building log string
                              				self.notify[i] = string_null;			//reset
                              			}
                              		}
                              		dprint( strcat(temp,"\n") );
                              	}
                              	cfg_var = CFG_WRITE_LOGS;
                              	if(FILES_WRITABLE && cfg_var)
                              		file_manager("notify.txt", FILE_APPEND, temp);
                              }
                              http://www.nextgenquake.com

                              Comment

                              Working...
                              X