|
QuakeZone MOD Coding Tutorials
|
|
|
Introduction Pre-requisites Code Notes Exercises |
|
| Introduction |
|
|
This tutorial describes how to create CTF/lithium-style Tech pick-ups. These pick-ups can then be assigned various "powers" - in this example we will be adding multi, homing, blister and accelerater power-ups for rockets. |
| Pre-requisites |
|
|
| Code |
|
|
g_local.h Firstly locate the first line below and insert the next line: #define IT_POWERUP 32 #define IT_TR_TECH 512 // PJT - tech pickupsAdd to the end of structure level_locals_t: float next_techdrop; // PJT - tech pickups - time to next Tech pickup dropAdd the following lines at the indicated location: // PJT - tech pick-ups extern cvar_t *sv_techspawns; // number of Tech pick-ups to spawn extern cvar_t *sv_techlife; // duration of Tech pick-up // PJT <-- end of insert #define world (&g_edicts[0])And again at this location: // PJT - tech types #define TR_TECH_MULTI "Multi Tech" #define TR_TECH_HOMING "Homing Tech" #define TR_TECH_BLISTER "Blister Tech" #define TR_TECH_ACCEL "Accelerator Tech" #define TR_TECH_TYPES 4 // PJT <-- end of insert // // g_monster.c // void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread,Next, add the following prototype at indicated location: // // p_hud.c // void init_hud_rocket_strings(void); // PJT - new hudAnd finally, add at the end of the file: // PJT - tech pickups extern char *tech_types[]; // PJT q_shared.h Add the following line at the indicated position: #define MAX_CONFIGSTRINGS (CS_GENERAL+MAX_GENERAL) #define CS_TR_STRINGS (CS_ITEMS+200) // PJT - tech pickups in p_hud.c g_items.c Near the start of the file at the indicated location:
// PJT - rocket techs
char *tech_types[] =
{
TR_TECH_MULTI,
TR_TECH_HOMING,
TR_TECH_BLISTER,
TR_TECH_ACCEL
} ;
// PJT <-- end of insert
qboolean Pickup_Weapon (edict_t *ent, edict_t *other);
Locate the structure item_list, and add to the bottom as indicated:
// PJT - tech pick-ups
{
"item_multi_tech",
NULL,
NULL,
NULL,
NULL,
"items/pkup.wav",
NULL,
0,
NULL,
/* icon */ "techs/tech1",
TR_TECH_MULTI,
0,
1,
NULL,
IT_TR_TECH,
0,
NULL,
0,
"" },
{
"item_homing_tech",
NULL,
NULL,
NULL,
NULL,
"items/pkup.wav",
NULL,
0,
NULL,
/* icon */ "techs/tech2",
TR_TECH_HOMING,
0,
1,
NULL,
IT_TR_TECH,
0,
NULL,
0,
"" },
{
"item_blister_tech",
NULL,
NULL,
NULL,
NULL,
"items/pkup.wav",
NULL,
0,
NULL,
/* icon */ "techs/tech3",
TR_TECH_BLISTER,
0,
1,
NULL,
IT_TR_TECH,
0,
NULL,
0,
"" },
{
"item_accel_tech",
NULL,
NULL,
NULL,
NULL,
"items/pkup.wav",
NULL,
0,
NULL,
/* icon */ "techs/tech4",
TR_TECH_ACCEL,
0,
1,
NULL,
IT_TR_TECH,
0,
NULL,
0,
"" },
// PJT <-- end of insert
// end of list marker
{NULL}
};
Then, at the end of the file, add the following 2 functions:
// PJT - tech pick-ups
//=========================================================
// Touch function for when Tech-pickup is picked up by other..
//=========================================================
void SpawnTech_Touch(edict_t *tech, edict_t *other, cplane_t *plane, csurface_t *surf)
{
int i, j, quantity, itemno;
gitem_t *item=itemlist;
// don't bother if not active player
if (!G_EntExists(other)) return;
if (!other->client) return; // only clients can pick up
if (other->health < 1) return; // dead people can't pickup
// check player already has pickup
itemno = ITEM_INDEX(FindItem(tech_types[tech->rocket_type - 1]));
if (other->client->pers.inventory[itemno])
return;
// Immediately turn OFF effects
tech->s.effects = 0;
tech->s.sound = 0;
// Play standard item pickup sound...
gi.sound(other, CHAN_ITEM, gi.soundindex("items/protect.wav"), 1.0, ATTN_NORM, 0); // PJT last 0 was 'OFF'
// pick up item
other->client->pers.inventory[itemno] = 1;
other->client->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(FindItem(tech_types[tech->rocket_type - 1])->icon);
other->client->ps.stats[STAT_PICKUP_STRING] = CS_TR_STRINGS + 30 + tech->rocket_type - 1; // ??
other->client->pickup_msg_time = level.time + 3.0;
gi.unlinkentity(tech); // Make tech disappear..
G_FreeEdict (tech); // really make it disappear
}
//=========================================================
// Toss a tech pickup from a random spawnpoint
//=========================================================
void SpawnTech(void)
{
int i,quantity;
float rnum;
gitem_t *item=itemlist;
edict_t *tech;
vec3_t forward, right, up, vrandom;
edict_t *dest;
// if (!deathmatch->value) return; // PJT -commented out - allow techs in coop/sp
// Create backpack entity
tech=G_Spawn();
// Choose random tech type, and give appropriate model
tech->rocket_type = (int)(random()*4)+1;
tech->s.renderfx = 0;
switch (tech->rocket_type)
{
case 1: // multi tech
tech->s.modelindex = gi.modelindex("models/techs/multi/tris.md2");
break;
case 2: // homing tech
tech->s.modelindex = gi.modelindex("models/techs/homing/tris.md2");
break;
case 3: // blister tech
tech->s.modelindex = gi.modelindex("models/techs/blister/tris.md2");
break;
case 4: // accelerator tech
tech->s.modelindex = gi.modelindex("models/techs/accel/tris.md2");
break;
}
// pick random destination
// dest = SelectRandomSpawnPoint(); // SelectRandomDeathmatchSpawnPoint();
// ------------------------------
// Finish making the pack entity
// ------------------------------
tech->owner = world;
tech->movetype = MOVETYPE_TOSS;
tech->takedamage = DAMAGE_NO;
tech->health = 0;
tech->deadflag = DEAD_DEAD; // So not in game..
tech->solid = SOLID_TRIGGER;
// tech->s.modelindex = gi.modelindex("models/objects/rocket/tris.md2");
tech->s.effects = EF_ROTATE; //EF_COLOR_SHELL & EF_ROTATE;
// VectorCopy(dest->s.origin,pack->s.origin);
VectorCopy(level.spawn_points[(int)(random()*level.num_spawn_points)] , tech->s.origin);
tech->s.origin[2] += 32;
VectorSet(tech->mins,-16,-16,-16); // Size of standard Q2 tech pick-up
VectorSet(tech->maxs, 16, 16, 16);
//AngleVectors(dest->s.angles, forward, right, up);
VectorSet(forward, 10, 0, 0);
VectorSet(right, 0, 10, 0);
VectorSet(up, 0, 0, 10);
VectorScale(forward, 20, tech->velocity);
VectorMA(tech->velocity, crandom()*10.0+20.0, up, tech->velocity);
VectorMA(tech->velocity, crandom()*20.0-10.0, right, tech->velocity);
VectorMA(tech->velocity, crandom()*20.0-10.0, forward, tech->velocity); // PJT random direction
VectorClear(tech->avelocity); // No angular rotation
VectorClear(tech->s.angles); // No tilt..
tech->touch = SpawnTech_Touch;
tech->nextthink = level.time + sv_techlife->value; // destroy time.
tech->think = G_FreeEdict; // destroy function
gi.linkentity(tech);
}
// PJT
p_hud.c Add the following procedure to the end of the file:
// PJT - new HUD - define HUD strings
void init_hud_rocket_strings(void)
{
// PJT - tech pick-ups
gi.configstring (CS_TR_STRINGS +30, "Multi Tech");
gi.configstring (CS_TR_STRINGS +31, "Homing Tech");
gi.configstring (CS_TR_STRINGS +32, "Blister Tech");
gi.configstring (CS_TR_STRINGS +33, "Accel Tech");
// PJT
}
// PJT <-- end of insert
g_spawn.c Add the following lines to procedure SP_worldspawn as indicated: // set configstrings for items SetItemNames (); // PJT - set configstrings for team rocket HUD (see p_hud.c) init_hud_rocket_strings(); // PJT - end of insert g_main.c Add the following lines as indicated near the top of the file: edict_t *g_edicts; // PJT - Tech pick-ups cvar_t *sv_techspawns; cvar_t *sv_techlife; // PJT <-- end of insertAdd the following procedure just before G_RunFrame:
// PJT - Tech pick-ups
/*
================
DropTechPickups
Drops the Tech Pick-ups
================
*/
void DropTechPickups (void)
{
int i;
// don't process if not time yet
if (level.time < level.next_techdrop) return;
// else spawn out rockets
for (i=0; i
Finally in G_RunFrame add following lines as indicated:
// PJT - see if Tech Pick-ups need dropping DropTechPickups(); // PJT <-- end of insert // see if needpass needs updated CheckNeedPass (); g_save.c Add the following lines to the end of procedure InitGame:
// PJT - Tech pick-ups
sv_techspawns = gi.cvar("sv_techspawns","5", CVAR_SERVERINFO); // number of Tech pick-ups
sv_techlife = gi.cvar("sv_techlife", "30", CVAR_SERVERINFO); // duration of Tech pick-up
level.next_techdrop = 0.0; // PJT - Tech pick-ups
// PJT
g_cmds.c Add the following lines to procedure Cmd_Give_f as indicated:
if (give_all)
{
for (i=0 ; i
If, for example, you have a command procedure within g_cmds.c to activate homing
missiles, but only if the player has a homing Tech, add the following lines to the
start of that procedure:
// PJT - tech pickups
if (!ent->client->pers.inventory[ITEM_INDEX(FindItem(TR_TECH_HOMING))])
{
gi.cprintf (ent, PRINT_HIGH, "Homing Tech required.\n");
return;
}
// PJT
cmds.cfg Add the following commands to your config file: set sv_techspawns 5 set sv_techlife 30 |
| Notes |
|
|
g_local.h
q_shared.h
g_items.c
p_hud.c
g_spawn.c
g_main.c
g_save.c
g_cmds.c
cmds.cfg
|
| Exercises |
|
|