1 void info_player_start (void)
3 self.classname = "info_player_deathmatch";
6 void info_player_deathmatch (void)
10 float spawn_goodspots, spawn_badspots;
11 entity Spawn_ClassifyPoints(entity firstspot, entity playerlist, float mindist, float goodspotnum, float badspotnum)
13 local entity spot, player;
25 if (vlen(player.origin - spot.origin) < 100)
27 player = player.chain;
31 if (spawn_goodspots >= badspotnum)
33 spawn_badspots = spawn_badspots + 1;
37 if (spawn_goodspots >= goodspotnum)
39 spawn_goodspots = spawn_goodspots + 1;
41 spot = find(spot, classname, "info_player_deathmatch");
46 entity Spawn_FurthestPoint(entity firstspot, entity playerlist)
48 local entity best, spot, player;
49 local float bestrating, rating;
51 bestrating = -1000000;
60 rating = min(rating, vlen(player.origin - spot.origin));
61 player = player.chain;
63 rating = rating + random() * 16;
64 if (bestrating < rating)
69 spot = find(spot, classname, "info_player_deathmatch");
78 Finds a point to respawn
81 entity SelectSpawnPoint (void)
83 local entity spot, firstspot, playerlist;
85 spot = find (world, classname, "testplayerstart");
89 playerlist = findchain(classname, "player");
90 firstspot = find(world, classname, "info_player_deathmatch");
91 Spawn_ClassifyPoints(firstspot, playerlist, 100, 1000000, 1000000);
92 // first check if there are ANY good spots
93 if (spawn_goodspots > 0)
95 // good spots exist, there is 50/50 chance of choosing a random good
96 // spot or the furthest spot
97 // (this means that roughly every other spawn will be furthest, so you
98 // usually won't get fragged at spawn twice in a row)
100 spot = Spawn_ClassifyPoints(firstspot, playerlist, 100, min(floor(random() * spawn_goodspots), spawn_goodspots - 1), 1000000);
102 spot = Spawn_FurthestPoint(firstspot, playerlist);
106 // no good spots exist, pick a random bad spot
107 spot = Spawn_ClassifyPoints(firstspot, playerlist, 100, 1000000, min(floor(random() * spawn_badspots), spawn_badspots - 1));
111 error ("PutClientInServer: no info_player_start on level");
120 Checks if the argument string can be a valid playermodel.
121 Returns a valid one in doubt.
124 string CheckPlayerModel(string plyermodel) {
125 if( substring(plyermodel,0,14) != "models/player/") plyermodel = "models/player/marine.zym";
127 /* Possible Fixme: Check if server can open the model?
128 This would kill custom models, however. */
138 Called when a client spawns in the server
141 void PutClientInServer (void)
145 spot = SelectSpawnPoint ();
147 self.classname = "player";
148 self.iscreature = TRUE;
149 self.movetype = MOVETYPE_WALK;
150 self.solid = SOLID_SLIDEBOX;
151 self.flags = FL_CLIENT;
152 self.takedamage = DAMAGE_AIM;
154 self.health = cvar("g_balance_health_start");
155 self.armorvalue = cvar("g_balance_armor_start");
156 self.pauserotarmor_finished = time + 10;
157 self.pauserothealth_finished = time + 10;
158 self.pauseregen_finished = 0;
159 self.damageforcescale = 2;
168 self.pain_finished = 0;
169 self.strength_finished = 0;
170 self.invincible_finished = 0;
172 //self.speed_finished = 0;
173 //self.slowmo_finished = 0;
174 // players have no think function
175 self.think = SUB_Null;
178 self.deadflag = DEAD_NO;
180 self.angles = spot.angles;
181 self.angles_z = 0; // never spawn tilted even if the spot says to
182 self.fixangle = TRUE; // turn this way immediately
183 self.velocity = '0 0 0';
184 self.avelocity = '0 0 0';
185 self.punchangle = '0 0 0';
186 self.punchvector = '0 0 0';
187 self.oldvelocity = self.velocity;
192 self.playermodel = CheckPlayerModel(self.playermodel);
194 precache_model (self.playermodel);
195 setmodel (self, self.playermodel);
196 self.skin = stof(self.playerskin);
198 self.view_ofs = PL_VIEW_OFS;
199 setsize (self, PL_MIN, PL_MAX);
200 setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
201 // don't reset back to last position, even if new position is stuck in solid
202 self.oldorigin = self.origin;
204 // self.items = IT_LASER | IT_UZI| IT_SHOTGUN | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER;
205 // self.weapon = IT_UZI;
207 if (cvar("g_instagib") == 1)
210 self.switchweapon = WEP_NEX;
211 self.ammo_shells = 0;
213 self.ammo_rockets = 0;
214 self.ammo_cells = 999;
216 else if (cvar("g_rocketarena") == 1)
218 self.items = IT_ROCKET_LAUNCHER;
219 self.switchweapon = WEP_ROCKET_LAUNCHER;
220 self.ammo_shells = 0;
222 self.ammo_rockets = 999;
227 self.items = IT_LASER | IT_SHOTGUN;
228 self.switchweapon = WEP_SHOTGUN;
229 self.ammo_shells = 50;
231 self.ammo_rockets = 0;
235 self.event_damage = PlayerDamage;
237 self.statdraintime = time + 5;
238 self.button0 = self.button1 = self.button2 = self.button3 = 0;
244 CL_SpawnWeaponentity();
246 //stuffcmd(self, "chase_active 0");
254 void SetNewParms (void)
264 void SetChangeParms (void)
273 Called when a client types 'kill' in the console
276 void ClientKill (void)
278 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
285 Called when a client connects to the server
288 void ClientConnect (void)
290 bprint ("^4",self.netname);
291 bprint (" connected\n");
292 stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
293 // send prediction settings to the client
294 stuffcmd(self, strcat("cl_movement_maxspeed ", ftos(cvar("sv_maxspeed")), "\n"));
295 stuffcmd(self, strcat("cl_movement_maxairspeed ", ftos(cvar("sv_maxairspeed")), "\n"));
296 stuffcmd(self, strcat("cl_movement_accelerate ", ftos(cvar("sv_accelerate")), "\n"));
297 stuffcmd(self, strcat("cl_movement_friction ", ftos(cvar("sv_friction")), "\n"));
298 stuffcmd(self, strcat("cl_movement_stopspeed ", ftos(cvar("sv_stopspeed")), "\n"));
299 stuffcmd(self, strcat("cl_movement_jumpvelocity ", ftos(cvar("g_balance_jumpheight")), "\n"));
300 stuffcmd(self, strcat("cl_movement_stepheight ", ftos(cvar("sv_stepheight")), "\n"));
301 stuffcmd(self, strcat("cl_movement_edgefriction 0\n"));
308 Called when a client disconnects from the server
311 .entity chatbubbleentity;
312 void ClientDisconnect (void)
314 bprint ("^4",self.netname);
315 bprint (" disconnected\n");
317 if (self.chatbubbleentity)
319 remove (self.chatbubbleentity);
320 self.chatbubbleentity = world;
325 void() ChatBubbleThink =
327 self.nextthink = time;
328 if (!self.owner.modelindex || self.owner.chatbubbleentity != self)
333 setorigin(self, self.owner.origin + '0 0 15' + self.owner.maxs_z * '0 0 1');
334 if (self.owner.buttonchat && !self.owner.deadflag)
335 self.model = self.mdl;
340 void() UpdateChatBubble =
342 if (!self.modelindex)
344 // spawn a chatbubble entity if needed
345 if (!self.chatbubbleentity)
347 self.chatbubbleentity = spawn();
348 self.chatbubbleentity.owner = self;
349 self.chatbubbleentity.exteriormodeltoclient = self;
350 self.chatbubbleentity.think = ChatBubbleThink;
351 self.chatbubbleentity.nextthink = time;
352 setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr");
353 setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
354 self.chatbubbleentity.mdl = self.chatbubbleentity.model;
355 self.chatbubbleentity.model = "";
359 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
360 // added to the model skins
361 void() UpdateColorModHack =
364 c = self.clientcolors & 15;
365 // LordHavoc: only bothering to support white, green, red, yellow, blue
366 if (teamplay == 0) self.colormod = '0 0 0';
367 else if (c == 0) self.colormod = '1.00 1.00 1.00';
368 else if (c == 3) self.colormod = '0.10 1.73 0.10';
369 else if (c == 4) self.colormod = '1.73 0.10 0.10';
370 else if (c == 12) self.colormod = '1.22 1.22 0.10';
371 else if (c == 13) self.colormod = '0.10 0.10 1.73';
372 else self.colormod = '1 1 1';
379 When you press the jump key
382 void PlayerJump (void)
384 if (self.waterlevel >= 2)
386 if (self.watertype == CONTENT_WATER)
387 self.velocity_z = 200;
388 else if (self.watertype == CONTENT_SLIME)
389 self.velocity_z = 80;
391 self.velocity_z = 50;
397 if (!(self.flags & FL_ONGROUND))
400 if (!(self.flags & FL_JUMPRELEASED))
403 self.velocity_z = self.velocity_z + cvar("g_balance_jumpheight");
404 self.oldvelocity_z = self.velocity_z;
406 self.flags = self.flags - FL_ONGROUND;
407 self.flags = self.flags - FL_JUMPRELEASED;
410 void() CheckWaterJump =
412 local vector start, end;
414 // check for a jump-out-of-water
415 makevectors (self.angles);
417 start_z = start_z + 8;
419 normalize(v_forward);
420 end = start + v_forward*24;
421 traceline (start, end, TRUE, self);
422 if (trace_fraction < 1)
424 start_z = start_z + self.maxs_z - 8;
425 end = start + v_forward*24;
426 self.movedir = trace_plane_normal * -50;
427 traceline (start, end, TRUE, self);
428 if (trace_fraction == 1)
429 { // open at eye level
430 self.flags = self.flags | FL_WATERJUMP;
431 self.velocity_z = 225;
432 self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
433 self.teleport_time = time + 2; // safety net
446 void player_powerups (void)
448 self.effects = self.effects - (self.effects & (EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT));
449 if (self.items & IT_STRENGTH)
451 self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
452 if (time > self.strength_finished)
454 self.items = self.items - (self.items & IT_STRENGTH);
455 sprint(self, "^3Strength has worn off\n");
460 if (time < self.strength_finished)
462 self.items = self.items | IT_STRENGTH;
463 sprint(self, "^3Strength infuses your weapons with devestating power\n");
466 if (self.items & IT_INVINCIBLE)
468 self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
469 if (time > self.invincible_finished)
471 self.items = self.items - (self.items & IT_INVINCIBLE);
472 sprint(self, "^3Shield has worn off\n");
477 if (time < self.invincible_finished)
479 self.items = self.items | IT_INVINCIBLE;
480 sprint(self, "^3Shield surrounds you\n");
483 if (cvar("g_fullbrightplayers"))
484 self.effects = self.effects | EF_FULLBRIGHT;
487 void player_regen (void)
491 maxh = cvar("g_balance_health_stable");
492 maxa = cvar("g_balance_armor_stable");
493 if (time > self.pauserothealth_finished)
494 if (self.health > maxh)
495 self.health = bound(0, self.health + (maxh - self.health) * cvar("g_balance_health_rot") * frametime, 1000);
496 if (time > self.pauserotarmor_finished)
497 if (self.armorvalue > maxa)
498 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_rot") * frametime, 1000);
499 if (time > self.pauseregen_finished)
501 if (self.health < maxh)
502 self.health = bound(0, self.health + (maxh- self.health) * cvar("g_balance_health_regen") * frametime, 1000);
503 if (self.armorvalue < maxa)
504 self.armorvalue = bound(0, self.armorvalue + (maxa - self.armorvalue) * cvar("g_balance_armor_regen") * frametime, 1000);
512 Called every frame for each client before the physics are run
515 void PlayerPreThink (void)
523 if (intermission_running)
525 IntermissionThink (); // otherwise a button could be missed between
526 return; // the think tics
529 if (self.deadflag != DEAD_NO)
533 if (self.deadflag == DEAD_DYING)
535 if (time > self.dead_time)
536 self.deadflag = DEAD_DEAD;
538 else if (self.deadflag == DEAD_DEAD)
540 if (!self.button0 && !self.button2 && !self.button3)
541 self.deadflag = DEAD_RESPAWNABLE;
543 else if (self.deadflag == DEAD_RESPAWNABLE)
545 if (self.button0 || self.button2 || self.button3 || self.button4)
556 self.view_ofs = PL_CROUCH_VIEW_OFS;
557 setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
564 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, FALSE, self);
565 if (!trace_startsolid)
568 self.view_ofs = PL_VIEW_OFS;
569 setsize (self, PL_MIN, PL_MAX);
574 if (self.playermodel != self.model)
576 self.playermodel = CheckPlayerModel(self.playermodel);
579 precache_model (self.playermodel);
580 setmodel (self, self.playermodel);
581 setsize (self, m1, m2);
584 // Savage: Check for nameless players
585 if (strlen(self.netname) < 1) {
586 self.netname = "Player";
587 stuffcmd(self, "name Player\n");
590 if (self.skin != stof(self.playerskin))
591 self.skin = stof(self.playerskin);
595 if (self.button4 || (self.weapon == WEP_NEX && self.button3))
597 if (self.viewzoom > 0.4)
598 self.viewzoom = max (0.4, self.viewzoom - frametime * 2);
600 else if (self.viewzoom < 1.0)
601 self.viewzoom = min (1.0, self.viewzoom + frametime);
607 self.flags = self.flags | FL_JUMPRELEASED;
614 //self.angles_y=self.v_angle_y + 90; // temp
616 if (self.waterlevel == 2)
619 //if (TetrisPreFrame()) return;
626 Called every frame for each client after the physics are run
629 void PlayerPostThink (void)
633 UpdateColorModHack();
634 if (self.deadflag == DEAD_NO)
637 if (intermission_running)
638 return; // intermission or finale
640 //if (TetrisPostFrame()) return;