News | Forum | People | FAQ | Links | Search | Register | Log in
Coding Help
This is a counterpart to the "Mapping Help" thread. If you need help with QuakeC coding, or questions about how to do some engine modification, this is the place for you! We've got a few coders here on the forum and hopefully someone knows the answer.
First | Previous | Next | Last
QuakeC Cleaned With Fixes 
Up for review here:

ID1 cleaned source + selected URQP fixes (1 MB)

I would really appreciate if anyone with profound QC knowledge did a thorough review of all the fixes I applied. Everything compiles just fine, but I might still have made some mistake(s). Especially the DM stuff couldn't be tested.

All edits should be sufficiently commented. If you want to see a complete list of all changes, please look into progs_fixed.src (which you also need for compiling the fixed version, progs.src only gives you cleaned up code without any other changes).

BTW: I added a few fixes going beyond URQP. 
QuakeC: Control Viewpoint Angles 
Hey! So, I need some help with this.

I'm working on a mod to put Q2 weapons into Quake. Similar to the Quake 2 Weapons Project from long ago if anyone remembers that.

I got this menu system where players can select a marine to play as: Q1 ranger or Q2 marine. It works pretty well but I have some trouble with the viewpoint angle. I want to lock the viewpoint so the player can't look around until he's selected a class. I can lock the viewpoint alright, the problem is that it'll snap to the wrong angle; 90 degrees to the left or right of the spawnpoint angle usually, and no matter what I try I can't reorient it to look in the right direction.

Here's the code in question:


void() SetMarineClass =
{
local vector loc, ang;
local entity spot;
spot = SelectSpawnPoint();// store the spawnpoint orientation

// disable weaponmodel if we don't have a class yet.
if (!parm11)
self.weaponmodel = string_null;

// Kex fix: delay select text for 0.5 seconds
if (marine_select_wait < 30)
{ marine_select_wait += 1; }

// postpone until wait timer has reached 0.5 seconds / exit if we already have a class
if (marine_select_wait < 30 || parm11 || self.marineclass)
{
menu_origin = self.origin;
menu_marine_selection = 1;
return;
}

// spawn a marine to display as menu choice
else if (marine_select_wait < 1000) // bogus value - doesn't get reached until we set it here
{
// self.v_angle = self.angles = spot.angles; // does nothing wtf?
// self.fixangle = TRUE; // turn this way immediately
makevectors (self.v_angle);
ang = self.v_angle;//spot.angles;

loc = self.origin + v_forward * 100 + v_up * 12;
menu_mdl = SpawnMenuMarine(loc);
menu_mdl.solid = SOLID_NOT;
setmodel(menu_mdl, "progs/player.mdl");
menu_mdl.frame = 12;
marine_select_wait = 1000;
}

// don't let monsters see us
self.notrace = 1;

// rotate menu model every tick
menu_mdl.angles_y -= 2;

// print class options
centerprint(self, "Choose your marine (1-4):");

// restrict movement
self.origin = menu_origin;
self.velocity = '0 0 0';
// restrict view
// Only keeps the correct rotation for the copper start map. Player gets insta-rotated 90 degrees to the left/right in other maps.
msg_entity = self;
WriteByte(MSG_ONE, 10);
WriteAngle( MSG_ONE, ang_x);
WriteAngle( MSG_ONE, ang_y);
WriteAngle( MSG_ONE, ang_z);
// put the marine in front of us at all times
makevectors (self.v_angle);
menu_mdl.origin = self.origin + v_forward * 100 + v_up * 12;



As you can see from the code that's been commented out, I've tried out a few different things to make this work. The result is always the same tho. The player's viewpoint ends up pointing 90 degrees to the left or right of the spawnpoint direction. I'm out of ideas for this one. Does anyone here know what I'm doing wrong and how to fix it?

Here's the pak and progs.dat if you wanna try it out and see for yourself:

https://drive.google.com/file/d/1fpnM1e1_Ux2wcrx2GAuXbVFW_bpkh7ql/view?usp=sharing 
Experiment 
What happens if you try out different hard-coded values in your WriteAngle code?

msg_entity = self;
WriteByte(MSG_ONE, 10);
WriteAngle( MSG_ONE, -20);
WriteAngle( MSG_ONE, 45);
WriteAngle( MSG_ONE, 10);

recompile

msg_entity = self;
WriteByte(MSG_ONE, 10);
WriteAngle( MSG_ONE, 40);
WriteAngle( MSG_ONE, 100);
WriteAngle( MSG_ONE, -25); 
Success! 
Ohh found it. Seems like I made a dumb mistake. 'ang' was a local variable that only got set once, so after the one-off conditional had finished it would just stay zero forever.

Thanks for helping! Here's how the code looks now:

void() SetMarineClass =
{
local vector loc, ang;
local entity spot;
spot = SelectSpawnPoint();// store the spawnpoint orientation


// get orientation from spawn point
self.fixangle = TRUE; // turn this way immediately
makevectors (spot.angles);
ang = spot.angles;


// disable weaponmodel if we don't have a class yet.
if (!parm11)
self.weaponmodel = string_null;


// Kex fix: delay select text for 0.5 seconds
if (marine_select_wait < 30)
marine_select_wait += 1;

// postpone until wait timer has reached 0.5 seconds / exit if we already have a class
if (marine_select_wait < 30 || parm11 || self.marineclass)
{
menu_origin = self.origin;
menu_marine_selection = 1;
return;
}

// spawn a marine to display as menu choice
else if (marine_select_wait < 1000)
{
loc = self.origin + v_forward * 100 + v_up * 12;
menu_mdl = SpawnMenuMarine(loc);
menu_mdl.solid = SOLID_NOT;
setmodel(menu_mdl, "progs/player.mdl");
menu_mdl.frame = 12;
marine_select_wait = 1000;
}

// don't let monsters see us
self.notrace = 1;

// rotate menu model every tick
menu_mdl.angles_y -= 2;

// print class options
centerprint(self, "Choose your marine (1-4):");

// restrict movement
self.origin = menu_origin;
self.velocity = '0 0 0';
// restrict view
msg_entity = self;
WriteByte(MSG_ONE, 10);
WriteAngle( MSG_ONE, ang_x);
WriteAngle( MSG_ONE, ang_y);
WriteAngle( MSG_ONE, ang_z);


// press 1-4 to select
if (self.impulse == 1)
menu_marine_selection = 1;

etc... 
Why Doesn't My Function Run? 
I created a new class chaos.qc and added it to progs.src. I get no compiling errors but when I load up a DM map nothing happens. The bprint doesnt even show up. Trying to add monsters to DM using info_player_deathmatch entity as spawns.

Heres my code contained in chaos.qc:

void() chaos_monsters =
{
bprint("started");

entity spotMon;
entity newMon;

if((total_monsters - killed_monsters) <= 16){

bprint("spawn");
newMon = spawn();
spotMon = SelectSpawnPoint();
precache_model2 ("progs/enforcer.mdl");
precache_model2 ("progs/h_mega.mdl");
precache_model2 ("progs/laser.mdl");

precache_sound2 ("enforcer/death1.wav");
precache_sound2 ("enforcer/enfire.wav");
precache_sound2 ("enforcer/enfstop.wav");
precache_sound2 ("enforcer/idle1.wav");
precache_sound2 ("enforcer/pain1.wav");
precache_sound2 ("enforcer/pain2.wav");
precache_sound2 ("enforcer/sight1.wav");
precache_sound2 ("enforcer/sight2.wav");
precache_sound2 ("enforcer/sight3.wav");
precache_sound2 ("enforcer/sight4.wav");

newMon.classname = ("monster_enforcer");
newMon.solid = SOLID_SLIDEBOX;
newMon.movetype = MOVETYPE_STEP;

setmodel(self, "progs/enforcer.mdl");

setsize(self, '-16 -16 -24', '16 16 40');
newMon.health = 80;

newMon.th_stand = enf_stand1;
newMon.th_walk = enf_walk1;
newMon.th_run = enf_run1;
newMon.th_pain = enf_pain;
newMon.th_die = enf_die;
newMon.th_missile = enf_atk1;

newMon.origin = spotMon.origin + '0 0 1';
newMon.angles = spotMon.angles;
newMon.fixangle = TRUE;
walkmonster_start();

}
}; 
CasJak: 
The function won’t run just by existing. It would need to be called by something else. One exception is if it’s name matches the class name of an entity in the map. This is how spawn functions are called. In your case, I assume there is not an entity in your map called chaos_monsters.

Also, it appears you want to run this in a loop so that it keeps spawning entities. Currently this code would only attempt to spawn one monster and then be finished. Or, if you want to run at most one monster per frame, you could call this function at the start of the frame. 
Metlslime 
How would I call it at the start of a frame? 
 
In world.qc there is a function called StartFrame, you could try calling it there.

On the other hand, it might be better to create an invisible entity to control all of your monster spawning logic, which can then use think and nextthink to periodically check on the state of the world and spawn more monsters when appropriate. If you do it that way, that entity could be created from the worldspawn function. 
Also... 
You might check out Aardappel's "DMSP" mod, which does something similar to what you're working on. It has source code too so you could use it as a reference.

https://strlen.com/maps/dmsp/index.html 
Hhmmm.. 
I guess I’m just not understanding something about how the engine operates. I created an entity with the worldspawn function. It has the same classname as the function above but the function doesn’t run. I obviously am not doing this right haha. I’m trying to spawn the monster handler entity and then make it run a function. That way this mod will work on any DM map. Must be above my skill level right now… Going to have to study that mod you linked. 
CasJak 
It may not have been clear that I was laying out multiple options:

1. Run your logic once every frame. You can do this by calling the function you already have from StartFrame.

2. Create an invisible entity to manage the spawning logic. You would create the entity in the worldspawn function. After creating it you would set nextthink and think. The function you already wrote would be modified to become a think function, which means at the end it needs self.nextthink = time + 0.1;

3. You place an entity in the level which has "classname" "chaos_monsters" then you split your current function into two parts, one is the spawn function and one is the think function -- this isn't really an option because you want it to work on existing levels. I mentioned it earlier just to explain how some function are automatically called based on the names of map entities. This case doesn't apply to you. 
 
How would I call the function using think if the function declaration is after the world spawn function. Would the function need to be declared before the world spawn function?

Say for progs.src
The order I currently have is like this
world.qc
.
.
.
etc.
chaos.qc <- contains my function for handling spawns. I set it at the bottom of progs.src so I can reference monster/ai functions.

Is there a different way to do this so I can access the functions that are defined later in the program?

By the way thank you for taking so much time helping me! 
CasJak 
That's called "forward declaration" and you can do it by naming the function in an earlier file, or in a place earlier in the same file, than where you use it.

For example you could declare the function with this one line, in world.qc before the worldspawn function:

void() chaos_monsters;

This allows you to call it in worldspawn even though the full definition of the function happens in a later file. 
Thank A Ton My Dude 
This is exactly what I needed and couldn’t figure out. Forward declaration is exactly the piece I was missing and needed haha. When I get home I’ll type it up and I’m pretty sure it will work. Thanks a ton mate. 
Every Thing Works 
So everything works as intended now, except for one issue. Some of the spawn points on all the maps are to close to walls or ceilings and the monsters get stuck. They still shoot and try to walk but can't. How can I move them into a valid space using the info_player_deathmatch as the origin? 
That Is Why Droptofloor() Is Your Friend 
Look into original id1 code and see what droptofloor() does when monsters or items are set. Pay attention to dprint messages.

Now use it in your new code to remove the entity if it fails. 
Particle Sphere 
Ok, so I got two things I'm trying to sort out with particles in quakec.

First one is to create a particle explosion effect. What I'm using now is just the in-built explosion effect...

WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
WriteCoord (MSG_BROADCAST, self.origin_x);
WriteCoord (MSG_BROADCAST, self.origin_y);
WriteCoord (MSG_BROADCAST, self.origin_z);
// these both crash the game session
// WriteByte (MSG_BROADCAST, 176); // quake's palette has no cyans!
// WriteByte (MSG_BROADCAST, 8); // palette color range

... which comes with two problems. One, it plays the standard explosion sound. Not a huge deal, I can get around that. Two, I can't change the color of the particles. If I try the game soft-crashes with a 'bad server message' error.

So ideally I'd like to create my own replacement. Unless there's another canned effect I can use that's a bit more flexible. I want this to be compatible with all kinds of different source ports btw.

---

And here's the second thing:
I need a uniform distribution of particles inside a spherical radius. I've got no idea how to go about this though. Is this doable with quakec? I mean, the Quake 2 Weapons mod has an effect like this for the BFG projectile so it has to be, right?

I thought I could use a for-loop and particle() to spawn particles at random within a limited radius but so far I haven't had much luck. Any ideas? Any open-source examples out there doing something similar? 
EF_BRIGHFIELD Is The Ticket.. 
Nevermind figured it out. It's all good 
 
Is there a good set of tutorials for beginners looking to dabble in QuakeC in 2021? 10-15 years ago I followed a few of the tutorials on inside3d with some success, but they're largely copy&paste guides and they seem to focus mostly on changing existing game assets instead of adding new stuff. They're also quite reliant on outdated compilers and source codes. 
You Might Look 
at quaketastic.

I copied all related stuff from Inside3D there as my experiences from the Quake lab, the first published examples from Id. 
Thanks Madfox I'll Have A Look 
 
The QuakeLab.., 
My anxious consideration's of the BillBoardService, when the internet was still a garden of unlimited treasures. 
Func_train 
What could cause a func_train entity to disappear? I have a case with my custom progs.dat and a custom level (the last of Gotshun's "Lost Levels" for SoA) where a silver key is supposed to be transported through a door into a cage at the start of the map. However, in QSS the train on which the key is supposed to be transported is gone, allowing you to grab the key directly.

Without my custom progs.dat (using the one embedded into SoA's pak0.pak), it works. However, Mark V runs the map just fine WITH my code, it's just QSS that doesn't like it.

Which source code files could be responsible? My first suspect would be plats.qc, but the only thing I did there was to make platform movement smoother (10 fps -> 100 fps), and reverting those changes didn't fix it. 
I Was Wondering What This Code Is Doing 
Is this a new feature, a fix or what?

In triggers.qc (from SoA):

void() hurt_touch =
{
if (other.takedamage)
{
self.solid = SOLID_NOT;
T_Damage (other, self, self, self.dmg);
self.think = hurt_on;
self.nextthink = time + 1;
// NEW CODE START
if (self.cnt > 0)
{
self.cnt = self.cnt - 1;
if (self.cnt == 0)
{
self.touch = SUB_Null;
self.nextthink = time + 0.1;
self.think = SUB_Remove;
}
}
// NEW CODE END
}
return;
};

void() trigger_hurt =
{
InitTrigger ();
self.touch = hurt_touch;
if (!self.dmg)
self.dmg = 5;
// NEW CODE START
if (self.cnt == 0)
self.cnt = -1;
// NEW CODE END
}; 
NightFright: 
looks like they added a property which will remove the trigger after it was used a certain number of times. If the mapper added "cnt" "5" it would delete itself after 5 uses.

Since unspecified properties default to "0", they convert 0 to -1 in the spawn function. 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.