/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // g_weapon.c #include "g_local.h" #include "g_gametypes.h" static qboolean is_quad; static qbyte is_silenced; void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) { vec3_t _distance; VectorCopy (distance, _distance); if (client->pers.hand == LEFT_HANDED) _distance[1] *= -1; else if (client->pers.hand == CENTER_HANDED) _distance[1] = 0; G_ProjectSource (point, _distance, forward, right, result); } //====================================================================== // // NEW WEAPON SYSTEM // //====================================================================== //=============== //Pickup_Weapon //=============== qboolean Pickup_Weapon (edict_t *ent, edict_t *other) { int ammo_tag; if ( ( (dmflags->integer & DF_WEAPONS_STAY) //|| game.gametype == GAMTYPE_COOP ) && other->r.client->inventory[ent->item->tag]) { if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ) ) return qfalse; // leave the weapon for others to pickup } other->r.client->inventory[ent->item->tag]++; if (!(ent->spawnflags & DROPPED_ITEM) ) { // give them some ammo with it ammo_tag = ent->item->weakammo_tag; if( ammo_tag ) { //jal: prevent null ammo crashes if ( dmflags->integer & DF_INFINITE_AMMO ) Add_Ammo (other, game.items[ammo_tag], 1000, qtrue); else Add_Ammo (other, game.items[ammo_tag], game.items[ammo_tag]->quantity, qtrue); } if (! (ent->spawnflags & DROPPED_PLAYER_ITEM) ) { if (G_Gametype_CanRespawnItem(ent->item))//newgametypes { if (dmflags->integer & DF_WEAPONS_STAY) ent->flags |= FL_RESPAWN; else SetRespawn (ent, G_Gametype_RespawnTimeForItem(ent->item)); } } } else { //it's a dropped weapon ammo_tag = ent->item->weakammo_tag; if( ent->count && ammo_tag ) { if ( dmflags->integer & DF_INFINITE_AMMO ) Add_Ammo (other, game.items[ammo_tag], 1000, qtrue); else Add_Ammo (other, game.items[ammo_tag], ent->count, qtrue); } } //if using gunblade or no weapon, change to the new one at picking up if( (!other->s.weapon || other->s.weapon == WEAP_GUNBLADE) && other->r.client->inventory[ent->item->tag] == 1 ) other->r.client->latched_weapon = ent->item->tag; return qtrue; } //=============== //ChangeWeapon // //The old weapon has been dropped all the way, so make the new one //current //=============== void ChangeWeapon (edict_t *ent) { gclient_t *client = ent->r.client; weapon_info_t *weaponinfo; //invalid if( client->latched_weapon < 0 || client->latched_weapon >= WEAP_TOTAL ) { return; } ent->s.weapon = client->latched_weapon; client->latched_weapon = -1; client->weapon_powered = 0; weaponinfo = &g_weaponInfos[ent->s.weapon]; if (ent->s.weapon && weaponinfo->firedef->usage_count) client->ammo_index = weaponinfo->firedef->ammo_id; else client->ammo_index = 0; if (ent->s.weapon && weaponinfo->firedef_weak->usage_count) client->ammo_weak_index = weaponinfo->firedef_weak->ammo_id; else client->ammo_weak_index = 0; if (!ent->s.weapon) { return; } // change selected item client->selected_item = client->ps.stats[STAT_SELECTED_ITEM] = ent->s.weapon; //remove the hold priority from weaponOUT and //set a standard animation for BASIC ent->pmAnim.anim_priority[UPPER] = ANIM_BASIC; ent->pmAnim.anim[UPPER] = TORSO_STAND; //Send the weaponIN animation and sound style through EVENTs G_AddEvent (ent, EV_WEAPONUP, 1, qtrue); } int SelectBestWeapon( gclient_t *client, int ignore_weapon ) { int weap, weap_chosen; weapon_info_t *weaponinfo; //find with strong ammos for( weap = WEAP_TOTAL-1; weap > WEAP_NONE; weap-- ) { if( ignore_weapon > 0 && weap == ignore_weapon ) continue; if( !client->inventory[weap] ) continue; weaponinfo = &g_weaponInfos[weap]; if( !weaponinfo->firedef ) continue; // wsw : pb : fixed noammo autoswitch to gunblade if( weaponinfo->weapon_id == WEAP_GUNBLADE ) continue; if( !weaponinfo->firedef->usage_count || client->inventory[weaponinfo->firedef->ammo_id] >= weaponinfo->firedef->usage_count ) { weap_chosen = weap; goto found; } } //repeat find with weak ammos for( weap = WEAP_TOTAL-1; weap > WEAP_NONE; weap-- ) { if( ignore_weapon > 0 && weap == ignore_weapon ) continue; if( !client->inventory[weap] ) continue; weaponinfo = &g_weaponInfos[weap]; if( !weaponinfo->firedef_weak ) continue; if( !weaponinfo->firedef_weak->usage_count || client->inventory[weaponinfo->firedef_weak->ammo_id] >= weaponinfo->firedef_weak->usage_count ) { weap_chosen = weap; goto found; } } // didnt found any weapon so use strong gunblade weap_chosen = WEAP_GUNBLADE; found: return weap_chosen; } //================= //NoAmmoWeaponChange //================= void NoAmmoWeaponChange( edict_t *ent ) { if( ent->r.client ) G_AddPlayerStateEvent( ent->r.client, PSEV_NOAMMO, ent->s.weapon ); } //================ //Use_Weapon // //Make the weapon ready if there is ammo //================ void Use_Weapon( edict_t *ent, gitem_t *item ) { int ammocount, weakammocount; weapon_info_t *weaponinfo; //invalid weapon item if( item->tag < 0 || item->tag >= WEAP_TOTAL ) return; // see if we're already changing to it if ( ent->r.client->latched_weapon == item->tag ) return; // see if we're already using it and not changing away from it if( item == game.items[ent->s.weapon] && ent->r.client->latched_weapon == -1 ) return; weaponinfo = &g_weaponInfos[item->tag]; if( !g_select_empty->integer && !(item->type & IT_AMMO) ) { if( weaponinfo->firedef->usage_count ) { if( weaponinfo->firedef->ammo_id ) ammocount = ent->r.client->inventory[weaponinfo->firedef->ammo_id]; else ammocount = weaponinfo->firedef->usage_count; // can change weapon } else ammocount = 1; // can change weapon if( weaponinfo->firedef_weak->usage_count ) { if( weaponinfo->firedef_weak->ammo_id ) weakammocount = ent->r.client->inventory[weaponinfo->firedef_weak->ammo_id]; else weakammocount = weaponinfo->firedef_weak->usage_count; // can change weapon }else weakammocount = 1; // can change weapon if (!ammocount && !weakammocount) { return; } if (ammocount < weaponinfo->firedef->usage_count && weakammocount < weaponinfo->firedef_weak->usage_count) { return; } } // change to this weapon when down ent->r.client->latched_weapon = item->tag; //if we don't have any weapon, no need to wait to be down if( !ent->s.weapon ) ChangeWeapon( ent ); } //================ //Drop_Weapon //================ void Drop_Weapon( edict_t *ent, gitem_t *item ) { int otherweapon; edict_t *drop; int ammodrop = 0; if (dmflags->integer & DF_WEAPONS_STAY) return; if( item->tag < 1 || item->tag >= WEAP_TOTAL ) { G_PrintMsg( ent, "Can't drop unknown weapon\n" ); return; } // see if we're already using it if( ( item->tag == ent->s.weapon || item->tag == ent->r.client->latched_weapon ) && ent->r.client->inventory[item->tag] == 1 ) { if( item->tag != WEAP_GUNBLADE ) { // put the weapon down otherweapon = SelectBestWeapon( ent->r.client, item->tag ); Use_Weapon( ent, game.items[otherweapon] ); ChangeWeapon(ent); } else { G_PrintMsg( ent, "Can't drop current weapon\n" ); return; } } // if we have weak_ammo for this weapon add some to the weapon drop // jalfixme: shouldn't be the same value for every weapon if( ent->r.client->inventory[game.items[item->tag]->weakammo_tag] > 5 ) { ammodrop = 5; } drop = Drop_Item( ent, item ); if( drop ) { ent->r.client->inventory[game.items[item->tag]->weakammo_tag] -= ammodrop; drop->count = ammodrop; drop->spawnflags |= DROPPED_PLAYER_ITEM; ent->r.client->inventory[item->tag]--; if( GS_Gametype_IsTeamBased(game.gametype) ) { if( ent->r.client->inventory[item->tag] > 1 ) ent->r.client->inventory[item->tag] = 1; } } } qboolean Check_BladeAttack( edict_t *ent ); //================= //Weapon_PowerFracToAmmoUsage //Find the ammout of ammo we will consume, being it a powered weapon or not //================= static int Weapon_PowerFracToAmmoUsage( gclient_t *client, firedef_t *firedef ) { float ammocount; float power; if( !firedef || !firedef->usage_count || !firedef->ammo_id ) return 0; if( firedef->powering_time <= game.framemsec ) return firedef->usage_count; power = (float)client->weapon_powered / (float)firedef->powering_time; if( power > 1.0f ) power = 1.0f; ammocount = power * (float)firedef->usage_count; // fix if off limits if( ammocount < 1.0f ) ammocount = 1.0f; if( client->inventory[firedef->ammo_id] ) { if( ammocount > client->inventory[firedef->ammo_id] ) ammocount = client->inventory[firedef->ammo_id]; } return (int)ammocount; } //================= //Weapon_GenericFrametime //================= static void Weapon_GenericFrametime( edict_t *ent, firedef_t *firedef ) { gclient_t *client = ent->r.client; if( ent->deadflag ) return; if( client->ps.pmove.stats[PM_STAT_SLOW] > 0 ) client->weapon_nexttime += (int)G_FRAMETIME/5.0*4.0; // pretty hax // remove reload wait if( level.timemsec >= client->weapon_nexttime && client->weaponstate == WEAPON_RELOADING ) { if( firedef->cooldown_time ) { client->weaponstate = WEAPON_COOLDOWN; client->weapon_nexttime = level.timemsec + firedef->cooldown_time; } else client->weaponstate = WEAPON_READY; } // remove cooldown wait if( client->weaponstate == WEAPON_COOLDOWN && level.timemsec >= client->weapon_nexttime ) client->weaponstate = WEAPON_READY; // change weapon init if( client->latched_weapon != -1 && (client->weaponstate == WEAPON_READY || client->weaponstate == WEAPON_ACTIVATING) && level.timemsec >= client->weapon_nexttime) { client->weaponstate = WEAPON_DROPPING; client->weapon_nexttime = level.timemsec + firedef->weapondown_time; //weapdown animation ent->pmAnim.anim_priority[UPPER] = ANIM_HOLD; ent->pmAnim.anim[UPPER] = TORSO_FLIPOUT; } //change weapon do if (client->weaponstate == WEAPON_DROPPING && level.timemsec >= client->weapon_nexttime) { ChangeWeapon (ent); client->weaponstate = WEAPON_ACTIVATING; client->weapon_nexttime = level.timemsec + firedef->weaponup_time; return; //FIXME: this one MUST return to refresh the fire function pointers after weapon change } // change weapon changed if( client->weaponstate == WEAPON_ACTIVATING && level.timemsec >= client->weapon_nexttime ) { client->weaponstate = WEAPON_READY; client->weapon_nexttime = level.timemsec; //no delay count } //check for firing if( level.timemsec >= client->weapon_nexttime && (client->weaponstate == WEAPON_READY || client->weaponstate == WEAPON_POWERING) ) { if( match.state == MATCH_STATE_COUNTDOWN ) { // don't allow firing while in countdown client->weaponstate = WEAPON_READY; return; } // player is pressing fire if( (client->latched_buttons|client->buttons) & BUTTON_ATTACK ) { client->latched_buttons &= ~BUTTON_ATTACK; //client->buttons &= ~BUTTON_ATTACK; if( client->weaponstate != WEAPON_POWERING ) { client->weapon_powered = 0; } client->weaponstate = WEAPON_POWERING; client->weapon_powered += G_FRAMETIME; // still powering up if( client->weapon_powered < firedef->powering_time ) { if( Weapon_PowerFracToAmmoUsage( client, firedef ) < client->inventory[firedef->ammo_id] ) return; } } //not pressing fire anymore, or powering time reached. if weapon is powered, fire it if( client->weaponstate == WEAPON_POWERING ) { client->weaponstate = WEAPON_FIRING; } } // must fire a projectile? (never leave from here with WEAPON_FIRING state, or having power) // note: power is removed after calling the fire function, so it's value can be used in it if( client->weaponstate == WEAPON_FIRING ) { if( match.state == MATCH_STATE_COUNTDOWN ) { // don't allow firing while in countdown client->weaponstate = WEAPON_READY; } else if( firedef->ammo_id && (!firedef->usage_count || client->inventory[firedef->ammo_id] >= Weapon_PowerFracToAmmoUsage( client, firedef ) ) ) { if( client->quad_timeout > level.timemsec ) G_Sound (ent, CHAN_ITEM, trap_SoundIndex(S_QUAD_FIRE), 1, ATTN_NORM); if( firedef->fire ) { firedef->fire(ent); client->resp.accuracy_shots[firedef->ammo_id-AMMO_CELLS] += firedef->projectile_count; } client->weaponstate = WEAPON_RELOADING; client->weapon_nexttime = level.timemsec + firedef->reload_time; } else { client->weaponstate = WEAPON_READY; NoAmmoWeaponChange ( ent ); } client->weapon_powered = 0; //(remove power) } } firedef_t *Player_GetCurrentWeaponFiredef( edict_t *ent ) { firedef_t *firedef = NULL; if( ent->deadflag ) return NULL; if( ent->s.weapon < 0 || ent->s.weapon >= WEAP_TOTAL ) //invalid weapon return NULL; //find out our current fire mode if( ent->r.client->inventory[g_weaponInfos[ent->s.weapon].firedef->ammo_id] >= Weapon_PowerFracToAmmoUsage( ent->r.client, g_weaponInfos[ent->s.weapon].firedef ) ) firedef = g_weaponInfos[ent->s.weapon].firedef; else firedef = g_weaponInfos[ent->s.weapon].firedef_weak; return firedef; } //================= //Weapon_Generic // Generic think for all weapons. It chooses the weapon firedef and calls the states machine //================= void Weapon_Generic( edict_t *ent ) { if( ent->deadflag ) return; if( ent->s.weapon < 0 || ent->s.weapon >= WEAP_TOTAL ) //invalid weapon return; if( Check_BladeAttack( ent ) ) return; Weapon_GenericFrametime( ent, Player_GetCurrentWeaponFiredef(ent) ); } //================= //Weapon_Generic_Fire // Kurim : Muzzleflash, Playernoise and ProjectSource //================= void Weapon_Generic_Fire( edict_t *ent, int firemode, vec3_t start, vec3_t forward ) { vec3_t offset; vec3_t right; AngleVectors (ent->r.client->v_angle, forward, right, NULL); G_AddEvent (ent, EV_MUZZLEFLASH, firemode, qtrue); VectorSet(offset, 0,0, ent->viewheight); P_ProjectSource (ent->r.client, ent->s.origin, offset, forward, right, start); } //================= //Think_Weapon // //Called by ClientBeginServerFrame and ClientThink //================= void Think_Weapon( edict_t *ent ) { if( ent->s.weapon > 0 && (game.items[ent->s.weapon]->type & IT_WEAPON) ) { is_quad = (ent->r.client->quad_timeout > level.timemsec); if( ent->r.client->silencer_shots ) is_silenced = MZ_SILENCED; else is_silenced = 0; //jal: ok, this is stupid. Every weapon is calling // weapon_generic, so stop using a function pointer for it Weapon_Generic(ent); } } /* ====================================================================== WEAPONS ====================================================================== */ /* ====================================================================== GUNBLADE ====================================================================== */ //=============== //Check_BladeAttack //=============== qboolean Check_BladeAttack( edict_t *ent ) { edict_t *other = NULL; vec3_t start, forward, offset, right, end; trace_t tr; gclient_t *client = ent->r.client; firedef_t *firedef; int damage; int range; int knockback; // wsw: pb disable collision in race mode if( game.gametype == GAMETYPE_RACE || match.state == MATCH_STATE_COUNTDOWN ) return qfalse; if( ent->s.weapon != WEAP_GUNBLADE ) return qfalse; firedef = g_weaponInfos[ent->s.weapon].firedef_weak; damage = firedef->damage; range = firedef->timeout; knockback = firedef->knockback; if( client->weaponstate != WEAPON_READY ) return qfalse; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } AngleVectors( ent->r.client->v_angle, forward, right, NULL ); VectorSet( offset, 0, 0, ent->viewheight ); P_ProjectSource( ent->r.client, ent->s.origin, offset, forward, right, start ); VectorMA( start, range, forward, end ); trap_Trace( &tr, start, NULL, NULL, end, ent, MASK_SHOT ); if( tr.fraction < 1.0 ) { other = &game.edicts[tr.ent]; if( other->r.client ) if( other->s.team != ent->s.team || ent->s.team == TEAM_PLAYERS ) { client->weaponstate = WEAPON_RELOADING; client->weapon_nexttime = level.timemsec + firedef->reload_time; client->resp.accuracy_shots[AMMO_WEAK_GUNBLADE-AMMO_CELLS]++; G_AddEvent( ent, EV_MUZZLEFLASH, FIRE_MODE_WEAK, qtrue ); W_Fire_Blade( ent, range, start, forward, firedef->damage, firedef->knockback, MOD_GUNBLADE_W ); return qtrue; } } return qfalse; } //================= //Weapon_Fire_Gunblade_Weak //================= void Weapon_Fire_Gunblade_Weak( edict_t *ent ) { vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int damage = firedef->damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Blade( ent, firedef->timeout, start, forward, damage, knockback, MOD_GUNBLADE_W ); } //================= //Weapon_Fire_Gunblade_Strong //================= void Weapon_Fire_Gunblade_Strong( edict_t *ent ) { gclient_t *client = ent->r.client; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; vec3_t start, forward; float power; int damage; int knockback; int radius; int speed = firedef->speed; //shut down power if not waited enough (works) //if( ent->r.client->weapon_powered < 5 * G_FRAMETIME ) // return; power = (float)ent->r.client->weapon_powered / (float)firedef->powering_time; if( power > 1.0f ) power = 1.0f; if( power < 0.1f ) power = 0.1f; damage = firedef->damage * power; knockback = firedef->knockback * power; radius = firedef->splash_radius * power; if( damage < firedef->splash_min_damage ) damage = firedef->splash_min_damage; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } if( ent->waterlevel == 3 ) { damage = (int)( damage * 0.8f ); knockback = (int)(knockback * 0.6f ); speed = (int)(speed * 0.6f ); } Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_GunbladeBlast( ent, start, forward, damage, knockback, firedef->splash_min_damage, radius, speed, firedef->timeout, MOD_GUNBLADE_S ); // ammo usage if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) { client->inventory[firedef->ammo_id] -= Weapon_PowerFracToAmmoUsage( client, firedef ); } } /* ====================================================================== SHOCKWAVE ====================================================================== */ void Weapon_Fire_Shockwave_Strong( edict_t *ent ) { vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; gclient_t *client = ent->r.client; Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_Shockwave( ent, start, forward, firedef->speed, firedef->splash_radius, firedef->timeout ); // ammo usage if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) { client->inventory[firedef->ammo_id] -= Weapon_PowerFracToAmmoUsage( client, firedef ); } } void Weapon_Fire_Shockwave_Weak( edict_t *ent ) { vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; gclient_t *client = ent->r.client; Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Shockwave( ent, start, forward, firedef->speed, firedef->splash_radius, firedef->timeout ); // ammo usage if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) { client->inventory[firedef->ammo_id] -= Weapon_PowerFracToAmmoUsage( client, firedef ); } } /* ====================================================================== RIOTGUN ====================================================================== */ //================= //Weapon_Fire_Riotgun_Strong //================= void Weapon_Fire_Riotgun_Strong( edict_t *ent ) { vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; int damage = firedef->damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } if( ent->waterlevel == 3 ) damage = (int)(damage * 0.8); Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_Riotgun( ent, start, forward, damage, knockback, firedef->h_spread, firedef->v_spread, firedef->projectile_count, DAMAGE_SHELL, MOD_RIOTGUN_S ); if( firedef->ammo_id && firedef->usage_count && !(dmflags->integer & DF_INFINITE_AMMO ) ) ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count; } //================= //Weapon_Fire_Riotgun_Weak //================= void Weapon_Fire_Riotgun_Weak( edict_t *ent ) { vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int damage = firedef->damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } if( ent->waterlevel == 3 ) damage = (int)(damage * 0.8); Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Riotgun( ent, start, forward, damage, knockback, firedef->h_spread, firedef->v_spread, firedef->projectile_count, DAMAGE_SHELL, MOD_RIOTGUN_W ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count; } /* ====================================================================== GRENADELAUNCHER ====================================================================== */ //================= //Weapon_GrenadeLauncher_Fire_Strong //================= void Weapon_GrenadeLauncher_Fire_Strong( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t forward; vec3_t start; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; int damage = firedef->damage; int mindmg = firedef->splash_min_damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; mindmg *= 2; } Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_Grenade( ent, start, forward, firedef->speed, damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_GRENADE_S ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } //================= //Weapon_GrenadeLauncher_Fire_Weak //================= void Weapon_GrenadeLauncher_Fire_Weak( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t forward; vec3_t start; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int damage = firedef->damage; int mindmg = firedef->splash_min_damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; mindmg *= 2; } Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Grenade( ent, start, forward, firedef->speed, damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_GRENADE_W ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } /* ====================================================================== ROCKETLAUNCHER ====================================================================== */ //================= //Weapon_RocketLauncher_Fire_Strong //================= void Weapon_RocketLauncher_Fire_Strong( edict_t *ent ) { vec3_t start; vec3_t forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; int mindmg = firedef->splash_min_damage; int damage = firedef->damage; int speed = firedef->speed; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; if( game.gametype == GAMETYPE_MIDAIR ) { mindmg = 500; speed *= 1.3; } } //slow down under water if( ent->waterlevel == 3 ) speed = (int)speed*0.5; Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_Rocket( ent, start, forward, speed, damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_ROCKET_S ); if( game.gametype == GAMETYPE_MIDAIR ) return; if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count; } //================= //Weapon_RocketLauncher_Fire_Weak //================= void Weapon_RocketLauncher_Fire_Weak( edict_t *ent ) { vec3_t start; vec3_t forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int mindmg = firedef->splash_min_damage; int damage = firedef->damage; int speed = firedef->speed; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } //slow down under water if ( ent->waterlevel == 3 ) speed = (int)speed*0.5; Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Rocket( ent, start, forward, speed, damage, knockback, mindmg, firedef->splash_radius, firedef->timeout, MOD_ROCKET_W ); if( game.gametype == GAMETYPE_MIDAIR ) return; if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) ent->r.client->inventory[firedef->ammo_id] -= firedef->usage_count; } /* ====================================================================== PLASMAGUN ====================================================================== */ //================= //Weapon_Fire_Plasmagun_Strong //================= void Weapon_Fire_Plasmagun_Strong( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; int damage = firedef->damage; int mindmg = firedef->splash_min_damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; mindmg *= 2; } Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_Plasma( ent, start, forward, damage, knockback, mindmg, firedef->splash_radius, firedef->speed, firedef->timeout, MOD_PLASMA_S ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } //================= //Weapon_Fire_Plasmagun_Weak //================= void Weapon_Fire_Plasmagun_Weak( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int damage = firedef->damage; int mindmg = firedef->splash_min_damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; mindmg *= 2; } Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Plasma( ent, start, forward, damage, knockback, mindmg, firedef->splash_radius, firedef->speed, firedef->timeout, MOD_PLASMA_W ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } /* ====================================================================== LASERGUN ====================================================================== */ //================= //Weapon_Fire_Lasergun_Strong //================= void Weapon_Fire_Lasergun_Strong( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; int damage = firedef->damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } // don't use Weapon_Generic_Fire because we don't want to send muzzleflash always { vec3_t offset; vec3_t right; AngleVectors (ent->r.client->v_angle, forward, right, NULL); VectorSet(offset, 0,0, ent->viewheight); P_ProjectSource (ent->r.client, ent->s.origin, offset, forward, right, start); } W_Fire_Lasergun( ent, start, forward, damage, knockback, firedef->timeout, DAMAGE_ENERGY, MOD_LASERGUN_S ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } //================= //Weapon_Fire_Lasergun_Weak //================= void Weapon_Fire_Lasergun_Weak( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int damage = firedef->damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } // don't use Weapon_Generic_Fire because we don't want to send muzzleflash always { vec3_t offset; vec3_t right; AngleVectors (ent->r.client->v_angle, forward, right, NULL); VectorSet(offset, 0,0, ent->viewheight); P_ProjectSource (ent->r.client, ent->s.origin, offset, forward, right, start); } // find the endpoint into the ones in the backup trail { vec3_t end; int backtime = 0; int framenum; int backframes = (int)(backtime/game.framemsec); if( backframes > LASERGUN_WEAK_TRAIL_MASK ) backframes = LASERGUN_WEAK_TRAIL_MASK; framenum = level.framenum - backframes; if( framenum < 0 ) framenum = 0; VectorCopy( client->lasergunTrail[framenum & LASERGUN_WEAK_TRAIL_MASK], end ); W_Fire_Lasergun_Weak( ent, start, end, forward, damage, knockback, firedef->timeout, DAMAGE_ENERGY, MOD_LASERGUN_W ); } //W_Fire_Lasergun( ent, start, forward, damage, knockback, firedef->timeout, DAMAGE_ENERGY, MOD_LASERGUN_W ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } /* ====================================================================== ELECTROBOLT ====================================================================== */ //================= //Weapon_Fire_Electrobolt_Strong //================= void Weapon_Fire_Electrobolt_Strong( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef; int damage = firedef->damage; int knockback = firedef->knockback; int dmgflags = DAMAGE_ENERGY; if( g_instagib->integer ) { damage = 200; dmgflags |= DAMAGE_NO_ARMOR; } else if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } Weapon_Generic_Fire( ent, FIRE_MODE_STRONG, start, forward ); W_Fire_Electrobolt_Strong( ent, start, forward, firedef->speed, damage, knockback, firedef->timeout, dmgflags, MOD_ELECTROBOLT_S ); if( !g_instagib->integer ) { if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } } //================= //Weapon_Fire_Electrobolt_Weak //================= void Weapon_Fire_Electrobolt_Weak( edict_t *ent ) { gclient_t *client = ent->r.client; vec3_t start, forward; firedef_t *firedef = g_weaponInfos[ent->s.weapon].firedef_weak; int damage = firedef->damage; int knockback = firedef->knockback; if( is_quad ) { damage *= QUAD_DAMAGE_SCALE; knockback *= QUAD_KNOCKBACK_SCALE; } Weapon_Generic_Fire( ent, FIRE_MODE_WEAK, start, forward ); W_Fire_Electrobolt_Weak( ent, start, forward, firedef->speed, damage, knockback, firedef->timeout, DAMAGE_BOLT_WEAK, MOD_ELECTROBOLT_W ); if( firedef->ammo_id && firedef->usage_count && !( dmflags->integer & DF_INFINITE_AMMO ) ) client->inventory[firedef->ammo_id] -= firedef->usage_count; } //====================================================== // // WEAPON DEFS // //====================================================== weapon_info_t g_weaponInfos[WEAP_TOTAL]; #define INSTANT 0 // WEAP_NONE firedef_t noweaponFireDef = { "No Weapon", // weapon name NULL, // fire func 0, // fire mode AMMO_NONE, // ammo tag 0, // ammo usage per shot 0, // projectiles fired each shot //timings (in msecs) 100, // weapon up frametime 100, // weapon down frametime 0, // reload frametime 0, // cooldown frametime 0, // max powering up time 0, // projectile timeout //damages 0, // damage 0, // knockback 0, // splash radius 0, // splash minimum damage //projectile def 0, // speed 0, // h spread (I added a small spread while using the replacement gun) 0, // v spread //ammo 0, // pickup amount 0 // max amount }; firedef_t ammoFireDefs[] = { // WEAP_GUNBLADE (strong) (the gun) { "Gunblade", // weapon name Weapon_Fire_Gunblade_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_CELLS, // ammo tag 20, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 250, // reload frametime 0, // cooldown frametime 2000, // max powering up time 5000, // projectile timeout //damages 60, // damage 60, // knockback 80, // splash radius 8, // splash minimum damage //projectile def 3000, // speed 0, // h spread 0, // v spread //ammo 10, // pickup amount 100 // max amount }, // WEAP_GUNBLADE (weak) (the blade) { "Gunblade", Weapon_Fire_Gunblade_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_GUNBLADE, // ammo tag 0, // ammo usage per shot 0, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 500, // reload frametime 0, // cooldown frametime 0, // max powering up time 64, // projectile timeout / projectile range for instant weapons //damages 50, // damage 100, // knockback 0, // splash radius 0, // splash minimum damage //projectile def 0, // speed 0, // h spread 0, // v spread //ammo 0, // pickup amount 0 // max amount }, // WEAP_SHOCKWAVE (strong) { "Shockwave", Weapon_Fire_Shockwave_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_WAVES, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 100, // weapon up frametime 100, // weapon down frametime 500, // reload frametime 0, // cooldown frametime 0, // max powering up time 0, // projectile timeout / projectile range for instant weapons //damages 5, // damage 0, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 0, // h spread 0, // v spread //ammo 5, // pickup amount 5 // max amount }, // WEAP_SHOCKWAVE (weak) { "Shockwave", Weapon_Fire_Shockwave_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_WAVES, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 100, // weapon up frametime 100, // weapon down frametime 500, // reload frametime 0, // cooldown frametime 0, // max powering up time 0, // projectile timeout / projectile range for instant weapons //damages 5, // damage 0, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 0, // h spread 0, // v spread //ammo 10, // pickup amount 10 // max amount }, // WEAP_RIOTGUN (strong) { "Riotgun", Weapon_Fire_Riotgun_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_SHELLS, // ammo tag 1, // ammo usage per shot 38, //38*3=114 // projectiles fired each shot //timings (in msecs) 100, // weapon up frametime 100, // weapon down frametime 1000, // reload frametime 0, // cooldown frametime 0, // max powering up time 8192, // projectile timeout / projectile range for instant weapons //damages 3, // damage 3, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 900, // h spread 900, // v spread //ammo 5, // pickup amount 10 // max amount }, // WEAP_RIOTGUN (weak) { "Riotgun", Weapon_Fire_Riotgun_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_SHELLS, // ammo tag 1, // ammo usage per shot 26, //26*3=78 // projectiles fired each shot //timings (in msecs) 100, // weapon up frametime 100, // weapon down frametime 1000, // reload frametime 0, // cooldown frametime 0, // max powering up time 8192, // projectile timeout / projectile range for instant weapons //damages 3, // damage 3, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 900, // h spread 900, // v spread //ammo 10, // pickup amount 15 // max amount }, // WEAP_GRENADELAUNCHER (strong) { "Grenade Launcher", Weapon_GrenadeLauncher_Fire_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_GRENADES, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 700, // reload frametime 0, // cooldown frametime 0, // max powering up time 2000, // projectile timeout //damages 110, // damage 120, // knockback 150, // splash radius 20, // splash minimum damage //projectile def 800, // speed 0, // h spread 0, // v spread //ammo 5, // pickup amount 10 // max amount }, // WEAP_GRENADELAUNCHER (weak) { "Grenade Launcher", Weapon_GrenadeLauncher_Fire_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_GRENADES, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 800, // reload frametime 0, // cooldown frametime 0, // max powering up time 2000, // projectile timeout //damages 100, // damage 80, // knockback 150, // splash radius 5, // splash minimum damage //projectile def 800, // speed 0, // h spread 0, // v spread //ammo 10, // pickup amount 15 // max amount }, // WEAP_ROCKETLAUNCHER (strong) { "Rocket Launcher", Weapon_RocketLauncher_Fire_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_ROCKETS, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 800, // reload frametime 0, // cooldown frametime 0, // max powering up time 10000, // projectile timeout //damages 105, // damage 90, // knockback 120, // splash radius 20, // splash minimum damage //projectile def 1000, // speed 0, // h spread 0, // v spread //ammo 5, // pickup amount 10 // max amount }, // WEAP_ROCKETLAUNCHER (weak) { "Rocket Launcher", Weapon_RocketLauncher_Fire_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_ROCKETS, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 800, // reload frametime 0, // cooldown frametime 0, // max powering up time 10000, // projectile timeout //damages 100, // damage 80, // knockback 105, // splash radius 10, // splash minimum damage //projectile def 1000, // speed 0, // h spread 0, // v spread //ammo 10, // pickup amount 15 // max amount }, // WEAP_PLASMAGUN (strong) { "Plasmagun", Weapon_Fire_Plasmagun_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_PLASMA, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 140, // reload frametime 0, // cooldown frametime 0, // max powering up time 5000, // projectile timeout //damages 23, // damage 24, // knockback 40, // splash radius 5, // splash minimum damage //projectile def 1900, // speed 0, // h spread 0, // v spread //ammo 25, // pickup amount 50 // max amount }, // WEAP_PLASMAGUN (weak) { "Plasmagun", Weapon_Fire_Plasmagun_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_PLASMA, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 140, // reload frametime 0, // cooldown frametime 0, // max powering up time 5000, // projectile timeout //damages 17, // damage 15, // knockback 20, // splash radius 1, // splash minimum damage //projectile def 1900, // speed 0, // h spread 0, // v spread //ammo 50, // pickup amount 75 // max amount }, // WEAP_ELECTROBOLT (strong) { "Electrobolt", Weapon_Fire_Electrobolt_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_BOLTS, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 1250, // reload frametime 0, // cooldown frametime 0, // max powering up time 4096, // projectile timeout / projectile range for instant weapons //damages 100, // damage 100, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 0, // h spread 0, // v spread //ammo 5, // pickup amount 5 // max amount }, // WEAP_ELECTROBOLT (weak) { "Electrobolt", Weapon_Fire_Electrobolt_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_BOLTS, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 1250, // reload frametime 0, // cooldown frametime 0, // max powering up time 5000, // projectile timeout //damages 100, // damage 100, // knockback 0, // splash radius 0, // splash minimum damage //projectile def 5000, // speed 0, // h spread 0, // v spread //ammo 5, // pickup amount 10 // max amount }, // WEAP_LASERGUN (strong) { "LaserGun", Weapon_Fire_Lasergun_Strong, // fire func FIRE_MODE_STRONG, // fire mode AMMO_LASERS, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 50, // reload frametime 0, // cooldown frametime 0, // max powering up time 532, // projectile timeout / projectile range for instant weapons //damages 8, // damage 16, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 0, // h spread 0, //ammo 40, // pickup amount 80 // max amount }, // WEAP_LASERGUN (weak) { "LaserGun", Weapon_Fire_Lasergun_Weak, // fire func FIRE_MODE_WEAK, // fire mode AMMO_WEAK_LASERS, // ammo tag 1, // ammo usage per shot 1, // projectiles fired each shot //timings (in msecs) 50, // weapon up frametime 50, // weapon down frametime 50, // reload frametime 0, // cooldown frametime 0, // max powering up time 532, // projectile timeout / projectile range for instant weapons //damages 8, // damage 16, // knockback 0, // splash radius 0, // splash minimum damage //projectile def INSTANT, // speed 0, // h spread 0, // v spread //ammo 80, // pickup amount 120 // max amount }, {NULL} }; //========================================== // // //========================================== qbyte *G_LoadWeaponDefFile( char *defname ) { int length, filenum; qbyte *data; char filename[MAX_QPATH]; Q_snprintfz( filename, sizeof(filename), "weapondefs/%s.def", defname ); length = trap_FS_FOpenFile( filename, &filenum, FS_READ ); if (length == -1) { G_Printf ("Couldn't find script: %s.\n", filename); return NULL; } if( !length ) { G_Printf ("Found empty script: %s.\n", filename); trap_FS_FCloseFile( filenum ); return NULL; } //load the script data into memory data = G_Malloc( length + 1 ); trap_FS_Read( data, length, filenum ); trap_FS_FCloseFile( filenum ); if( !data[0] ) { G_Printf ("Found empty script: %s.\n", filename); return NULL; } return data; } #ifdef ALLOW_WEAPONDEFS_READED_FROM_DISK #define WEAPONDEF_NUMPARMS 17 qboolean G_ParseFiredefFile( qbyte *buf, firedef_t *firedef ) { char *ptr, *token; int count = 0; int parm[WEAPONDEF_NUMPARMS]; // jal: this is quite ugly. ptr = ( char * )buf; while ( ptr ) { token = COM_ParseExt ( &ptr, qtrue ); if( !token ) break; if( !token[0] ) continue; //ignore spacing tokens if( !Q_stricmp( token, "," ) || !Q_stricmp( token, "{" ) || !Q_stricmp( token, "}" ) ) continue; //some token sanity checks if( token[strlen(token)-1] == ',' ) token[strlen(token)-1] = 0; if( token[strlen(token)-1] == '}' ) token[strlen(token)-1] = 0; //(I don't fix these ones, but show the error) if( token[0] == ',' ) { G_Printf( "ERROR in script. Comma must be followed by space or newline\n" ); return qfalse; } if( token[0] == '{' || token[0] == '}' ) { G_Printf( "ERROR in script. Scorches must be followed by space or newline\n" ); return qfalse; } if( count > WEAPONDEF_NUMPARMS ) return qfalse; if( !Q_stricmp( token, "instant" ) ) parm[count] = 0; else parm[count] = atoi( token ); if( parm[count] < 0 ) return qfalse; count++; } // incomplete or wrong file if( count < WEAPONDEF_NUMPARMS ) return qfalse; // validate // 2 and 3 are weapon switches. They MUST be G_FRAMETIME msecs at least if( parm[2] < G_FRAMETIME ) parm[2] = G_FRAMETIME; if( parm[3] < G_FRAMETIME ) parm[3] = G_FRAMETIME; count = 0; // put the data into the firedef firedef->usage_count = parm[count++]; firedef->projectile_count = parm[count++]; firedef->weaponup_time = parm[count++]; firedef->weapondown_time = parm[count++]; firedef->reload_time = parm[count++]; firedef->cooldown_time = parm[count++]; firedef->powering_time = parm[count++]; firedef->timeout = parm[count++]; firedef->damage = parm[count++]; firedef->knockback = parm[count++]; firedef->splash_radius = parm[count++]; firedef->splash_min_damage = parm[count++]; firedef->speed = parm[count++]; firedef->h_spread = parm[count++]; firedef->v_spread = parm[count++]; firedef->ammo_pickup = parm[count++]; firedef->ammo_max = parm[count++]; return qtrue; } qboolean G_LoadFiredefFromFile( firedef_t *firedef ) { char filename[MAX_QPATH]; qbyte *data; if( !firedef || !firedef->name ) return qfalse; Q_snprintfz( filename, sizeof(filename), "%s %s", firedef->name, (firedef->fire_mode == FIRE_MODE_STRONG) ? "strong" : "weak" ); data = G_LoadWeaponDefFile( filename ); if( !data ) return qfalse; //parse the file updating the firedef if( !G_ParseFiredefFile( data, firedef ) ) { G_Printf( "'InitWeapons': Error in definition file %s\n", filename ); G_Free(data); return qfalse; } G_Free(data); return qtrue; } #endif //ALLOW_WEAPONDEFS_READED_FROM_DISK //================= //G_FiredefForAmmo //================= firedef_t *G_FiredefForAmmo( int tag ) { gitem_t *item = GS_FindItemByTag( tag ); if( item->type == IT_AMMO) return (firedef_t *)item->info; else return NULL; } //================= //G_FiredefForWeapon //================= weapon_info_t *G_FiredefForWeapon( int tag ) { gitem_t *item = GS_FindItemByTag( tag ); if( item->type == IT_WEAPON ) return (weapon_info_t *)item->info; else return NULL; } //================= //G_InitWeapons //================= void G_InitWeapons( void ) { int i; firedef_t *firedef; //set up no weapon with no fires (not totally correct. I should not use item->info) g_weaponInfos[WEAP_NONE].firedef = &noweaponFireDef; g_weaponInfos[WEAP_NONE].firedef_weak = &noweaponFireDef; //assign the actual weapons for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { g_weaponInfos[i].firedef = g_weaponInfos[i].firedef_weak = NULL; //start clean //find predefined firedefs for this weapon for( firedef = &ammoFireDefs[0]; firedef->name; firedef++ ) { if( !Q_stricmp( firedef->name, game.items[i]->pickup_name ) ) { if( firedef->ammo_id < AMMO_WEAK_GUNBLADE ) { g_weaponInfos[i].firedef = firedef; //G_Printf( "WEAPONS: Assigned firedef strong to weapon %s\n", firedef->name ); } else {//if( firedef->fire_mode == FIRE_MODE_WEAK ) g_weaponInfos[i].firedef_weak = firedef; //G_Printf( "WEAPONS: Assigned firedef weak to weapon %s\n", firedef->name ); } } } if( !g_weaponInfos[i].firedef ) G_Error( "'G_InitWeapons': Weapon %s doesn't have any strong fire defined\n", game.items[i]->pickup_name ); if( !g_weaponInfos[i].firedef_weak ) G_Error( "'G_InitWeapons': Weapon %s doesn't have any weak fire defined\n", game.items[i]->pickup_name ); //link here too, but use g_weaponInfos better game.items[i]->info = (weapon_info_t *)&g_weaponInfos[i]; if( game.items[i]->weakammo_tag ) { game.items[game.items[i]->weakammo_tag]->info = g_weaponInfos[i].firedef_weak; game.items[game.items[i]->weakammo_tag]->quantity = g_weaponInfos[i].firedef_weak->ammo_pickup; } if( game.items[i]->ammo_tag ) { game.items[game.items[i]->ammo_tag]->info = g_weaponInfos[i].firedef; game.items[game.items[i]->ammo_tag]->quantity = g_weaponInfos[i].firedef->ammo_pickup; } } #ifdef ALLOW_WEAPONDEFS_READED_FROM_DISK // ok, we assigned the default ones, but meanwhile we are designing the // game we will load replacements from text files for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { if( G_LoadFiredefFromFile( g_weaponInfos[i].firedef ) ) if( developer->integer ) G_Printf("'G_InitWeapons': Found firedef file for %s strong\n", g_weaponInfos[i].firedef->name); if( G_LoadFiredefFromFile( g_weaponInfos[i].firedef_weak ) ) if( developer->integer ) G_Printf("'G_InitWeapons': Found firedef file for %s weak\n", g_weaponInfos[i].firedef->name); } #endif //ALLOW_WEAPONDEFS_READED_FROM_DISK G_Printf( "WEAPONS Initialized\n" ); }