This tutorial was created by Hap of FreeForm Interactive(creators of Future
vs. Fantasy). These works are all (c) 1998 Freeform Interactive.
This tutorial will modify the shotgun we created in the last tutorial to shoot pellets rather than big bullet holes
and will also add a punch attack. This requires the modifications you did in the last tutorial. First we need to
modify the weapons.c file.
First venture to the bottom of the file and edit the FireWeapon function to look like
so:
geBoolean FireWeapon(GenVSI *VSI, void *PlayerData, float Time)
{
GPlayer *Player;
assert(PlayerData);
Player = (GPlayer*)PlayerData;
if (GenVSI_GetTime(VSI) < Player->NextWeaponTime)
return GE_TRUE;
ValidateWeapon(VSI, PlayerData);
switch (Player->CurrentWeapon)
{
case 0:
FirePunch(VSI,
Player, Time);
break;
case 1:
FireGrenade(VSI, Player, Time);
break;
case 2:
FireRocket(VSI, Player, Time);
break;
case 3:
FireCombatShotgun(VSI,
Player, Time);
break;
}
return GE_TRUE;
}
Now we must replace our old shotgun fire and control routines to look like the code below:
//=====================================================================================
// CombatShotgun_Control
//=====================================================================================
geBoolean CombatShotgun_Control(GenVSI *VSI, void *PlayerData, float Time)
{
GPlayer *Player;
static int currentPellet; //number of effective shotgun pellet number
Player = (GPlayer*)PlayerData;
currentPellet++;
if (currentPellet == 1)
{
// Play the sound
//GenVSI_PlaySound(VSI, SOUND_INDEX_SHREDDER, &Player->XForm.Translation);
}
else if (currentPellet >= 6) // max number of shotgun pellets... should be a constant
{
Player->Owner->Weapon = NULL;
GenVSI_DestroyPlayer(VSI, PlayerData);
currentPellet = 0;
return GE_TRUE;
}
Player->XForm.Translation.X += rndSpread(10); //Shotgun spread
Player->XForm.Translation.Y += rndSpread(10);
return GE_TRUE;
}
//=====================================================================================
// FireCombatShotgun
//=====================================================================================
void FireCombatShotgun(GenVSI *VSI, void *PlayerData, float Time)
{
GPlayer *Weapon;
GE_Collision Collision;
geVec3d Front, Back, In;
GPlayer *Player;
geWorld *World;
Player = (GPlayer*)PlayerData;
if (Player->Inventory[ITEM_SHREDDER] <= 0 || !Player->InventoryHas[ITEM_SHREDDER])
{
SwitchToNextBestWeapon(VSI, Player);
return;
}
if (!Player->Weapon) // Check to see if allready
firing
{
Weapon = GenVSI_SpawnPlayer(VSI, "Weapon_Shredder");
if (!Weapon)
{
GenVSI_ConsolePrintf(VSI, "FireShredder:
Failed to add player.\n");
return;
}
}
else
return;
if (Player->Inventory[ITEM_SHREDDER] < 0)
{
SwitchToNextBestWeapon(VSI, Player);
Player->Inventory[ITEM_SHREDDER] = 0;
}
// Setup the callbacks for this weapon
Weapon->Control = CombatShotgun_Control;
Weapon->Blocked = NULL;
Weapon->Trigger = NULL;
Weapon->Owner = Player;
Player->Weapon = Weapon;
Weapon->Time = 0.0f;
Weapon->ViewFlags = VIEW_TYPE_NONE;
Weapon->ViewIndex = VIEW_INDEX_NONE;
Weapon->FxFlags = FX_SCATTERGUN;
Weapon->XForm = Player->XForm;
geVec3d_Add(&Weapon->XForm.Translation, &Player->GunOffset, &Weapon->XForm.Translation);
Front = Weapon->XForm.Translation;
geXForm3d_GetIn(&Weapon->XForm, &In);
geVec3d_AddScaled(&Front, &In, 10000.0f, &Back);
World = GenVSI_GetWorld(VSI);
assert(World);
if (geWorld_Collision(World, NULL, NULL, &Front, &Back, GE_COLLIDE_ACTORS, 0xffffffff,
&Collision))
{
if (Collision.Actor)
{
GPlayer
*PlayerHit;
PlayerHit = GenVSI_ActorToPlayer(VSI, Collision.Actor);
if (PlayerHit)
DammagePlayer(VSI, Weapon,
PlayerHit, 25, 90.0f, Time);
}
if (Collision.Model)
{
GPlayer
*Target;
#pragma message ("Use: GenVSI_ModelToPlayer...")
Target = (GPlayer*)geWorld_ModelGetUserData(Collision.Model);
if (Target && Target->Trigger &&
(Target->ViewFlags & VIEW_TYPE_PHYSOB))
{
geXForm3d_GetIn(&Player->XForm,
&In);
Player->Inertia =
In;
geVec3d_Scale(&In,
1000.f, &Player->Inertia);
Target->Trigger(VSI,
Target, Player, (void*)&Collision);
}
}
}
Player->VPos = Player->XForm.Translation;
Player->NextWeaponTime = GenVSI_GetTime(VSI) + 1.0f; //fire rate
}
Lastly we need to add in our new punch code. Add the following code somewhere into your weapons.c file:
//=====================================================================================
// Punch_Control
//=====================================================================================
geBoolean Punch_Control(GenVSI *VSI, void *PlayerData, float Time)
{
GPlayer *Player;
Player = (GPlayer*)PlayerData;
Player->Time += Time;
if (Player->Time > 0.1f)
{
Player->Owner->Weapon = NULL;
GenVSI_DestroyPlayer(VSI, PlayerData);
return GE_TRUE;
}
Player->XForm = Player->Owner->XForm;
geVec3d_Add(&Player->XForm.Translation, &Player->Owner->GunOffset, &Player->XForm.Translation);
Player->VPos = Player->XForm.Translation;
return GE_TRUE;
}
//=====================================================================================
// FirePunch
//=====================================================================================
void FirePunch(GenVSI *VSI, void *PlayerData, float Time)
{
GPlayer *Weapon;
GE_Collision Collision;
geVec3d Front, Back, In;
GPlayer *Player;
geWorld *World;
float distance; //used for limiting range
Player = (GPlayer*)PlayerData;
Weapon = GenVSI_SpawnPlayer(VSI, "Weapon_Shredder");
// Setup the callbacks for this weapon
Weapon->Control = Punch_Control;
Weapon->Blocked = NULL;
Weapon->Trigger = NULL;
Weapon->Owner = Player;
Player->Weapon = Weapon;
Weapon->Time = 0.0f;
Weapon->ViewFlags = VIEW_TYPE_NONE;
Weapon->ViewIndex = VIEW_INDEX_NONE;
Weapon->FxFlags = FX_PUNCH;
Weapon->XForm = Player->XForm;
geVec3d_Add(&Weapon->XForm.Translation, &Player->GunOffset, &Weapon->XForm.Translation);
// Play the sound
//GenVSI_PlaySound(VSI, SOUND_INDEX_SHREDDER, &Player->XForm.Translation);
Front = Weapon->XForm.Translation;
geXForm3d_GetIn(&Weapon->XForm, &In);
geVec3d_AddScaled(&Front, &In, 150.0f, &Back);
World = GenVSI_GetWorld(VSI);
assert(World);
if (geWorld_Collision(World, NULL, NULL, &Front, &Back, GE_COLLIDE_ACTORS, 0xffffffff, &Collision))
{
if (Collision.Actor)
{
GPlayer
*PlayerHit;
PlayerHit = GenVSI_ActorToPlayer(VSI, Collision.Actor);
if (PlayerHit)
DammagePlayer(VSI, Weapon,
PlayerHit, 25, 90.0f, Time);
}
if (Collision.Model)
{
GPlayer
*Target;
#pragma message ("Use: GenVSI_ModelToPlayer...")
Target = (GPlayer*)geWorld_ModelGetUserData(Collision.Model);
if (Target && Target->Trigger &&
(Target->ViewFlags & VIEW_TYPE_PHYSOB))
{
geXForm3d_GetIn(&Player->XForm,
&In);
Player->Inertia =
In;
geVec3d_Scale(&In,
1000.f, &Player->Inertia);
Target->Trigger(VSI,
Target, Player, (void*)&Collision);
}
}
}
Player->VPos = Player->XForm.Translation;
Player->NextWeaponTime = GenVSI_GetTime(VSI) + 0.5f;
}
Next we need to edit our fx.c file. Add the following functions to the bottom your fx.c file.
//=====================================================================================
// ControlScattergunAnim
//=====================================================================================
static geBoolean ControlScattergunAnim(Fx_System *Fx, Fx_TempPlayer *Player, float Time)
{
GE_LVertex Vert;
int32 Frame;
Player->Time += Time;
Vert.X = Player->Pos.X;
Vert.Y = Player->Pos.Y;
Vert.Z = Player->Pos.Z;
Vert.r = Vert.g = Vert.b = Vert.a = 255.0f;
Vert.u = Vert.v = 0.0f;
Frame = (int32)(Player->Time*20.0f);
Frame *= 2; //twice as fast, don't want long pellet trails
if (Frame >= NUM_SMOKE_TEXTURES)
{
// Make sure we draw the last frame...
Frame = NUM_SMOKE_TEXTURES-1;
//smaller pellets
geWorld_AddPolyOnce(Fx->World, &Vert, 1, Fx->SmokeTextures[Frame],
GE_TEXTURED_POINT, GE_RENDER_DO_NOT_OCCLUDE_OTHERS, 0.1f);
Fx_SystemRemoveTempPlayer(Fx, Player);
// Smoke is done...
}
else
geWorld_AddPolyOnce(Fx->World, &Vert, 1, Fx->SmokeTextures[Frame],
GE_TEXTURED_POINT, GE_RENDER_DO_NOT_OCCLUDE_OTHERS, 0.1f);
return GE_TRUE;
}
//=====================================================================================
// ControlScattergunFx
//=====================================================================================
static geBoolean ControlScattergunFx(Fx_System *Fx, Fx_Player *Player, float Time)
{
assert(Fx);
assert(Player);
Player->Time += Time;
if (Player->Time >= 0.03f)
{
Fx_TempPlayer *TempPlayer;
GE_Collision Collision;
geVec3d
Front, Back, In;
geVec3d
Mins = {-1.0f, -1.0f, -1.0f};
geVec3d
Maxs = { 1.0f, 1.0f, 1.0f};
TempPlayer = Fx_SystemAddTempPlayer(Fx);
if (!TempPlayer)
return GE_TRUE;
// Oh well...
TempPlayer->Control = ControlScattergunAnim;
TempPlayer->Time = 0.0f;
Front = Player->XForm.Translation;
geXForm3d_GetIn(&Player->XForm, &In);
geVec3d_AddScaled(&Front, &In, 10000.0f, &Back);
if (geWorld_Collision(Fx->World, NULL, NULL, &Front, &Back,
GE_COLLIDE_ACTORS | GE_COLLIDE_MODELS, 0xffffffff, &Collision))
{
// Move it back a little from the wall
geVec3d_AddScaled(&Collision.Impact, &In,
-50.0f, &Collision.Impact);
TempPlayer->Pos = Collision.Impact;
}
else
TempPlayer->Pos = Player->XForm.Translation;
Player->Time = 0.0f;
}
return GE_TRUE;
}
//=====================================================================================
// ControlPunchAnim
//=====================================================================================
static geBoolean ControlPunchAnim(Fx_System *Fx, Fx_TempPlayer *Player, float Time)
{
GE_LVertex Vert;
int32 Frame;
Player->Time += Time;
Vert.X = Player->Pos.X;
Vert.Y = Player->Pos.Y;
Vert.Z = Player->Pos.Z;
Vert.r = Vert.g = Vert.b = Vert.a = 255.0f;
Vert.u = Vert.v = 0.0f;
Frame = (int32)(Player->Time*20.0f);
if (Frame >= NUM_PARTICLE_TEXTURES)
{
// Make sure we draw the last frame...
Frame = NUM_SMOKE_TEXTURES-1;
geWorld_AddPolyOnce(Fx->World, &Vert, 1, Fx->ParticleTextures[Frame],
GE_TEXTURED_POINT, GE_RENDER_DO_NOT_OCCLUDE_OTHERS, 0.1f); // 2,6
Fx_SystemRemoveTempPlayer(Fx, Player);
// Smoke is done...
}
else
geWorld_AddPolyOnce(Fx->World, &Vert, 1, Fx->ParticleTextures[Frame],
GE_TEXTURED_POINT, GE_RENDER_DO_NOT_OCCLUDE_OTHERS, 0.1f);
return GE_TRUE;
}
//=====================================================================================
// ControlPunchFx
//=====================================================================================
static geBoolean ControlPunchFx(Fx_System *Fx, Fx_Player *Player, float Time)
{
assert(Fx);
assert(Player);
Player->Time += Time;
if (Player->Time >= 0.03f)
{
Fx_TempPlayer *TempPlayer;
GE_Collision Collision;
geVec3d
Front, Back, In;
geVec3d
Mins = {-1.0f, -1.0f, -1.0f};
geVec3d
Maxs = { 1.0f, 1.0f, 1.0f};
TempPlayer = Fx_SystemAddTempPlayer(Fx);
if (!TempPlayer)
return GE_TRUE;
// Oh well...
TempPlayer->Control = ControlPunchAnim;
TempPlayer->Time = 0.0f;
Front = Player->XForm.Translation;
geXForm3d_GetIn(&Player->XForm, &In);
geVec3d_AddScaled(&Front, &In, 150.0f, &Back);
if (geWorld_Collision(Fx->World, NULL, NULL, &Front, &Back,
GE_COLLIDE_ACTORS | GE_COLLIDE_MODELS, 0xffffffff, &Collision))
{
// Move it back a little from the wall
geVec3d_AddScaled(&Collision.Impact, &In,
-50.0f, &Collision.Impact);
TempPlayer->Pos = Collision.Impact;
}
else
TempPlayer->Pos = Player->XForm.Translation;
Player->Time = 0.0f;
}
return GE_TRUE;
}
Lastly we need to modify the fx.h file. Modify the definitions(towards
the beginning of your file) to look like the code below:
// Flagged fx for players
#define FX_SMOKE_TRAIL
(1<<0)
#define FX_PARTICLE_TRAIL
(1<<1)
#define FX_SHREDDER
(1<<2)
#define FX_PUNCH
(1<<4)
#define FX_SCATTERGUN
(1<<8)
// Spawnable fx
#define FX_EXPLODE1
0
#define FX_EXPLODE2
1
The end. Enjoy!