#include "g_local.h" #include "m_player.h" #include "bot.h" int cumsindex; void ClientUserinfoChanged (edict_t *ent, char *userinfo); void SP_misc_teleporter_dest (edict_t *ent); // // Gross, ugly, disgustuing hack section // // this function is an ugly as hell hack to fix some map flaws // // the coop spawn spots on some maps are SNAFU. There are coop spots // with the wrong targetname as well as spots with no name at all // // we use carnal knowledge of the maps to fix the coop spot targetnames to match // that of the nearest named single player spot static void SP_FixCoopSpots (edict_t *self) { edict_t *spot; vec3_t d; spot = NULL; while(1) { spot = G_Find(spot, FOFS(classname), "info_player_start"); if (!spot) return; if (!spot->targetname) continue; VectorSubtract(self->s.origin, spot->s.origin, d); if (VectorLength(d) < 384) { if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0) { // gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname); self->targetname = spot->targetname; } return; } } } // now if that one wasn't ugly enough for you then try this one on for size // some maps don't have any coop spots at all, so we need to create them // where they should have been static void SP_CreateCoopSpots (edict_t *self) { edict_t *spot; if (Q_stricmp(level.mapname, "security") == 0) { spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 - 64; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 + 64; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 + 128; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; return; } } /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) The normal starting point for a level. */ void SP_info_player_start(edict_t *self) { if (!coop->value) return; if (Q_stricmp(level.mapname, "security") == 0) { // invoke one of our gross, ugly, disgusting hacks self->think = SP_CreateCoopSpots; self->nextthink = level.time + FRAMETIME; } } /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) potential spawning position for deathmatch games */ void SP_info_player_deathmatch(edict_t *self) { if (!deathmatch->value) { G_FreeEdict (self); return; } SP_misc_teleporter_dest (self); } /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) potential spawning position for coop games */ void SP_info_player_coop(edict_t *self) { if (!coop->value) { G_FreeEdict (self); return; } if ((Q_stricmp(level.mapname, "jail2") == 0) || (Q_stricmp(level.mapname, "jail4") == 0) || (Q_stricmp(level.mapname, "mine1") == 0) || (Q_stricmp(level.mapname, "mine2") == 0) || (Q_stricmp(level.mapname, "mine3") == 0) || (Q_stricmp(level.mapname, "mine4") == 0) || (Q_stricmp(level.mapname, "lab") == 0) || (Q_stricmp(level.mapname, "boss1") == 0) || (Q_stricmp(level.mapname, "fact3") == 0) || (Q_stricmp(level.mapname, "biggun") == 0) || (Q_stricmp(level.mapname, "space") == 0) || (Q_stricmp(level.mapname, "command") == 0) || (Q_stricmp(level.mapname, "power2") == 0) || (Q_stricmp(level.mapname, "strike") == 0)) { // invoke one of our gross, ugly, disgusting hacks self->think = SP_FixCoopSpots; self->nextthink = level.time + FRAMETIME; } } /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) The deathmatch intermission point will be at one of these Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll' */ void SP_info_player_intermission(void) { } //======================================================================= void player_pain (edict_t *self, edict_t *other, float kick, int damage) { // player pain is handled at the end of the frame in P_DamageFeedback } qboolean IsFemale (edict_t *ent) { char *info; if (!ent->client) return false; info = Info_ValueForKey (ent->client->pers.userinfo, "skin"); if (info[0] == 'f' || info[0] == 'F') return true; return false; } void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker) { int mod; char *message; char *message2; qboolean ff; if (coop->value && attacker->client) meansOfDeath |= MOD_FRIENDLY_FIRE; if (deathmatch->value || coop->value) { ff = meansOfDeath & MOD_FRIENDLY_FIRE; mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; message = NULL; message2 = ""; switch (mod) { case MOD_SUICIDE: message = "suicides"; break; case MOD_FALLING: message = "cratered"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "sank like a rock"; break; case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "does a back flip into the lava"; break; case MOD_EXPLOSIVE: case MOD_BARREL: message = "blew up"; break; case MOD_EXIT: message = "found a way out"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_TARGET_BLASTER: message = "got blasted"; break; case MOD_BOMB: case MOD_SPLASH: case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; // RAFAEL case MOD_GEKK: case MOD_BRAINTENTACLE: message = "that's gotta hurt"; break; } if (attacker == self) { switch (mod) { case MOD_HELD_GRENADE: message = "tried to put the pin back in"; break; case MOD_HG_SPLASH: case MOD_G_SPLASH: if (IsFemale(self)) message = "tripped on her own grenade"; else message = "tripped on his own grenade"; break; case MOD_R_SPLASH: if (IsFemale(self)) message = "blew herself up"; else message = "blew himself up"; break; case MOD_BFG_BLAST: message = "should have used a smaller gun"; break; // RAFAEL 03-MAY-98 case MOD_TRAP: message = "sucked into his own trap"; break; default: if (IsFemale(self)) message = "killed herself"; else message = "killed himself"; break; } } if (message) { gi.bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message); if (deathmatch->value) self->client->resp.score--; self->enemy = NULL; return; } self->enemy = attacker; if (attacker && attacker->client) { switch (mod) { case MOD_BLASTER: message = "was blasted by"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_SSHOTGUN: message = "was blown away by"; message2 = "'s super shotgun"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_CHAINGUN: message = "was cut in half by"; message2 = "'s chaingun"; break; case MOD_GRENADE: message = "was popped by"; message2 = "'s grenade"; break; case MOD_G_SPLASH: message = "was shredded by"; message2 = "'s shrapnel"; break; case MOD_ROCKET: message = "ate"; message2 = "'s rocket"; break; case MOD_R_SPLASH: message = "almost dodged"; message2 = "'s rocket"; break; case MOD_HYPERBLASTER: message = "was melted by"; message2 = "'s hyperblaster"; break; case MOD_RAILGUN: message = "was railed by"; break; case MOD_BFG_LASER: message = "saw the pretty lights from"; message2 = "'s BFG"; break; case MOD_BFG_BLAST: message = "was disintegrated by"; message2 = "'s BFG blast"; break; case MOD_BFG_EFFECT: message = "couldn't hide from"; message2 = "'s BFG"; break; case MOD_HANDGRENADE: message = "caught"; message2 = "'s handgrenade"; break; case MOD_HG_SPLASH: message = "didn't see"; message2 = "'s handgrenade"; break; case MOD_HELD_GRENADE: message = "feels"; message2 = "'s pain"; break; case MOD_TELEFRAG: message = "tried to invade"; message2 = "'s personal space"; break; //ZOID case MOD_GRAPPLE: message = "was caught by"; message2 = "'s grapple"; break; //ZOID // RAFAEL 14-APR-98 case MOD_RIPPER: message = "ripped to shreds by"; message2 = "'s ripper gun"; break; case MOD_PHALANX: message = "was evaporated by"; break; case MOD_TRAP: message = "caught in trap by"; break; // END 14-APR-98 //PONKO case MOD_AIRSTRIKE: message = "was striked by"; message2 = "'s airstrike"; break; //PONKO } if (message) { gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2); if (deathmatch->value) { if (ff) attacker->client->resp.score--; else attacker->client->resp.score++; } return; } } } gi.bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname); if (deathmatch->value) self->client->resp.score--; } void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); void TossClientWeapon (edict_t *self) { gitem_t *item; edict_t *drop; qboolean quad; // RAFAEL qboolean quadfire; float dist; vec3_t v; edict_t *enemy = NULL; float spread; if (self->enemy && self->enemy != self) { if (self->enemy->classname[0] == 'p') { VectorSubtract(self->s.origin,self->enemy->s.origin,v); dist = VectorLength(v); if (dist < 200) enemy = self->enemy; } } if (!deathmatch->value) return; item = self->client->pers.weapon; if (! self->client->pers.inventory[self->client->ammo_index] ) item = NULL; if (item && (strcmp (item->pickup_name, "Blaster") == 0)) item = NULL; if (!((int)(dmflags->value) & DF_QUAD_DROP)) quad = false; else quad = (self->client->quad_framenum > (level.framenum + 10)); // RAFAEL if (!((int)(dmflags->value) & DF_QUADFIRE_DROP)) quadfire = false; else quadfire = (self->client->quadfire_framenum > (level.framenum + 10)); if (item && quad) spread = 22.5; else if (item && quadfire) spread = 12.5; else spread = 0.0; if (item) { self->client->v_angle[YAW] -= spread; drop = Drop_Item (self, item); self->client->v_angle[YAW] += spread; drop->spawnflags = DROPPED_PLAYER_ITEM; if (enemy) enemy->client->zc.second_target = drop; } if (quad) { self->client->v_angle[YAW] += spread; drop = Drop_Item (self, FindItemByClassname ("item_quad")); self->client->v_angle[YAW] -= spread; drop->spawnflags |= DROPPED_PLAYER_ITEM; drop->touch = Touch_Item; drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME; drop->think = G_FreeEdict; if (enemy) enemy->client->zc.second_target = drop; } // RAFAEL if (quadfire) { self->client->v_angle[YAW] += spread; drop = Drop_Item (self, FindItemByClassname ("item_quadfire")); self->client->v_angle[YAW] -= spread; drop->spawnflags |= DROPPED_PLAYER_ITEM; drop->touch = Touch_Item; drop->nextthink = level.time + (self->client->quadfire_framenum - level.framenum) * FRAMETIME; drop->think = G_FreeEdict; if (enemy) enemy->client->zc.second_target = drop; } } /* ================== LookAtKiller ================== */ void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker) { vec3_t dir; if (attacker && attacker != world && attacker != self) { VectorSubtract (attacker->s.origin, self->s.origin, dir); } else if (inflictor && inflictor != world && inflictor != self) { VectorSubtract (inflictor->s.origin, self->s.origin, dir); } else { self->client->killer_yaw = self->s.angles[YAW]; return; } self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]); } /* ================== player_die ================== */ void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int n; VectorClear (self->avelocity); self->takedamage = /*DAMAGE_NO;//*/DAMAGE_YES; self->movetype = MOVETYPE_TOSS; self->s.modelindex2 = 0; // remove linked weapon model //ZOID self->s.modelindex3 = 0; // remove linked ctf flag //ZOID self->s.angles[0] = 0; self->s.angles[2] = 0; self->s.sound = 0; self->client->weapon_sound = 0; self->maxs[2] = -8; // self->solid = SOLID_NOT; self->svflags |= SVF_DEADMONSTER; if (!self->deadflag) { if (self->svflags & SVF_MONSTER) { LookAtKiller (self, inflictor, attacker); self->nextthink = level.time + FRAMETIME; self->think = Bot_Think; self->client->respawn_time = level.time + 2.0; self->s.skinnum = (self - g_edicts - 1); } else { self->client->respawn_time = level.time + 1.0; LookAtKiller (self, inflictor, attacker); } self->client->ps.pmove.pm_type = PM_DEAD; ClientObituary (self, inflictor, attacker); //ZOID if (ctf->value) CTFFragBonuses(self, inflictor, attacker); //ZOID //旗持ってる場合は落とす if (self->client->pers.inventory[ITEM_INDEX(zflag_item)]) zflag_item->drop(self,zflag_item); TossClientWeapon (self); //ZOID if (ctf->value) { CTFPlayerResetGrapple (self); CTFDeadDropFlag (self); CTFDeadDropTech (self); } //ZOID // Knightmare added- drop ammogen backpack if (!OnSameTeam(self, attacker)) CTFApplyAmmogen (attacker, self); if (deathmatch->value && !(self->svflags & SVF_MONSTER)) Cmd_Help_f (self); // show scores } // remove powerups self->client->quad_framenum = 0; self->client->invincible_framenum = 0; self->client->breather_framenum = 0; self->client->enviro_framenum = 0; // Knightmare added self->client->double_framenum = 0; // RAFAEL self->client->quadfire_framenum = 0; // clear inventory memset(self->client->pers.inventory, 0, sizeof(self->client->pers.inventory)); if (mega_gibs->value && (self->health < -20)) // Octavian's modified gibs { // gib int num_giblets; gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); if (random () < 0.2) { num_giblets = 3; ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } else if (random () < 0.25) { num_giblets = 2; ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); } else if (random () < 0.35) { num_giblets = 3; ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } else if (random () < 0.5) { num_giblets = 4; ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } else { num_giblets = 2; ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/arm/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/bone2/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < num_giblets; n++) ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ThrowClientHead (self, damage); //ZOID self->client->anim_priority = ANIM_DEATH; self->client->anim_end = 0; //ZOID. self->takedamage = DAMAGE_NO; } else if (self->health < -40) { // gib gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n= 0; n < 4; n++) ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ThrowClientHead (self, damage); //ZOID self->client->anim_priority = ANIM_DEATH; self->client->anim_end = 0; //ZOID. self->takedamage = DAMAGE_NO; } else { // normal death if (!self->deadflag) { static int i; i = (i+1)%3; // start a death animation self->client->anim_priority = ANIM_DEATH; if (self->client->ps.pmove.pm_flags & PMF_DUCKED) { self->s.frame = FRAME_crdeath1-1; self->client->anim_end = FRAME_crdeath5; } else switch (i) { case 0: self->s.frame = FRAME_death101-1; self->client->anim_end = FRAME_death106; break; case 1: self->s.frame = FRAME_death201-1; self->client->anim_end = FRAME_death206; break; case 2: self->s.frame = FRAME_death301-1; self->client->anim_end = FRAME_death308; break; } gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); } } self->deadflag = DEAD_DEAD; //routing last index move if (chedit->value && self == &g_edicts[1]) Move_LastRouteIndex(); gi.linkentity (self); } //======================================================================= void SwitchToBestStartWeapon (gclient_t *client) { if (!client) return; if ( client->pers.inventory[slugs_index] && client->pers.inventory[ITEM_INDEX(FindItem("railgun"))] ) { client->pers.weapon = FindItem ("railgun"); return; } if ( client->pers.inventory[magslug_index] && client->pers.inventory[ITEM_INDEX (FindItem ("phalanx"))]) { client->pers.weapon = FindItem ("phalanx"); return; } if ( (client->pers.inventory[cells_index] >= 2) && client->pers.inventory[ITEM_INDEX (FindItem ("Ionripper"))]) { client->pers.weapon = FindItem ("Ionripper"); return; } /* if ( (client->pers.inventory[cells_index] >= 2) && client->pers.inventory[ITEM_INDEX(FindItem("Plasma Beam"))] ) { client->pers.weapon = FindItem ("Plasma Beam"); return; } */ if ( client->pers.inventory[cells_index] && client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))] ) { client->pers.weapon = FindItem ("hyperblaster"); return; } /* if ( client->pers.inventory[flechettes_index] && client->pers.inventory[ITEM_INDEX(FindItem("ETF Rifle"))] ) { client->pers.weapon = FindItem ("ETF Rifle"); return; } if ( client->pers.inventory[disruptors_index] && client->pers.inventory[ITEM_INDEX(FindItem("Disintegrator"))] ) { client->pers.weapon = FindItem ("Disintegrator"); return; } */ if ( client->pers.inventory[bullets_index] && client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))] ) { client->pers.weapon = FindItem ("chaingun"); return; } if ( client->pers.inventory[bullets_index] && client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))] ) { client->pers.weapon = FindItem ("machinegun"); return; } if ( client->pers.inventory[shells_index] >= 2 && client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))] ) { client->pers.weapon = FindItem ("super shotgun"); return; } if ( client->pers.inventory[shells_index] && client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] ) { client->pers.weapon = FindItem ("shotgun"); return; } client->pers.weapon = FindItem ("blaster"); } void SelectStartWeapon (gclient_t *client) { gitem_t *item; //test // item = FindItem("Zig Flag"); // client->pers.selected_item = ITEM_INDEX(item); // client->pers.inventory[client->pers.selected_item] = 1; // item = FindItem("Trap"); // client->pers.inventory[ITEM_INDEX(item)] = 100; //test item = FindItem("Blaster"); // was Fdi_BLASTER client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; // client->pers.weapon = item; //ZOID client->pers.lastweapon = item; //ZOID //ZOID item = FindItem("Grapple"); if (ctf->value) client->pers.inventory[ITEM_INDEX(item)] = 1; //ponpoko //ZOID // Knightmare- DM start values if (deathmatch->value) { client->pers.inventory[ITEM_INDEX(FindItem("Shells"))] = sk_dm_start_shells->value; client->pers.inventory[ITEM_INDEX(FindItem("Bullets"))] = sk_dm_start_bullets->value; client->pers.inventory[ITEM_INDEX(FindItem("Rockets"))] = sk_dm_start_rockets->value; client->pers.inventory[ITEM_INDEX(FindItem("Grenades"))] = sk_dm_start_grenades->value; client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] = sk_dm_start_cells->value; client->pers.inventory[ITEM_INDEX(FindItem("Slugs"))] = sk_dm_start_slugs->value; client->pers.inventory[ITEM_INDEX(FindItem("Mag Slug"))] = sk_dm_start_magslugs->value; client->pers.inventory[ITEM_INDEX(FindItem("Trap"))] = sk_dm_start_traps->value; /* client->pers.inventory[ITEM_INDEX(FindItem("Flechettes"))] = sk_dm_start_flechettes->value; client->pers.inventory[ITEM_INDEX(FindItem("Disruptors"))] = sk_dm_start_rounds->value; client->pers.inventory[ITEM_INDEX(FindItem("Prox"))] = sk_dm_start_prox->value; client->pers.inventory[ITEM_INDEX(FindItem("Tesla"))] = sk_dm_start_tesla->value; client->pers.inventory[ITEM_INDEX(FindItem("Shocksphere"))] = sk_dm_start_shocksphere->value; */ client->pers.inventory[ITEM_INDEX(FindItem("Shotgun"))] = sk_dm_start_shotgun->value; client->pers.inventory[ITEM_INDEX(FindItem("Super Shotgun"))] = sk_dm_start_sshotgun->value; client->pers.inventory[ITEM_INDEX(FindItem("Machinegun"))] = sk_dm_start_machinegun->value; client->pers.inventory[ITEM_INDEX(FindItem("Chaingun"))] = sk_dm_start_chaingun->value; client->pers.inventory[ITEM_INDEX(FindItem("Grenade Launcher"))] = sk_dm_start_grenadelauncher->value; client->pers.inventory[ITEM_INDEX(FindItem("Rocket Launcher"))] = sk_dm_start_rocketlauncher->value; client->pers.inventory[ITEM_INDEX(FindItem("HyperBlaster"))] = sk_dm_start_hyperblaster->value; client->pers.inventory[ITEM_INDEX(FindItem("Railgun"))] = sk_dm_start_railgun->value; client->pers.inventory[ITEM_INDEX(FindItem("BFG10K"))] = sk_dm_start_bfg->value; client->pers.inventory[ITEM_INDEX(FindItem("Ionripper"))] = sk_dm_start_ionripper->value; client->pers.inventory[ITEM_INDEX(FindItem("Phalanx"))] = sk_dm_start_phalanx->value; /* client->pers.inventory[ITEM_INDEX(FindItem("ETF Rifle"))] = sk_dm_start_etfrifle->value; client->pers.inventory[ITEM_INDEX(FindItem("Prox Launcher"))] = sk_dm_start_proxlauncher->value; client->pers.inventory[ITEM_INDEX(FindItem("Plasma Beam"))] = sk_dm_start_plasmabeam->value; client->pers.inventory[ITEM_INDEX(FindItem("Disintegrator"))] = sk_dm_start_disruptor->value; client->pers.inventory[ITEM_INDEX(FindItem("Chainfist"))] = sk_dm_start_chainfist->value; client->pers.inventory[ITEM_INDEX(FindItem("Shockwave"))] = sk_dm_start_shockwave->value; client->pers.inventory[ITEM_INDEX(FindItem(PLASMA_PICKUP_NAME))] = sk_dm_start_plasmarifle->value; */ SwitchToBestStartWeapon (client); } } /* ============== InitClientPersistant This is only called when the game first initializes in single player, but is called after each death and level change in deathmatch ============== */ void InitClientPersistant (gclient_t *client) { // gitem_t *item; memset (&client->pers, 0, sizeof(client->pers)); SelectStartWeapon (client); client->pers.health = 100; if (deathmatch->value) client->pers.max_health = sk_max_health_dm->value; else client->pers.max_health = sk_max_health->value; client->pers.max_fc_health = sk_max_foodcube_health->value; client->pers.max_bullets = sk_max_bullets->value; // was 200 client->pers.max_shells = sk_max_shells->value; // was 100 client->pers.max_rockets = sk_max_rockets->value; // was 50 client->pers.max_grenades = sk_max_grenades->value; // was 50 client->pers.max_cells = sk_max_cells->value; // was 200 client->pers.max_slugs = sk_max_slugs->value; // was 50 // RAFAEL client->pers.max_magslug = sk_max_magslugs->value; // was 50 client->pers.max_trap = sk_max_traps->value; // was 5 // Knightmare added client->pers.max_armor = 200; client->pers.connected = true; // Kngihtmare- custom client colors Vector4Set (client->pers.color1, 255, 255, 255, 0); Vector4Set (client->pers.color2, 255, 255, 255, 0); } void InitClientResp (gclient_t *client) { //ZOID int ctf_team = client->resp.ctf_team; //ZOID memset (&client->resp, 0, sizeof(client->resp)); //ZOID client->resp.ctf_team = ctf_team; //ZOID client->resp.enterframe = level.framenum; client->resp.coop_respawn = client->pers; //ZOID if (ctf->value && client->resp.ctf_team < CTF_TEAM1) CTFAssignTeam(client); //ZOID } /* ================== SaveClientData Some information that should be persistant, like health, is still stored in the edict structure, so it needs to be mirrored out to the client structure before all the edicts are wiped. ================== */ void SaveClientData (void) { int i; edict_t *ent; for (i=0 ; iinuse) continue; game.clients[i].pers.health = ent->health; game.clients[i].pers.max_health = ent->max_health; // game.clients[i].pers.powerArmorActive = (ent->flags & (FL_POWER_SHIELD|FL_POWER_SCREEN)); game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_SHIELD|FL_POWER_SCREEN)); if (coop->value) game.clients[i].pers.score = ent->client->resp.score; } } void FetchClientEntData (edict_t *ent) { ent->health = ent->client->pers.health; ent->max_health = ent->client->pers.max_health; // if (ent->client->pers.powerArmorActive) // ent->flags |= FL_POWER_ARMOR; ent->flags |= ent->client->pers.savedFlags; if (coop->value) ent->client->resp.score = ent->client->pers.score; } /* ======================================================================= SelectSpawnPoint ======================================================================= */ /* ================ PlayersRangeFromSpot Returns the distance to the nearest player from the given spot ================ */ float PlayersRangeFromSpot (edict_t *spot) { edict_t *player; float bestplayerdistance; vec3_t v; int n; float playerdistance; bestplayerdistance = 9999999; for (n = 1; n <= maxclients->value; n++) { player = &g_edicts[n]; if (!player->inuse) continue; if (player->health <= 0) continue; VectorSubtract (spot->s.origin, player->s.origin, v); playerdistance = VectorLength (v); if (playerdistance < bestplayerdistance) bestplayerdistance = playerdistance; } return bestplayerdistance; } /* ================ SelectRandomDeathmatchSpawnPoint go to a random point, but NOT the two points closest to other players ================ */ edict_t *SelectRandomDeathmatchSpawnPoint (void) { edict_t *spot, *spot1, *spot2; int count = 0; int selection; float range, range1, range2; spot = NULL; range1 = range2 = 99999; spot1 = spot2 = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { count++; range = PlayersRangeFromSpot(spot); if (range < range1) { range1 = range; spot1 = spot; } else if (range < range2) { range2 = range; spot2 = spot; } } if (!count) return NULL; if (count <= 2) { spot1 = spot2 = NULL; } else count -= 2; selection = rand() % count; spot = NULL; do { spot = G_Find (spot, FOFS(classname), "info_player_deathmatch"); if (spot == spot1 || spot == spot2) selection++; } while(selection--); return spot; } /* ================ SelectFarthestDeathmatchSpawnPoint ================ */ edict_t *SelectFarthestDeathmatchSpawnPoint (void) { edict_t *bestspot; float bestdistance, bestplayerdistance; edict_t *spot; spot = NULL; bestspot = NULL; bestdistance = 0; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { bestplayerdistance = PlayersRangeFromSpot (spot); if (bestplayerdistance > bestdistance) { bestspot = spot; bestdistance = bestplayerdistance; } } if (bestspot) { return bestspot; } // if there is a player just spawned on each and every start spot // we have no choice to turn one into a telefrag meltdown spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch"); return spot; } edict_t *SelectDeathmatchSpawnPoint (void) { if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST) return SelectFarthestDeathmatchSpawnPoint (); else return SelectRandomDeathmatchSpawnPoint (); } edict_t *SelectCoopSpawnPoint (edict_t *ent) { int index; edict_t *spot = NULL; char *target; index = ent->client - game.clients; // player 0 starts in normal player spawn point if (!index) return NULL; spot = NULL; // assume there are four coop spots at each spawnpoint while (1) { spot = G_Find (spot, FOFS(classname), "info_player_coop"); if (!spot) return NULL; // we didn't have enough... target = spot->targetname; if (!target) target = ""; if ( Q_stricmp(game.spawnpoint, target) == 0 ) { // this is a coop spawn point for one of the clients here index--; if (!index) return spot; // this is it } } return spot; } /* =========== SelectSpawnPoint Chooses a player start, deathmatch start, coop start, etc ============ */ void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles) { edict_t *spot = NULL; if (deathmatch->value) //ZOID if (ctf->value) spot = SelectCTFSpawnPoint(ent); else //ZOID spot = SelectDeathmatchSpawnPoint (); else if (coop->value) spot = SelectCoopSpawnPoint (ent); // find a single player start spot if (!spot) { while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL) { if (!game.spawnpoint[0] && !spot->targetname) break; if (!game.spawnpoint[0] || !spot->targetname) continue; if (Q_stricmp(game.spawnpoint, spot->targetname) == 0) break; } if (!spot) { if (!game.spawnpoint[0]) { // there wasn't a spawnpoint without a target, so use any spot = G_Find (spot, FOFS(classname), "info_player_start"); } if (!spot) gi.error ("Couldn't find spawn point %s\n", game.spawnpoint); } } VectorCopy (spot->s.origin, origin); if (ent->svflags & SVF_MONSTER) origin[2] += 32; else origin[2] += 9; VectorCopy (spot->s.angles, angles); } //====================================================================== void InitBodyQue (void) { int i; edict_t *ent; level.body_que = 0; for (i=0; iclassname = "bodyque"; } } void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int n; if (self->health < -40) { gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n= 0; n < 4; n++) ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); self->s.origin[2] -= 48; ThrowClientHead (self, damage); self->s.frame = 0; self->takedamage = DAMAGE_YES;//DAMAGE_NO; self->solid = SOLID_NOT; } } void CopyToBodyQue (edict_t *ent) { edict_t *body; // grab a body que and cycle to the next one body = &g_edicts[(int)maxclients->value + level.body_que + 1]; level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE; // FIXME: send an effect on the removed body gi.unlinkentity (ent); gi.unlinkentity (body); body->s = ent->s; body->s.number = body - g_edicts; //強引にフレームセット if (body->s.modelindex == skullindex || body->s.modelindex == headindex) body->s.frame = 0; body->svflags = ent->svflags; VectorCopy (ent->mins, body->mins); VectorCopy (ent->maxs, body->maxs); VectorCopy (ent->absmin, body->absmin); VectorCopy (ent->absmax, body->absmax); VectorCopy (ent->size, body->size); // body->solid = SOLID_NOT;//ent->solid; body->clipmask = 0;//ent->clipmask; body->owner = ent->owner; body->movetype = MOVETYPE_TOSS;//ent->movetype; body->die = body_die; if (ent->health < -40) { body->takedamage = DAMAGE_NO;//DAMAGE_YES; body->solid = SOLID_NOT; } else { body->solid = ent->solid; body->takedamage = DAMAGE_YES; } gi.linkentity (body); } void respawn (edict_t *self) { if (deathmatch->value || coop->value) { // spectator's don't leave bodies if (self->movetype != MOVETYPE_NOCLIP) CopyToBodyQue (self); self->svflags &= ~SVF_NOCLIENT; PutClientInServer (self); // add a teleportation effect self->s.event = EV_PLAYER_TELEPORT; // hold in place briefly self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; self->client->ps.pmove.pm_time = 14; self->client->respawn_time = level.time; return; } // restart the entire server gi.AddCommandString ("menu_loadgame\n"); } /* * only called when pers.spectator changes * note that resp.spectator should be the opposite of pers.spectator here */ void spectator_respawn (edict_t *ent) { int i, numspec; // if the user wants to become a spectator, make sure he doesn't // exceed max_spectators if (ent->client->pers.spectator) { char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator"); if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n"); ent->client->pers.spectator = false; gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 0\n"); gi.unicast(ent, true); return; } // count spectators for (i = 1, numspec = 0; i <= maxclients->value; i++) if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator) numspec++; if (numspec >= maxspectators->value) { gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full."); ent->client->pers.spectator = false; // reset his spectator var gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 0\n"); gi.unicast(ent, true); return; } } else { // he was a spectator and wants to join the game // he must have the right password char *value = Info_ValueForKey (ent->client->pers.userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n"); ent->client->pers.spectator = true; gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 1\n"); gi.unicast(ent, true); return; } } // clear client on respawn ent->client->resp.score = ent->client->pers.score = 0; ent->svflags &= ~SVF_NOCLIENT; PutClientInServer (ent); // add a teleportation effect if (!ent->client->pers.spectator) { // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); // hold in place briefly ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; ent->client->ps.pmove.pm_time = 14; } ent->client->respawn_time = level.time; if (ent->client->pers.spectator) gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname); else gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname); } //============================================================== /* =========== PutClientInServer Called when a player connects to a server or respawns in a deathmatch. ============ */ // ### Hentai ### BEGIN void ShowGun(edict_t *ent); // ### Hentai ### END void PutClientInServer (edict_t *ent) { vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; client_persistant_t saved; client_respawn_t resp; zgcl_t zgcl; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client SelectSpawnPoint (ent, spawn_origin, spawn_angles); index = ent-g_edicts-1; client = ent->client; // deathmatch wipes most client data every spawn if (deathmatch->value) { char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant (client); ClientUserinfoChanged (ent, userinfo); } else if (coop->value) { int n; char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); // this is kind of ugly, but it's how we want to handle keys in coop for (n = 0; n < MAX_ITEMS; n++) { if (itemlist[n].flags & IT_KEY) resp.coop_respawn.inventory[n] = client->pers.inventory[n]; } client->pers = resp.coop_respawn; ClientUserinfoChanged (ent, userinfo); if (resp.score > client->pers.score) client->pers.score = resp.score; } else { memset (&resp, 0, sizeof(resp)); } // clear everything but the persistant data saved = client->pers; memcpy (&zgcl,&client->zc,sizeof(zgcl_t)); memset (client, 0, sizeof(*client)); memcpy (&client->zc,&zgcl,sizeof(zgcl_t)); client->pers = saved; if (client->pers.health <= 0) InitClientPersistant (client); client->resp = resp; // copy some data from the client to the entity FetchClientEntData (ent); // clear entity values ent->groundentity = NULL; ent->client = &game.clients[index]; ent->takedamage = DAMAGE_AIM; ent->movetype = MOVETYPE_WALK; ent->viewheight = 22; ent->inuse = true; ent->classname = "player"; ent->mass = 200; ent->solid = SOLID_BBOX; ent->deadflag = DEAD_NO; ent->air_finished = level.time + 12; ent->clipmask = MASK_PLAYERSOLID; ent->model = "players/male/tris.md2"; ent->pain = player_pain; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->svflags &= ~SVF_DEADMONSTER; ent->svflags &= ~SVF_MONSTER; //ponko ent->client->zc.aiming = 0; ent->client->zc.distance = 90; //ponko VectorCopy (mins, ent->mins); VectorCopy (maxs, ent->maxs); VectorClear (ent->velocity); // clear playerstate values memset (&ent->client->ps, 0, sizeof(client->ps)); client->ps.pmove.origin[0] = spawn_origin[0]*8; client->ps.pmove.origin[1] = spawn_origin[1]*8; client->ps.pmove.origin[2] = spawn_origin[2]*8; //ZOID client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; //ZOID if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { client->ps.fov = 90; } else { client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov")); if (client->ps.fov < 1) client->ps.fov = 90; else if (client->ps.fov > 160) client->ps.fov = 160; } client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); // Knightmare- Server-side speed control stuff #ifdef KMQUAKE2_ENGINE_MOD client->ps.maxspeed = player_max_speed->value; client->ps.duckspeed = player_crouch_speed->value; client->ps.accel = player_accel->value; client->ps.stopspeed = player_stopspeed->value; #endif // clear entity state values ent->s.effects = 0; ent->s.skinnum = ent - g_edicts - 1; ent->s.modelindex = MAX_MODELS-1; // will use the skin specified model // ent->s.modelindex2 = MAX_MODELS-1; // custom gun model ShowGun(ent); // ### Hentai ### special gun model ent->s.frame = 0; VectorCopy (spawn_origin, ent->s.origin); ent->s.origin[2] += 1; // make sure off ground VectorCopy (ent->s.origin, ent->s.old_origin); // set the delta angle for (i=0 ; i<3 ; i++) client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); ent->s.angles[PITCH] = 0; ent->s.angles[YAW] = spawn_angles[YAW]; ent->s.angles[ROLL] = 0; VectorCopy (ent->s.angles, client->ps.viewangles); VectorCopy (ent->s.angles, client->v_angle); // spawn a spectator if (client->pers.spectator) { client->chase_target = NULL; client->resp.spectator = true; ent->movetype = MOVETYPE_NOCLIP; ent->solid = SOLID_NOT; ent->svflags |= SVF_NOCLIENT; ent->client->ps.gunindex = 0; gi.linkentity (ent); return; } else client->resp.spectator = false; //ZOID if (ctf->value) { if (CTFStartClient(ent)) return; } //ZOID //ponpoko /* if (hokuto->value) { if (ZigockStartClient(ent)) return; }*/ //ponpoko if (!KillBox (ent)) { // could't spawn in? } gi.linkentity (ent); // force the current weapon up client->newweapon = client->pers.weapon; ChangeWeapon (ent); } /* ===================== ClientBeginDeathmatch A client has just connected to the server in deathmatch mode, so clear everything out before starting them. ===================== */ void ClientBeginDeathmatch (edict_t *ent) { G_InitEdict (ent); InitClientResp (ent->client); // locate ent at a spawn point PutClientInServer (ent); // zgcl clear // memset (&ent->client->zc,0,sizeof(zgcl_t)); // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); gi.centerprintf(ent,ClientMessage); // make sure all view stuff is valid ClientEndServerFrame (ent); } /* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the game. This will happen every level load. ============ */ void ClientBegin (edict_t *ent) { int i; ent->client = game.clients + (ent - g_edicts - 1); if (deathmatch->value) { ClientBeginDeathmatch (ent); return; } // if there is already a body waiting for us (a loadgame), just // take it, otherwise spawn one from scratch if (ent->inuse == true) { // the client has cleared the client side viewangles upon // connecting to the server, which is different than the // state when the game is saved, so we need to compensate // with deltaangles for (i=0 ; i<3 ; i++) ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]); } else { // a spawn point will completely reinitialize the entity // except for the persistant data that was initialized at // ClientConnect() time G_InitEdict (ent); ent->classname = "player"; InitClientResp (ent->client); PutClientInServer (ent); } if (level.intermissiontime) { MoveClientToIntermission (ent); } else { // send effect if in a multiplayer game if (game.maxclients > 1) { gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); } } // make sure all view stuff is valid ClientEndServerFrame (ent); } /* =========== ClientUserInfoChanged called whenever the player updates a userinfo variable. The game can override any of the settings in place (forcing skins or names, etc) before copying it off. ============ */ void ClientUserinfoChanged (edict_t *ent, char *userinfo) { char *s; int playernum; // check for malformed or illegal info strings if (!Info_Validate(userinfo)) { Com_strcpy (userinfo, MAX_INFO_STRING, "\\name\\badinfo\\skin\\male/grunt"); // userinfo is always length of MAX_INFO_STRING } // set name s = Info_ValueForKey (userinfo, "name"); strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1); // set spectator s = Info_ValueForKey (userinfo, "spectator"); // spectators are only supported in deathmatch if (deathmatch->value && !ctf->value && *s && strcmp(s, "0")) ent->client->pers.spectator = true; else ent->client->pers.spectator = false; // set skin s = Info_ValueForKey (userinfo, "skin"); playernum = ent-g_edicts-1; // combine name and skin into a configstring //ZOID if (ctf->value) CTFAssignSkin(ent, s); else //ZOID gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) ); // fov if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { ent->client->ps.fov = 90; } else { ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov")); if (ent->client->ps.fov < 1) ent->client->ps.fov = 90; else if (ent->client->ps.fov > 160) ent->client->ps.fov = 160; } // handedness s = Info_ValueForKey (userinfo, "hand"); if (strlen(s)) { ent->client->pers.hand = atoi(s); } // Knightmare- custom colors s = Info_ValueForKey (userinfo, "color1"); if (strlen(s) >= 6) { if ( Com_ParseColorString (s, ent->client->pers.color1) ) ent->client->pers.color1[3] = 255; // mark as set } s = Info_ValueForKey (userinfo, "color2"); if (strlen(s) >= 6) { if ( Com_ParseColorString (s, ent->client->pers.color2) ) ent->client->pers.color2[3] = 255; // mark as set } // save off the userinfo in case we want to check something later strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1); } /* =========== ClientConnect Called when a player begins connecting to the server. The game can refuse entrance to a client by returning false. If the client is allowed, the connection process will continue and eventually get to ClientBegin() Changing levels will NOT cause this to be called again, but loadgames will. ============ */ qboolean ClientConnect (edict_t *ent, char *userinfo) { char *value; // check to see if they are on the banned IP list /* value = Info_ValueForKey (userinfo, "ip"); // check for a password value = Info_ValueForKey (userinfo, "password"); if (strcmp(password->string, value) != 0) return false;*/ // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if (SV_FilterPacket(value)) { Info_SetValueForKey(userinfo, "rejmsg", "Banned."); return false; } // check for a spectator value = Info_ValueForKey (userinfo, "spectator"); if (deathmatch->value && *value && !ctf->value && strcmp(value, "0")) { int i, numspec; if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); return false; } // count spectators for (i = numspec = 0; i < maxclients->value; i++) if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator) numspec++; if (numspec >= maxspectators->value) { Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); return false; } } else { // check for a password value = Info_ValueForKey (userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); return false; } } // they can connect ent->client = game.clients + (ent - g_edicts - 1); // if there is already a body waiting for us (a loadgame), just // take it, otherwise spawn one from scratch if (ent->inuse == false) { // clear the respawning variables //ZOID -- force team join ent->client->resp.ctf_team = -1; //ZOID InitClientResp (ent->client); if (!game.autosaved || !ent->client->pers.weapon) InitClientPersistant (ent->client); } ClientUserinfoChanged (ent, userinfo); if (game.maxclients > 1) gi.dprintf ("%s connected\n", ent->client->pers.netname); ent->client->pers.connected = true; return true; } /* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. ============ */ void ClientDisconnect (edict_t *ent) { int playernum; if (!ent->client) return; gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname); //ZOID CTFDeadDropFlag (ent); CTFDeadDropTech (ent); //ZOID // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGOUT); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.unlinkentity (ent); ent->s.modelindex = 0; ent->solid = SOLID_NOT; ent->inuse = false; ent->classname = "disconnected"; ent->client->pers.connected = false; playernum = ent-g_edicts-1; gi.configstring (CS_PLAYERSKINS+playernum, ""); } //============================================================== edict_t *pm_passent; // pmove doesn't need to know about passent and contentmask trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { if (pm_passent->health > 0) return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); else return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID); } unsigned CheckBlock (void *b, int c) { int v,i; v = 0; for (i=0 ; is, sizeof(pm->s)); c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd)); Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2); } /* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame. ============== */ //edict_t *GetBotFlag1(); //edict_t *GetBotFlag2(); void SpawnExtra(vec3_t position,char *classname); void Get_Position ( edict_t *ent, vec3_t position ) { float yaw,pitch; yaw = ent->s.angles[YAW]; pitch = ent->s.angles[PITCH]; yaw = yaw * M_PI * 2 / 360; pitch = pitch * M_PI * 2 / 360; position[0] = cos(yaw) * cos(pitch); position[1] = sin(yaw) * cos(pitch); position[2] = -sin(pitch); } void ChainPodThink (edict_t *ent) { if (ent->owner == NULL )return; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BFG_LASER); gi.WritePosition (ent->s.origin); gi.WritePosition (ent->owner->s.origin); gi.multicast (ent->s.origin, MULTICAST_PHS); if (ent->target_ent != NULL) { if (Q_stricmp (ent->target_ent->classname, "item_flag_team2") == 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BFG_LASER); gi.WritePosition (ent->s.origin); gi.WritePosition (ent->target_ent->s.origin); gi.multicast (ent->s.origin, MULTICAST_PHS); } } ent->nextthink = level.time + FRAMETIME * 10; } qboolean Bot_traceX (edict_t *ent,edict_t *other); qboolean ChkTFlg(); qboolean TraceX (edict_t *ent,vec3_t p2) { trace_t rs_trace; vec3_t v1,v2; int contents; contents = CONTENTS_SOLID | CONTENTS_WINDOW; if (!(ent->svflags & SVF_MONSTER)) { if (ent->client->zc.waterstate) { VectorCopy(ent->mins,v1); VectorCopy(ent->maxs,v2); /* v1[0] -= 4; v1[1] -= 4; v2[0] += 4; v2[1] += 4;*/ } else if (!(ent->client->ps.pmove.pm_flags & PMF_DUCKED)) { VectorSet(v1,-16,-16,-4); VectorSet(v2,16,16,32); } else { // VectorCopy(ent->mins,v1); // VectorCopy(ent->maxs,v2); VectorSet(v1,-4,-4,-4); VectorSet(v2,4,4,4); } } else { VectorSet(v1,0,0,0); VectorSet(v2,0,0,0); contents |= CONTENTS_LAVA | CONTENTS_SLIME; } rs_trace = gi.trace (ent->s.origin, v1, v2, p2 ,ent, contents ); if (rs_trace.fraction == 1.0 && !rs_trace.allsolid && !rs_trace.startsolid ) return true; if (ent->client->zc.route_trace && rs_trace.ent && (ent->svflags & SVF_MONSTER)) { //if (!rs_trace.ent->targetname) if (!Q_stricmp(rs_trace.ent->classname, "func_door")) { if (rs_trace.ent->moveinfo.state == PSTATE_UP) return true; else return false; } // if (!Q_stricmp(rs_trace.ent->classname, "func_train")) return true; } return false; } void ClientThink (edict_t *ent, usercmd_t *ucmd) { gclient_t *client; edict_t *other; edict_t *ground; int i, j, k,l,oldwaterstate; byte impulse; pmove_t pm; float ground_speed; vec3_t min,max,v,vv; float x; trace_t rs_trace; static edict_t *old_ground; static qboolean wasground; // Knightmare- determine if client-side prediction should be on ground = ent->groundentity; if (ground && (ground->movetype == MOVETYPE_PUSH) && (ground != world) && ground->turn_rider) ground_speed = VectorLength(ground->velocity); else ground_speed = 0; if ( (ent->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL) || (ent->client->chase_target != NULL) || (ground_speed > 0) ) ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; else ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; // end Knightmare // Knightmare- Server-side speed control stuff #ifdef KMQUAKE2_ENGINE_MOD ent->client->ps.maxspeed = player_max_speed->value; ent->client->ps.duckspeed = player_crouch_speed->value; ent->client->ps.accel = player_accel->value; ent->client->ps.stopspeed = player_stopspeed->value; #endif impulse = ucmd->impulse; if (impulse == 1) gi.bprintf(PRINT_HIGH,"%f\n",ent->s.origin[2]); //-------------------------------------------------------------------------------------- //Target check if (ent->client->zc.first_target) { if (!ent->client->zc.first_target->inuse) ent->client->zc.first_target = NULL; else if (!ent->client->zc.first_target->deadflag) ent->client->zc.first_target = NULL; } //-------------------------------------------------------------------------------------- //get JumpMax if (JumpMax == 0) { x = VEL_BOT_JUMP - ent->gravity * sv_gravity->value * FRAMETIME; JumpMax = 0; while(1) { JumpMax += x * FRAMETIME; x -= ent->gravity * sv_gravity->value * FRAMETIME; if ( x < 0 ) break; } } //route nodeput j = 0; if (ent->client->ctf_grapple && ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) j = 1; if (!j && chedit->value && CurrentIndex < MAXNODES && !ent->deadflag && ent == &g_edicts[1]) { if (targetindex > 0) { if (ent->target_ent == NULL) return; other = ent->target_ent; if (!TraceX(ent,other->s.origin)) { k = 0; i = other->client->zc.routeindex; while(1) { if (i + 1 >= CurrentIndex) { j = Route[i + 1].state; if (j == GRS_ONTRAIN) if (Route[i + 1].ent->trainteam) break; Get_RouteOrigin(i + 1,v); if (!TraceX(ent,other->s.origin)) { break; } } else break; i++; } Get_RouteOrigin(i + 1,v); VectorCopy(v,ent->s.origin); } VectorSubtract(other->s.origin,ent->s.origin,vv); ent->client->ps.viewangles[YAW] = Get_yaw(vv); ent->client->v_angle[YAW] = ent->client->ps.viewangles[YAW]; ent->client->ps.viewangles[PITCH] = Get_pitch(vv); ent->client->v_angle[PITCH] = ent->client->ps.viewangles[PITCH]; ent->viewheight = 22; ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION ; ent->client->ps.pmove.pm_flags = PM_FREEZE; gi.linkentity(ent); return; } oldwaterstate = ent->client->zc.waterstate; Get_WaterState(ent); i = false; l = GRS_NORMAL; if (CurrentIndex > 0) Get_RouteOrigin(CurrentIndex - 1,v); if (!Route[CurrentIndex].index) { VectorCopy(ent->s.origin,v); old_ground = ent->groundentity; //gi.bprintf(PRINT_HIGH,"1\n"); if (ent->groundentity) i = true; } else if (!TraceX(ent,v) /*&& ent->groundentity*/) { VectorCopy(ent->s.old_origin,v); //gi.bprintf(PRINT_HIGH,"2\n"); i = 3; if (0/*ent->groundentity*/) { if (ent->groundentity->classname[0] == 'f') i = false; } } else if (ent->client->zc.waterstate != oldwaterstate) { i = true; if (ent->groundentity ) { if (!Q_stricmp(ent->groundentity->classname, "func_train") || !Q_stricmp(ent->groundentity->classname, "func_plat") || !Q_stricmp(ent->groundentity->classname, "func_plat2") || !Q_stricmp(ent->groundentity->classname, "func_door")) i = false; } if (ent->client->zc.waterstate > oldwaterstate) VectorCopy(ent->s.origin,v); else VectorCopy(ent->s.old_origin,v); //gi.bprintf(PRINT_HIGH,"5\n"); } else if (fabs(v[2] - ent->s.origin[2]) > 20) { if (ent->groundentity && ent->waterlevel < 2) { k = true; if (k) { VectorCopy(ent->s.origin,v); //gi.bprintf(PRINT_HIGH,"3\n"); i = true; } } } else if (((/*ent->velocity[2] > 10 &&*/ !ent->groundentity && wasground == true) || (/*ent->velocity[2] < -0.5 &&*/ ent->groundentity && wasground == false)) && Route[CurrentIndex - 1].state <= GRS_ITEMS) { j = false; k = true;//false; VectorCopy(ent->s.old_origin,v); v[2] -= 2; rs_trace = gi.trace(ent->s.old_origin,ent->mins ,ent->maxs, v ,ent,MASK_PLAYERSOLID); if (rs_trace.fraction != 1.0) j = true; if (old_ground) { if (!Q_stricmp(old_ground->classname, "func_train") || !Q_stricmp(old_ground->classname, "func_plat") || !Q_stricmp(old_ground->classname, "func_plat2") || !Q_stricmp(old_ground->classname, "func_door")) k = false; } if (!ent->groundentity /*&& j*/&& wasground == true && k) { VectorCopy(ent->s.old_origin,v); //gi.bprintf(PRINT_HIGH,"6\n"); i = true; } else if (ent->groundentity /*&& !j*/&& wasground == false && k) { // VectorSubtract(ent->s.origin) VectorCopy(ent->s.origin,v); //gi.bprintf(PRINT_HIGH,"7\n"); i = true; } } else if (Route[CurrentIndex-1].index > 1) { k = true; if (0/*old_ground*/) { if (!Q_stricmp(old_ground->classname, "func_train") || !Q_stricmp(old_ground->classname, "func_plat") || !Q_stricmp(old_ground->classname, "func_plat2") || !Q_stricmp(old_ground->classname, "func_door")) k = false; } Get_RouteOrigin(CurrentIndex - 1,min); Get_RouteOrigin(CurrentIndex - 2,max); VectorSubtract(min,max,v); x = Get_yaw(v); VectorSubtract(ent->s.origin,/*Route[CurrentIndex-1].Pt*/ent->s.old_origin,v); if (VectorLength(v) > 0 && Get_vec_yaw(v,x) > 45 && k ) { VectorCopy(ent->s.old_origin,v); //gi.bprintf(PRINT_HIGH,"8\n"); i = true; } } if (ent->groundentity) { if (ent->groundentity != old_ground) { other = old_ground; old_ground = ent->groundentity; if ( !Q_stricmp(old_ground->classname, "func_plat") || !Q_stricmp(old_ground->classname, "func_plat2") ) { if (old_ground->union_ent) { if (old_ground->union_ent->inuse && old_ground->union_ent->classname[0] == 'R') { //gi.bprintf(PRINT_HIGH,"plat put\n"); VectorCopy(old_ground->monsterinfo.last_sighting,v); l = GRS_ONPLAT; i = 2; } } } else if (!Q_stricmp(old_ground->classname, "func_train")) { if (old_ground->union_ent) { if (old_ground->union_ent->inuse && old_ground->union_ent->classname[0] == 'R') { VectorCopy(old_ground->monsterinfo.last_sighting,v); l = GRS_ONTRAIN; i = 2; } } } else if (!Q_stricmp(old_ground->classname, "func_door")) { k = false; if (old_ground->targetname && old_ground->union_ent) { if (TraceX(ent,old_ground->union_ent->s.origin) && fabs(ent->s.origin[2] - old_ground->union_ent->s.origin[2]) < JumpMax) { VectorCopy(old_ground->monsterinfo.last_sighting,v); l = GRS_ONDOOR; i = 2; } else k = true; } else k = true; if (k && i) { i = 2; old_ground = other; } } } } if (old_ground) { if (old_ground->classname[0] == 'f' && i != 2) { if (!Q_stricmp(old_ground->classname, "func_train") || !Q_stricmp(old_ground->classname, "func_plat") || !Q_stricmp(old_ground->classname, "func_plat2") || !Q_stricmp(old_ground->classname, "func_door")) i = false; } } if (Route[CurrentIndex-1].index > 0 && i == true) { Get_RouteOrigin(CurrentIndex - 1,max); VectorSubtract(max,v,vv); if (VectorLength(vv) <= 32 ) i = false; } if (l == GRS_ONTRAIN || l == GRS_ONPLAT || l == GRS_ONDOOR) { if (Route[CurrentIndex - 1].ent == old_ground) i = false; } if (i) { if (l == GRS_NORMAL && ent->groundentity) { if (!Q_stricmp(old_ground->classname, "func_rotating")) { l = GRS_ONROTATE; // gi.bprintf(PRINT_HIGH,"On Rotate\n"); } } VectorCopy(v,Route[CurrentIndex].Pt); Route[CurrentIndex].state = l; if (l > GRS_ITEMS && l <= GRS_ONTRAIN) Route[CurrentIndex].ent = old_ground; else if (l == GRS_ONDOOR) Route[CurrentIndex].ent = old_ground; if (l == GRS_ONTRAIN && old_ground->trainteam && old_ground->target_ent) { if (!Q_stricmp(old_ground->target_ent->classname,"path_corner")) VectorCopy(old_ground->target_ent->s.origin,Route[CurrentIndex].Tcourner); //gi.bprintf(PRINT_HIGH,"get chain\n"); } //when normal or items if (++CurrentIndex < MAXNODES) { gi.bprintf(PRINT_HIGH,"Last %i pod(s).\n",MAXNODES - CurrentIndex); memset(&Route[CurrentIndex],0,sizeof(route_t)); //initialize Route[CurrentIndex].index = Route[CurrentIndex - 1].index +1; } } // VectorCopy(ent->s.origin,old_origin); if (ent->groundentity != NULL) wasground = true; else wasground = false; } //-------------------------------------- level.current_entity = ent; client = ent->client; if (level.intermissiontime) { client->ps.pmove.pm_type = PM_FREEZE; // can exit intermission after five seconds if (level.time > level.intermissiontime + 5.0 && (ucmd->buttons & BUTTON_ANY) ) level.exitintermission = true; return; } pm_passent = ent; if (ent->client->chase_target) { client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); } else { // set up for pmove memset (&pm, 0, sizeof(pm)); if (ent->movetype == MOVETYPE_NOCLIP) client->ps.pmove.pm_type = PM_SPECTATOR; else if (ent->s.modelindex != MAX_MODELS-1) client->ps.pmove.pm_type = PM_GIB; else if (ent->deadflag) client->ps.pmove.pm_type = PM_DEAD; else client->ps.pmove.pm_type = PM_NORMAL; client->ps.pmove.gravity = sv_gravity->value; pm.s = client->ps.pmove; for (i=0 ; i<3 ; i++) { pm.s.origin[i] = ent->s.origin[i]*8; pm.s.velocity[i] = ent->velocity[i]*8; } if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) { pm.snapinitial = true; // gi.dprintf ("pmove changed!\n"); } pm.cmd = *ucmd; pm.trace = PM_trace; // adds default parms pm.pointcontents = gi.pointcontents; // perform a pmove gi.Pmove (&pm); // save results of pmove client->ps.pmove = pm.s; client->old_pmove = pm.s; for (i=0 ; i<3 ; i++) { ent->s.origin[i] = pm.s.origin[i]*0.125; ent->velocity[i] = pm.s.velocity[i]*0.125; } VectorCopy (pm.mins, ent->mins); VectorCopy (pm.maxs, ent->maxs); client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) { gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0); PlayerNoise(ent, ent->s.origin, PNOISE_SELF); } ent->viewheight = pm.viewheight; ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; ent->groundentity = pm.groundentity; if (pm.groundentity) ent->groundentity_linkcount = pm.groundentity->linkcount; if (ent->deadflag) { client->ps.viewangles[ROLL] = 40; client->ps.viewangles[PITCH] = -15; client->ps.viewangles[YAW] = client->killer_yaw; } else { VectorCopy (pm.viewangles, client->v_angle); VectorCopy (pm.viewangles, client->ps.viewangles); } //ZOID if (client->ctf_grapple) CTFGrapplePull(client->ctf_grapple); //ZOID gi.linkentity (ent); if (ent->movetype != MOVETYPE_NOCLIP) G_TouchTriggers (ent); // touch other objects for (i=0 ; itouch) continue; other->touch (other, ent, NULL, NULL); } } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // save light level the player is standing on for // monster sighting AI ent->light_level = ucmd->lightlevel; // fire weapon from final position if needed /* if (client->latched_buttons & BUTTON_ATTACK //ZOID && ent->movetype != MOVETYPE_NOCLIP //ZOID ) { if (!client->weapon_thunk) { client->weapon_thunk = true; Think_Weapon (ent); } }*/ // fire weapon from final position if needed if (client->latched_buttons & BUTTON_ATTACK) { if (client->resp.spectator) { client->latched_buttons = 0; if (client->chase_target) { client->chase_target = NULL; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; } else GetChaseTarget(ent); } else if (!client->weapon_thunk && ent->movetype != MOVETYPE_NOCLIP) // Knightmare- restored noclip check { client->weapon_thunk = true; Think_Weapon (ent); } } if (client->resp.spectator) { if (ucmd->upmove >= 10) { if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { client->ps.pmove.pm_flags |= PMF_JUMP_HELD; if (client->chase_target) ChaseNext(ent); else GetChaseTarget(ent); } } else client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; } //ZOID //regen tech CTFApplyRegeneration(ent); //ZOID // update chase cam if being followed for (i = 1; i <= maxclients->value; i++) { other = g_edicts + i; if (other->inuse && ent->client->chase_target == other/*other->client->chase_target == ent*/) UpdateChaseCam(ent/*other*/); } } /* ============== ClientBeginServerFrame This will be called once for each server frame, before running any other entities in the world. ============== */ void ClientBeginServerFrame (edict_t *ent) { gclient_t *client; if (level.intermissiontime) return; client = ent->client; if (deathmatch->value && client->pers.spectator != client->resp.spectator && (level.time - client->respawn_time) >= 5) { spectator_respawn(ent); return; } // run weapon animations if it hasn't been done by a ucmd_t if (!client->weapon_thunk //ZOID && ent->movetype != MOVETYPE_NOCLIP //ZOID ) Think_Weapon (ent); else client->weapon_thunk = false; if (ent->deadflag) { // wait for any button just going down if ( level.time > client->respawn_time) { if (client->latched_buttons || (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) ) { respawn(ent); client->latched_buttons = 0; } } return; } // add player trail so monsters can follow if (!deathmatch->value) if (!visible (ent, PlayerTrail_LastSpot() ) ) PlayerTrail_Add (ent->s.old_origin); client->latched_buttons = 0; }