📄 g_items.c
字号:
qboolean Pickup_Health (edict_t *ent, edict_t *other)
{
if (!(ent->style & HEALTH_IGNORE_MAX))
if (other->health >= other->max_health)
return false;
other->health += ent->count;
if (!(ent->style & HEALTH_IGNORE_MAX))
{
if (other->health > other->max_health)
other->health = other->max_health;
}
if (ent->style & HEALTH_TIMED)
{
ent->think = MegaHealth_think;
ent->nextthink = level.time + 5;
ent->owner = other;
ent->flags |= FL_RESPAWN;
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_NOT;
}
else
{
if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value))
SetRespawn (ent, 30);
}
return true;
}
//======================================================================
int ArmorIndex (edict_t *ent)
{
if (!ent->client)
return 0;
if (ent->client->pers.inventory[jacket_armor_index] > 0)
return jacket_armor_index;
if (ent->client->pers.inventory[combat_armor_index] > 0)
return combat_armor_index;
if (ent->client->pers.inventory[body_armor_index] > 0)
return body_armor_index;
return 0;
}
qboolean Pickup_Armor (edict_t *ent, edict_t *other)
{
int old_armor_index;
gitem_armor_t *oldinfo;
gitem_armor_t *newinfo;
int newcount;
float salvage;
int salvagecount;
// get info on new armor
newinfo = (gitem_armor_t *)ent->item->info;
old_armor_index = ArmorIndex (other);
// handle armor shards specially
if (ent->item->tag == ARMOR_SHARD)
{
if (!old_armor_index)
other->client->pers.inventory[jacket_armor_index] = 2;
else
other->client->pers.inventory[old_armor_index] += 2;
}
// if player has no armor, just use it
else if (!old_armor_index)
{
other->client->pers.inventory[ITEM_INDEX(ent->item)] = newinfo->base_count;
}
// use the better armor
else
{
// get info on old armor
if (old_armor_index == jacket_armor_index)
oldinfo = &jacketarmor_info;
else if (old_armor_index == combat_armor_index)
oldinfo = &combatarmor_info;
else // (old_armor_index == body_armor_index)
oldinfo = &bodyarmor_info;
if (newinfo->normal_protection > oldinfo->normal_protection)
{
// calc new armor values
salvage = oldinfo->normal_protection / newinfo->normal_protection;
salvagecount = salvage * other->client->pers.inventory[old_armor_index];
newcount = newinfo->base_count + salvagecount;
if (newcount > newinfo->max_count)
newcount = newinfo->max_count;
// zero count of old armor so it goes away
other->client->pers.inventory[old_armor_index] = 0;
// change armor to new item with computed value
other->client->pers.inventory[ITEM_INDEX(ent->item)] = newcount;
}
else
{
// calc new armor values
salvage = newinfo->normal_protection / oldinfo->normal_protection;
salvagecount = salvage * newinfo->base_count;
newcount = other->client->pers.inventory[old_armor_index] + salvagecount;
if (newcount > oldinfo->max_count)
newcount = oldinfo->max_count;
// if we're already maxed out then we don't need the new armor
if (other->client->pers.inventory[old_armor_index] >= newcount)
return false;
// update current armor value
other->client->pers.inventory[old_armor_index] = newcount;
}
}
if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value))
SetRespawn (ent, 20);
return true;
}
//======================================================================
int PowerArmorType (edict_t *ent)
{
if (!ent->client)
return POWER_ARMOR_NONE;
if (!(ent->flags & FL_POWER_ARMOR))
return POWER_ARMOR_NONE;
if (ent->client->pers.inventory[power_shield_index] > 0)
return POWER_ARMOR_SHIELD;
if (ent->client->pers.inventory[power_screen_index] > 0)
return POWER_ARMOR_SCREEN;
return POWER_ARMOR_NONE;
}
void Use_PowerArmor (edict_t *ent, gitem_t *item)
{
int index;
if (ent->flags & FL_POWER_ARMOR)
{
ent->flags &= ~FL_POWER_ARMOR;
gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
}
else
{
index = ITEM_INDEX(FindItem("cells"));
if (!ent->client->pers.inventory[index])
{
gi.cprintf (ent, PRINT_HIGH, "No cells for power armor.\n");
return;
}
ent->flags |= FL_POWER_ARMOR;
gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power1.wav"), 1, ATTN_NORM, 0);
}
}
qboolean Pickup_PowerArmor (edict_t *ent, edict_t *other)
{
int quantity;
quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)];
other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
if (deathmatch->value)
{
if (!(ent->spawnflags & DROPPED_ITEM) )
SetRespawn (ent, ent->item->quantity);
// auto-use for DM only if we didn't already have one
if (!quantity)
ent->item->use (other, ent->item);
}
return true;
}
void Drop_PowerArmor (edict_t *ent, gitem_t *item)
{
if ((ent->flags & FL_POWER_ARMOR) && (ent->client->pers.inventory[ITEM_INDEX(item)] == 1))
Use_PowerArmor (ent, item);
Drop_General (ent, item);
}
//======================================================================
/*
===============
Touch_Item
===============
*/
void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
qboolean taken;
if (!other->client)
return;
if (other->health < 1)
return; // dead people can't pickup
if (!ent->item->pickup)
return; // not a grabbable item?
taken = ent->item->pickup(ent, other);
if (taken)
{
// flash the screen
other->client->bonus_alpha = 0.25;
// show icon and name on status bar
other->client->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(ent->item->icon);
other->client->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS+ITEM_INDEX(ent->item);
other->client->pickup_msg_time = level.time + 3.0;
// change selected item
if (ent->item->use)
other->client->pers.selected_item = other->client->ps.stats[STAT_SELECTED_ITEM] = ITEM_INDEX(ent->item);
if (ent->item->pickup == Pickup_Health)
{
if (ent->count == 2)
gi.sound(other, CHAN_ITEM, gi.soundindex("items/s_health.wav"), 1, ATTN_NORM, 0);
else if (ent->count == 10)
gi.sound(other, CHAN_ITEM, gi.soundindex("items/n_health.wav"), 1, ATTN_NORM, 0);
else if (ent->count == 25)
gi.sound(other, CHAN_ITEM, gi.soundindex("items/l_health.wav"), 1, ATTN_NORM, 0);
else // (ent->count == 100)
gi.sound(other, CHAN_ITEM, gi.soundindex("items/m_health.wav"), 1, ATTN_NORM, 0);
}
else if (ent->item->pickup_sound)
{
gi.sound(other, CHAN_ITEM, gi.soundindex(ent->item->pickup_sound), 1, ATTN_NORM, 0);
}
}
if (!(ent->spawnflags & ITEM_TARGETS_USED))
{
G_UseTargets (ent, other);
ent->spawnflags |= ITEM_TARGETS_USED;
}
if (!taken)
return;
if (!((coop->value) && (ent->item->flags & IT_STAY_COOP)) || (ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)))
{
if (ent->flags & FL_RESPAWN)
ent->flags &= ~FL_RESPAWN;
else
G_FreeEdict (ent);
}
}
//======================================================================
static void drop_temp_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other == ent->owner)
return;
Touch_Item (ent, other, plane, surf);
}
static void drop_make_touchable (edict_t *ent)
{
ent->touch = Touch_Item;
if (deathmatch->value)
{
ent->nextthink = level.time + 29;
ent->think = G_FreeEdict;
}
}
edict_t *Drop_Item (edict_t *ent, gitem_t *item)
{
edict_t *dropped;
vec3_t forward, right;
vec3_t offset;
dropped = G_Spawn();
dropped->classname = item->classname;
dropped->item = item;
dropped->spawnflags = DROPPED_ITEM;
dropped->s.effects = item->world_model_flags;
dropped->s.renderfx = RF_GLOW;
VectorSet (dropped->mins, -15, -15, -15);
VectorSet (dropped->maxs, 15, 15, 15);
gi.setmodel (dropped, dropped->item->world_model);
dropped->solid = SOLID_TRIGGER;
dropped->movetype = MOVETYPE_TOSS;
dropped->touch = drop_temp_touch;
dropped->owner = ent;
if (ent->client)
{
trace_t trace;
AngleVectors (ent->client->v_angle, forward, right, NULL);
VectorSet(offset, 24, 0, -16);
G_ProjectSource (ent->s.origin, offset, forward, right, dropped->s.origin);
trace = gi.trace (ent->s.origin, dropped->mins, dropped->maxs,
dropped->s.origin, ent, CONTENTS_SOLID);
VectorCopy (trace.endpos, dropped->s.origin);
}
else
{
AngleVectors (ent->s.angles, forward, right, NULL);
VectorCopy (ent->s.origin, dropped->s.origin);
}
VectorScale (forward, 100, dropped->velocity);
dropped->velocity[2] = 300;
dropped->think = drop_make_touchable;
dropped->nextthink = level.time + 1;
gi.linkentity (dropped);
return dropped;
}
void Use_Item (edict_t *ent, edict_t *other, edict_t *activator)
{
ent->svflags &= ~SVF_NOCLIENT;
ent->use = NULL;
if (ent->spawnflags & ITEM_NO_TOUCH)
{
ent->solid = SOLID_BBOX;
ent->touch = NULL;
}
else
{
ent->solid = SOLID_TRIGGER;
ent->touch = Touch_Item;
}
gi.linkentity (ent);
}
//======================================================================
/*
================
droptofloor
================
*/
void droptofloor (edict_t *ent)
{
trace_t tr;
vec3_t dest;
float *v;
v = tv(-15,-15,-15);
VectorCopy (v, ent->mins);
v = tv(15,15,15);
VectorCopy (v, ent->maxs);
if (ent->model)
gi.setmodel (ent, ent->model);
else
gi.setmodel (ent, ent->item->world_model);
ent->solid = SOLID_TRIGGER;
ent->movetype = MOVETYPE_TOSS;
ent->touch = Touch_Item;
v = tv(0,0,-128);
VectorAdd (ent->s.origin, v, dest);
tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
if (tr.startsolid)
{
gi.dprintf ("droptofloor: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
G_FreeEdict (ent);
return;
}
VectorCopy (tr.endpos, ent->s.origin);
if (ent->team)
{
ent->flags &= ~FL_TEAMSLAVE;
ent->chain = ent->teamchain;
ent->teamchain = NULL;
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_NOT;
if (ent == ent->teammaster)
{
ent->nextthink = level.time + FRAMETIME;
ent->think = DoRespawn;
}
}
if (ent->spawnflags & ITEM_NO_TOUCH)
{
ent->solid = SOLID_BBOX;
ent->touch = NULL;
ent->s.effects &= ~EF_ROTATE;
ent->s.renderfx &= ~RF_GLOW;
}
if (ent->spawnflags & ITEM_TRIGGER_SPAWN)
{
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_NOT;
ent->use = Use_Item;
}
gi.linkentity (ent);
}
/*
===============
PrecacheItem
Precaches all data needed for a given item.
This will be called for each item spawned in a level,
and for each item in each client's inventory.
===============
*/
void PrecacheItem (gitem_t *it)
{
char *s, *start;
char data[MAX_QPATH];
int len;
gitem_t *ammo;
if (!it)
return;
if (it->pickup_sound)
gi.soundindex (it->pickup_sound);
if (it->world_model)
gi.modelindex (it->world_model);
if (it->view_model)
gi.modelindex (it->view_model);
if (it->icon)
gi.imageindex (it->icon);
// parse everything for its ammo
if (it->ammo && it->ammo[0])
{
ammo = FindItem (it->ammo);
if (ammo != it)
PrecacheItem (ammo);
}
// parse the space seperated precache string for other items
s = it->precaches;
if (!s || !s[0])
return;
while (*s)
{
start = s;
while (*s && *s != ' ')
s++;
len = s-start;
if (len >= MAX_QPATH || len < 5)
gi.error ("PrecacheItem: %s has bad precache string", it->classname);
memcpy (data, start, len);
data[len] = 0;
if (*s)
s++;
// determine type based on extension
if (!strcmp(data+len-3, "md2"))
gi.modelindex (data);
else if (!strcmp(data+len-3, "sp2"))
gi.modelindex (data);
else if (!strcmp(data+len-3, "wav"))
gi.soundindex (data);
if (!strcmp(data+len-3, "pcx"))
gi.imageindex (data);
}
}
/*
============
SpawnItem
Sets the clipping size and plants the object on the floor.
Items can't be immediately dropped to floor, because they might
be on an entity that hasn't spawned yet.
============
*/
void SpawnItem (edict_t *ent, gitem_t *item)
{
PrecacheItem (item);
if (ent->spawnflags)
{
if (strcmp(ent->classname, "key_power_cube") != 0)
{
ent->spawnflags = 0;
gi.dprintf("%s at %s has invalid spawnflags set\n", ent->classname, vtos(ent->s.origin));
}
}
// some items will be prevented in deathmatch
if (deathmatch->value)
{
if ( (int)dmflags->value & DF_NO_ARMOR )
{
if (item->pickup == Pickup_Armor || item->pickup == Pickup_PowerArmor)
{
G_FreeEdict (ent);
return;
}
}
if ( (int)dmflags->value & DF_NO_ITEMS )
{
if (item->pickup == Pickup_Powerup)
{
G_FreeEdict (ent);
return;
}
}
if ( (int)dmflags->value & DF_NO_HEALTH )
{
if (item->pickup == Pickup_Health || item->pickup == Pickup_Adrenaline || item->pickup == Pickup_AncientHead)
{
G_FreeEdict (ent);
return;
}
}
if ( (int)dmflags->value & DF_INFINITE_AMMO )
{
if ( (item->flags == IT_AMMO) || (strcmp(ent->classname, "weapon_bfg") == 0) )
{
G_FreeEdict (ent);
return;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -