FS2_Open
Open source remastering of the Freespace 2 engine
scoring.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #include "ai/ai_profiles.h"
14 #include "debugconsole/console.h"
15 #include "freespace2/freespace.h"
16 #include "hud/hud.h"
17 #include "hud/hudmessage.h"
18 #include "iff_defs/iff_defs.h"
19 #include "localization/localize.h"
20 #include "mission/missionparse.h"
21 #include "network/multi.h"
22 #include "network/multi_dogfight.h"
23 #include "network/multi_pmsg.h"
24 #include "network/multi_team.h"
25 #include "network/multimsgs.h"
26 #include "network/multiutil.h"
27 #include "object/object.h"
28 #include "parse/parselo.h"
29 #include "pilotfile/pilotfile.h"
30 #include "playerman/player.h"
31 #include "ship/ship.h"
32 #include "stats/medals.h"
33 #include "stats/scoring.h"
34 #include "weapon/weapon.h"
35 
36 /*
37 // uncomment to get extra debug messages when a player scores
38 #define SCORING_DEBUG
39 */
40 // what percent of points of total damage to a ship a player has to have done to get an assist (or a kill) when it is killed
41 float Kill_percentage = 0.30f;
42 float Assist_percentage = 0.15f;
43 
44 // these tables are overwritten with the values from rank.tbl
46 
47 // scoring scale factors by skill level
49  0.2f, // very easy
50  0.4f, // easy
51  0.7f, // medium
52  1.0f, // hard
53  1.25f // insane
54 };
55 
56 void scoreing_close();
57 
59 {
60  atexit(scoreing_close);
61  char buf[MULTITEXT_LENGTH];
62  int idx, persona;
63 
64  try
65  {
66  read_file_text("rank.tbl", CF_TYPE_TABLES);
67  reset_parse();
68 
69  // parse in all the rank names
70  idx = 0;
71  skip_to_string("[RANK NAMES]");
73  while (required_string_either("#End", "$Name:")) {
74  Assert(idx < NUM_RANKS);
75  required_string("$Name:");
76  stuff_string(Ranks[idx].name, F_NAME, NAME_LENGTH);
77  required_string("$Points:");
78  stuff_int(&Ranks[idx].points);
79  required_string("$Bitmap:");
81  required_string("$Promotion Voice Base:");
82  stuff_string(Ranks[idx].promotion_voice_base, F_NAME, MAX_FILENAME_LEN);
83  while (check_for_string("$Promotion Text:")) {
84  required_string("$Promotion Text:");
85  stuff_string(buf, F_MULTITEXT, sizeof(buf));
86  drop_white_space(buf);
88  persona = -1;
89  if (optional_string("+Persona:")) {
90  stuff_int(&persona);
91  if (persona < 0) {
92  Warning(LOCATION, "Debriefing text for %s rank is assigned to an invalid persona: %i (must be 0 or greater).\n", Ranks[idx].name, persona);
93  continue;
94  }
95  }
96  Ranks[idx].promotion_text[persona] = vm_strdup(buf);
97  }
98  if (Ranks[idx].promotion_text.find(-1) == Ranks[idx].promotion_text.end()) {
99  Warning(LOCATION, "%s rank is missing default debriefing text.\n", Ranks[idx].name);
100  Ranks[idx].promotion_text[-1] = "";
101  }
102  idx++;
103  }
104 
105  required_string("#End");
106 
107  // be sure that all rank points are in order
108 #ifndef NDEBUG
109  for (idx = 0; idx < NUM_RANKS - 1; idx++) {
110  if (Ranks[idx].points >= Ranks[idx + 1].points)
111  Int3();
112  }
113 #endif
114  }
115  catch (const parse::ParseException& e)
116  {
117  mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "rank.tbl", e.what()));
118  return;
119  }
120 }
121 
122 // initialize a nice blank scoring element
124 {
125  flags = 0;
126  score = 0;
127  rank = RANK_ENSIGN;
128 
129  medal_counts.assign(Num_medals, 0);
130 
131  memset(kills, 0, MAX_SHIP_CLASSES * sizeof(int));
132  assists = 0;
133  kill_count = 0;
134  kill_count_ok = 0;
135  p_shots_fired = 0;
136  s_shots_fired = 0;
137 
138  p_shots_hit = 0;
139  s_shots_hit = 0;
140 
141  p_bonehead_hits = 0;
142  s_bonehead_hits = 0;
143  bonehead_kills = 0;
144 
145  missions_flown = 0;
146  flight_time = 0;
147  last_flown = 0;
148  last_backup = 0;
149 
150  m_medal_earned = -1; // hasn't earned a medal yet
151  m_promotion_earned = -1;
152  m_badge_earned.clear();
153 
154  m_score = 0;
155  memset(m_kills, 0, MAX_SHIP_CLASSES * sizeof(int));
156  memset(m_okKills, 0, MAX_SHIP_CLASSES * sizeof(int));
157  m_kill_count = 0;
158  m_kill_count_ok = 0;
159  m_assists = 0;
160  mp_shots_fired = 0;
161  ms_shots_fired = 0;
162  mp_shots_hit = 0;
163  ms_shots_hit = 0;
164  mp_bonehead_hits = 0;
165  ms_bonehead_hits = 0;
166  m_bonehead_kills = 0;
167  m_player_deaths = 0;
168 
169  memset(m_dogfight_kills, 0, MAX_PLAYERS * sizeof(int));
170 }
171 
172 // clone someone else's scoring element
174 {
175  flags = s.flags;
176  score = s.score;
177  rank = s.rank;
178 
179  medal_counts.assign(s.medal_counts.begin(), s.medal_counts.end());
180 
181  memcpy(kills, s.kills, MAX_SHIP_CLASSES * sizeof(int));
182  assists = s.assists;
187 
190 
194 
199 
203 
204  m_score = s.m_score;
205  memcpy(m_kills, s.m_kills, MAX_SHIP_CLASSES * sizeof(int));
206  memcpy(m_okKills, s.m_okKills, MAX_SHIP_CLASSES * sizeof(int));
209  m_assists = s.m_assists;
218 
219  memcpy(m_dogfight_kills, s.m_dogfight_kills, MAX_PLAYERS * sizeof(int));
220 }
221 
222 // initialize the Player's mission-based stats before he goes into a mission
224 {
225  scp->m_medal_earned = -1; // hasn't earned a medal yet
226  scp->m_promotion_earned = -1;
227  scp->m_badge_earned.clear();
228  scp->m_score = 0;
229  scp->m_assists = 0;
230  scp->mp_shots_fired = 0;
231  scp->mp_shots_hit = 0;
232  scp->ms_shots_fired = 0;
233  scp->ms_shots_hit = 0;
234 
235  scp->mp_bonehead_hits=0;
236  scp->ms_bonehead_hits=0;
237  scp->m_bonehead_kills=0;
238 
239  memset(scp->m_kills, 0, MAX_SHIP_CLASSES * sizeof(int));
240  memset(scp->m_okKills, 0, MAX_SHIP_CLASSES * sizeof(int));
241 
242  scp->m_kill_count = 0;
243  scp->m_kill_count_ok = 0;
244 
245  scp->m_player_deaths = 0;
246 
247  memset(scp->m_dogfight_kills, 0, MAX_PLAYERS * sizeof(int));
248 
249  if (The_mission.ai_profile != NULL) {
252  } else {
253  Kill_percentage = 0.30f;
254  Assist_percentage = 0.15f;
255  }
256 }
257 
259 {
260  int i, score, new_rank, old_rank;
261 
262  old_rank = sc->rank;
263  new_rank = old_rank;
264 
265  // first check to see if the promotion flag is set -- if so, return the new rank
267 
268  // if the player does indeed get promoted, we should change his mission score
269  // to reflect the difference between all time and new rank score
270  if ( old_rank < MAX_FREESPACE2_RANK ) {
271  new_rank++;
272  if ( (sc->m_score + sc->score) < Ranks[new_rank].points )
273  sc->m_score = (Ranks[new_rank].points - sc->score);
274  }
275  } else {
276  // we get here only if player wasn't promoted automatically.
277  // it is possible to get a negative mission score but that will
278  // never result in a degradation
279  score = sc->m_score + sc->score;
280  for (i=old_rank + 1; i<NUM_RANKS; i++) {
281  if ( score >= Ranks[i].points )
282  new_rank = i;
283  }
284  }
285 
286  // if the ranks do not match, then "grant" the new rank
287  if ( old_rank != new_rank ) {
288  Assert( new_rank >= 0 );
289  sc->m_promotion_earned = new_rank;
290  sc->rank = new_rank;
291  }
292 }
293 
294 // function to evaluate whether or not a new badge is going to be awarded. This function returns
295 // which medal is awarded.
297 {
298  int total_kills;
299 
300  // to determine badges, we count kills based on fighter/bomber types. We must count kills in
301  // all time stats + current mission stats. And, only for enemy fighters/bombers
302  total_kills = 0;
303  for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it ) {
304  if ( (it->flags & SIF_FIGHTER) || (it->flags & SIF_BOMBER) ) {
305  auto i = std::distance(Ship_info.cbegin(), it);
306  total_kills += sc->m_okKills[i];
307  total_kills += sc->kills[i];
308  }
309  }
310 
311  // total_kills should now reflect the number of kills on hostile fighters/bombers. Check this number
312  // against badge kill numbers, and award the appropriate badges as neccessary.
313  int last_badge_kills = 0;
314  for (auto i = 0; i < Num_medals; i++ ) {
315  if ( total_kills >= Medals[i].kills_needed
316  && Medals[i].kills_needed > last_badge_kills
317  && Medals[i].kills_needed > 0 )
318  {
319  last_badge_kills = Medals[i].kills_needed;
320  if (sc->medal_counts[i] < 1) {
321  sc->medal_counts[i] = 1;
322  sc->m_badge_earned.push_back(i);
323  }
324  }
325  }
326 }
327 
328 // central point for dealing with accepting the score for a misison.
330 {
331  int idx;
332 
333  // do rank, badges, and medals first since they require the alltime stuff
334  // to not be updated yet.
335 
336  // do medal stuff
337  if ( score->m_medal_earned != -1 ){
338  score->medal_counts[score->m_medal_earned]++;
339  }
340 
341  // return when in training mission. We can grant a medal in training, but don't
342  // want to calculate any other statistics.
344  return;
345  }
346 
347  scoring_eval_rank(score);
348  scoring_eval_badges(score);
349 
350  score->kill_count += score->m_kill_count;
351  score->kill_count_ok += score->m_kill_count_ok;
352 
353  score->score += score->m_score;
354  score->assists += score->m_assists;
355  score->p_shots_fired += score->mp_shots_fired;
356  score->s_shots_fired += score->ms_shots_fired;
357 
358  score->p_shots_hit += score->mp_shots_hit;
359  score->s_shots_hit += score->ms_shots_hit;
360 
361  score->p_bonehead_hits += score->mp_bonehead_hits;
362  score->s_bonehead_hits += score->ms_bonehead_hits;
363  score->bonehead_kills += score->m_bonehead_kills;
364 
365  for(idx=0;idx<MAX_SHIP_CLASSES;idx++){
366  score->kills[idx] = (int)(score->kills[idx] + score->m_okKills[idx]);
367  }
368 
369  // add in mission time
370  score->flight_time += (unsigned int)f2fl(Missiontime);
371  score->last_backup = score->last_flown;
372  score->last_flown = (_fs_time_t)time(NULL);
373  score->missions_flown++;
374 }
375 
376 // backout the score for a mission. This function gets called when the player chooses to refly a misison
377 // after debriefing
379 {
380  int idx;
381 
382  // if a badge was earned, take it back
383  if ( score->m_badge_earned.size() ){
384  for (size_t medal = 0; medal < score->m_badge_earned.size(); medal++) {
385  score->medal_counts[score->m_badge_earned[medal]] = 0;
386  }
387  }
388 
389  // return when in training mission. We can grant a medal in training, but don't
390  // want to calculate any other statistics.
392  return;
393  }
394 
395  score->kill_count -= score->m_kill_count;
396  score->kill_count_ok -= score->m_kill_count_ok;
397 
398  score->score -= score->m_score;
399  score->assists -= score->m_assists;
400  score->p_shots_fired -= score->mp_shots_fired;
401  score->s_shots_fired -= score->ms_shots_fired;
402 
403  score->p_shots_hit -= score->mp_shots_hit;
404  score->s_shots_hit -= score->ms_shots_hit;
405 
406  score->p_bonehead_hits -= score->mp_bonehead_hits;
407  score->s_bonehead_hits -= score->ms_bonehead_hits;
408  score->bonehead_kills -= score->m_bonehead_kills;
409 
410  for(idx=0;idx<MAX_SHIP_CLASSES;idx++){
411  score->kills[idx] = (unsigned short)(score->kills[idx] - score->m_okKills[idx]);
412  }
413 
414  // if the player was given a medal, take it back
415  if ( score->m_medal_earned != -1 ) {
416  score->medal_counts[score->m_medal_earned]--;
417  Assert( score->medal_counts[score->m_medal_earned] >= 0 );
418  }
419 
420  // if the player was promoted, take it back
421  if ( score->m_promotion_earned != -1) {
422  score->rank--;
423  Assert( score->rank >= 0 );
424  }
425 
426  score->flight_time -= (unsigned int)f2fl(Missiontime);
427  score->last_flown = score->last_backup;
428  score->missions_flown--;
429 }
430 
431 // merge any mission stats accumulated into the alltime stats (as well as updating per campaign stats)
432 void scoring_level_close(int accepted)
433 {
434  // want to calculate any other statistics.
436  // call scoring_do_accept
437  // this will grant any potential medals and then early bail, and
438  // then we will early bail
440  Pilot.update_stats(&Player->stats, true);
441  return;
442  }
443 
444  if(accepted){
445  // apply mission stats for all players in the game
446  int idx;
447  scoring_struct *sc;
448 
450  nprintf(("Network","Storing stats for all players now\n"));
451  for(idx=0;idx<MAX_PLAYERS;idx++){
453  // get the scoring struct
454  sc = &Net_players[idx].m_player->stats;
455  scoring_do_accept( sc );
456 
457  if (Net_player == &Net_players[idx]) {
458  Pilot.update_stats(sc);
459  }
460  }
461  }
462  } else {
463  nprintf(("General","Storing stats now\n"));
465  }
466 
467  // If this mission doesn't allow promotion or badges
468  // then be sure that these don't get done. Don't allow promotions or badges when
469  // playing normally and not in a campaign.
471  if ( Player->stats.m_promotion_earned != -1) {
472  Player->stats.rank--;
474  }
475 
476  // if a badge was earned, take it back
477  if ( Player->stats.m_badge_earned.size() ){
478  for (size_t medal = 0; medal < Player->stats.m_badge_earned.size(); medal++) {
480  }
481  Player->stats.m_badge_earned.clear();
482  }
483  }
484 
485  if ( !(Game_mode & GM_MULTIPLAYER) ) {
487  }
488  }
489 }
490 
491 // STATS damage, assists recording stuff
492 void scoring_add_damage(object *ship_objp,object *other_obj,float damage)
493 {
494  int found_slot, signature;
495  int lowest_index,idx;
496  object *use_obj;
497  ship *sp;
498 
499  // multiplayer clients bail here
500  if(MULTIPLAYER_CLIENT){
501  return;
502  }
503 
504  // if we have no other object, bail
505  if(other_obj == NULL){
506  return;
507  }
508 
509  // for player kill/assist evaluation, we have to know exactly how much damage really mattered. For example, if
510  // a ship had 1 hit point left, and the player hit it with a nuke, it doesn't matter that it did 10,000,000
511  // points of damage, only that 1 point would count
512  float actual_damage = 0.0f;
513 
514  // other_obj might not always be the parent of other_obj (in the case of debug code for sure). See
515  // if the other_obj has a parent, and if so, use the parent. If no parent, see if other_obj is a ship
516  // and if so, use that ship.
517  if ( other_obj->parent != -1 ){
518  use_obj = &Objects[other_obj->parent];
519  signature = use_obj->signature;
520  } else {
521  signature = other_obj->signature;
522  use_obj = other_obj;
523  }
524 
525  // don't count damage done to a ship by himself
526  if(use_obj == ship_objp){
527  return;
528  }
529 
530  // get a pointer to the ship and add the actual amount of damage done to it
531  // get the ship object, and determine the _actual_ amount of damage done
532  sp = &Ships[ship_objp->instance];
533  // see comments at beginning of function
534  if(ship_objp->hull_strength < 0.0f){
535  actual_damage = damage + ship_objp->hull_strength;
536  } else {
537  actual_damage = damage;
538  }
539  if(actual_damage < 0.0f){
540  actual_damage = 0.0f;
541  }
542  sp->total_damage_received += actual_damage;
543 
544  // go through and clear out all old damagers
545  for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
546  if((sp->damage_ship_id[idx] >= 0) && (ship_get_by_signature(sp->damage_ship_id[idx]) < 0)){
547  sp->damage_ship_id[idx] = -1;
548  sp->damage_ship[idx] = 0;
549  }
550  }
551 
552  // only evaluate possible kill/assist numbers if the hitting object (use_obj) is a piloted ship (ie, ignore asteroids, etc)
553  // don't store damage a ship may do to himself
554  if((ship_objp->type == OBJ_SHIP) && (use_obj->type == OBJ_SHIP)){
555  found_slot = 0;
556  // try and find an open slot
557  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
558  // if this ship object doesn't exist anymore, use the slot
559  if((sp->damage_ship_id[idx] == -1) || (ship_get_by_signature(sp->damage_ship_id[idx]) < 0) || (sp->damage_ship_id[idx] == signature) ){
560  found_slot = 1;
561  break;
562  }
563  }
564 
565  // if not found (implying all slots are taken), then find the slot with the lowest damage % and use that
566  if(!found_slot){
567  lowest_index = 0;
568  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
569  if(sp->damage_ship[idx] < sp->damage_ship[lowest_index]){
570  lowest_index = idx;
571  }
572  }
573  } else {
574  lowest_index = idx;
575  }
576 
577  // fill in the slot damage and damager-index
578  if(found_slot){
579  sp->damage_ship[lowest_index] += actual_damage;
580  } else {
581  sp->damage_ship[lowest_index] = actual_damage;
582  }
583  sp->damage_ship_id[lowest_index] = signature;
584  }
585 }
586 
588 
589 // evaluate a kill on a ship
590 int scoring_eval_kill(object *ship_objp)
591 {
592  float max_damage_pct; // the pct% of total damage the max damage object did
593  int max_damage_index; // the index into the dying ship's damage_ship[] array corresponding the greatest amount of damage
594  int killer_sig; // signature of the guy getting credit for the kill (or -1 if none)
595  int idx,net_player_num;
596  player *plr; // pointer to a player struct if it was a player who got the kill
597  net_player *net_plr = NULL;
598  ship *dead_ship; // the ship which was killed
599  net_player *dead_plr = NULL;
600  float scoring_scale_by_damage = 1; // percentage to scale the killer's score by if we score based on the amount of damage caused
601  int kill_score, assist_score;
602  bool is_enemy_player = false; // true if the player just killed an enemy player ship
603 
604 
605  // multiplayer clients bail here
606  if(MULTIPLAYER_CLIENT){
607  return -1;
608  }
609 
610  // we don't evaluate kills on anything except ships
611  if(ship_objp->type != OBJ_SHIP){
612  return -1;
613  }
614  if((ship_objp->instance < 0) || (ship_objp->instance >= MAX_SHIPS)){
615  return -1;
616  }
617 
618  // assign the dead ship
619  dead_ship = &Ships[ship_objp->instance];
620 
621  // evaluate player deaths
623  net_player_num = multi_find_player_by_object(ship_objp);
624  if(net_player_num != -1){
625  Net_players[net_player_num].m_player->stats.m_player_deaths++;
626  nprintf(("Network","Setting player %s deaths to %d\n",Net_players[net_player_num].m_player->callsign,Net_players[net_player_num].m_player->stats.m_player_deaths));
627  dead_plr = &Net_players[net_player_num];
628  is_enemy_player = true;
629  }
630  } else {
631  if(ship_objp == Player_obj){
633  }
634  }
635 
636  net_player_num = -1;
637 
638  // clear out invalid damager ships
639  for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
640  if((dead_ship->damage_ship_id[idx] >= 0) && (ship_get_by_signature(dead_ship->damage_ship_id[idx]) < 0)){
641  dead_ship->damage_ship[idx] = 0.0f;
642  dead_ship->damage_ship_id[idx] = -1;
643  }
644  }
645 
646  // determine which object did the most damage to the dying object, and how much damage that was
647  max_damage_index = -1;
648  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
649  // bogus ship
650  if(dead_ship->damage_ship_id[idx] < 0){
651  continue;
652  }
653 
654  // if this slot did more damage then the next highest slot
655  if((max_damage_index == -1) || (dead_ship->damage_ship[idx] > dead_ship->damage_ship[max_damage_index])){
656  max_damage_index = idx;
657  }
658  }
659 
660  // doh
661  if((max_damage_index < 0) || (max_damage_index >= MAX_DAMAGE_SLOTS)){
662  return -1;
663  }
664 
665  // the pct of total damage applied to this ship
666  max_damage_pct = dead_ship->damage_ship[max_damage_index] / dead_ship->total_damage_received;
667 
668  CLAMP(max_damage_pct, 0.0f, 1.0f);
669 
670  // only evaluate if the max damage % is high enough to record a kill and it was done by a valid object
671  if((max_damage_pct >= Kill_percentage) && (dead_ship->damage_ship_id[max_damage_index] >= 0)){
672  // set killer_sig for this ship to the signature of the guy who gets credit for the kill
673  killer_sig = dead_ship->damage_ship_id[max_damage_index];
674 
675  // set the scale value if we only award 100% score for 100% damage
677  scoring_scale_by_damage = max_damage_pct;
678  }
679 
680  // null this out for now
681  plr = NULL;
682  net_plr = NULL;
683 
684  // get the player (whether single or multiplayer)
685  net_player_num = -1;
686 
687  if(Game_mode & GM_MULTIPLAYER){
688  net_player_num = multi_find_player_by_signature(killer_sig);
689  if(net_player_num != -1){
690  plr = Net_players[net_player_num].m_player;
691  net_plr = &Net_players[net_player_num];
692  }
693  } else {
694  if(Objects[Player->objnum].signature == killer_sig){
695  plr = Player;
696  }
697  }
698 
699  // if we found a valid player, evaluate some kill details
700  if(plr != NULL){
701  int si_index;
702 
703  // bogus
704  if((plr->objnum < 0) || (plr->objnum >= MAX_OBJECTS)){
705  return -1;
706  }
707 
708  // get the ship info index of the ship type of this kill. we need to take ship
709  // copies into account here.
710  si_index = dead_ship->ship_info_index;
711  if (Ship_info[si_index].flags & SIF_SHIP_COPY)
712  {
713  char temp[NAME_LENGTH];
714  strcpy_s(temp, Ship_info[si_index].name);
716 
717  // Goober5000 - previous error checking guarantees that this will be >= 0
718  si_index = ship_info_lookup(temp);
719  }
720 
721  // if he killed a guy on his own team increment his bonehead kills
722  if((Ships[Objects[plr->objnum].instance].team == dead_ship->team) && !MULTI_DOGFIGHT ){
724  plr->stats.m_bonehead_kills++;
725  kill_score = -(int)(dead_ship->score * scoring_get_scale_factor());
726  plr->stats.m_score += kill_score;
727 
728  if(net_plr != NULL ) {
729  multi_team_maybe_add_score(-(dead_ship->score), net_plr->p_info.team);
730  }
731  }
732  }
733  // otherwise increment his valid kill count and score
734  else {
735  // dogfight mode
736  if(MULTI_DOGFIGHT && (multi_find_player_by_object(ship_objp) < 0)){
737  // don't add a kill for dogfight kills on non-players
738  } else {
739  plr->stats.m_okKills[si_index]++;
740  plr->stats.m_kill_count_ok++;
741 
742  // only computer controlled enemies should scale with difficulty
743  if (is_enemy_player) {
744  kill_score = (int)(dead_ship->score * scoring_scale_by_damage);
745  }
746  else {
747  kill_score = (int)(dead_ship->score * scoring_get_scale_factor() * scoring_scale_by_damage);
748  }
749 
750 
751  plr->stats.m_score += kill_score;
753 
754 #ifdef SCORING_DEBUG
755  char kill_score_text[1024] = "";
756  sprintf(kill_score_text, "SCORING : %s killed a ship worth %d points and gets %d pts for the kill\n", plr->callsign, dead_ship->score, kill_score);
757  if (MULTIPLAYER_MASTER) {
759  }
760  HUD_printf(kill_score_text);
761  mprintf((kill_score_text));
762 #endif
763 
764  // multiplayer
765  if(net_plr != NULL){
766  multi_team_maybe_add_score(dead_ship->score , net_plr->p_info.team);
767 
768  // award teammates % of score value for big ship kills
769  // not in dogfight tho
770  // and not if there is no assist threshold (as otherwise assists could get higher scores than kills)
772  for (idx=0; idx<MAX_PLAYERS; idx++) {
773  if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == net_plr->p_info.team) && (&Net_players[idx] != net_plr)) {
775  Net_players[idx].m_player->stats.m_score += assist_score;
776 
777 #ifdef SCORING_DEBUG
778  // DEBUG CODE TO TEST NEW SCORING
779  char score_text[1024] = "";
780  sprintf(score_text, "SCORING : All team mates get %d pts for helping kill the capship\n", assist_score);
782  HUD_printf(score_text);
783  mprintf((score_text));
784 #endif
785  }
786  }
787  }
788 
789  // death message
790  if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (net_plr != NULL) && (dead_plr != NULL) && (net_plr->m_player != NULL) && (dead_plr->m_player != NULL)){
791  char dead_text[1024] = "";
792 
793  sprintf(dead_text, "%s gets the kill for %s", net_plr->m_player->callsign, dead_plr->m_player->callsign);
794  send_game_chat_packet(Net_player, dead_text, MULTI_MSG_ALL, NULL, NULL, 2);
795  HUD_printf(dead_text);
796  }
797  }
798  }
799  }
800 
801  // increment his all-encompassing kills
802  plr->stats.m_kills[si_index]++;
803  plr->stats.m_kill_count++;
804 
805  // update everyone on this guy's kills if this is multiplayer
806  if(MULTIPLAYER_MASTER && (net_player_num != -1)){
807  // send appropriate stats
809  // evaluate dogfight kills
810  multi_df_eval_kill(&Net_players[net_player_num], ship_objp);
811 
812  // update stats
814  } else {
816  }
817  }
818  }
819  } else {
820  // set killer_sig for this ship to -1, indicating no one got the kill for it
821  killer_sig = -1;
822  }
823 
824  // pass in the guy who got the credit for the kill (if any), so that he doesn't also
825  // get credit for an assist
826  scoring_eval_assists(dead_ship,killer_sig, is_enemy_player);
827 
828 #ifdef SCORING_DEBUG
829 
830  if (Game_mode & GM_MULTIPLAYER) {
831  char buf[256];
832  sprintf(Scoring_debug_text, "SCORING : %s killed.\nDamage by ship:\n\n", Ship_info[dead_ship->ship_info_index].name);
833 
834  // show damage done by player
835  for (int i=0; i<MAX_DAMAGE_SLOTS; i++) {
836  int net_player_num = multi_find_player_by_signature(dead_ship->damage_ship_id[i]);
837  if (net_player_num != -1) {
838  plr = Net_players[net_player_num].m_player;
839  sprintf(buf, "%s: %f", plr->callsign, dead_ship->damage_ship[i]);
840 
841  if (dead_ship->damage_ship_id[i] == killer_sig ) {
842  strcat_s(buf, " KILLER\n");
843  } else {
844  strcat_s(buf, "\n");
845  }
846 
848  }
849  }
851  }
852 #endif
853 
854  return max_damage_index;
855 }
856 
857 //evaluate a kill on a weapon, right now this is only called on bombs. -Halleck
858 int scoring_eval_kill_on_weapon(object *weapon_obj, object *other_obj) {
859  float max_damage_pct; // the pct% of total damage the max damage object did
860  int max_damage_index; // the index into the dying ship's damage_ship[] array corresponding the greatest amount of damage
861  int killer_sig; // signature of the guy getting credit for the kill (or -1 if none)
862  int idx,net_player_num;
863  player *plr; // pointer to a player struct if it was a player who got the kill
864  net_player *net_plr = NULL;
865  net_player *dead_plr = NULL;
866  float scoring_scale_by_damage = 1; // percentage to scale the killer's score by if we score based on the amount of damage caused
867  int kill_score;
868 
869  weapon *dead_wp; // the weapon that was killed
870  weapon_info *dead_wip; // info on the weapon that was killed
871 
872  if((weapon_obj->instance < 0) || (weapon_obj->instance >= MAX_WEAPONS)){
873  return -1;
874  }
875 
876  dead_wp = &Weapons[weapon_obj->instance]; //assign the dead weapon
877  dead_wip = &Weapon_info[dead_wp->weapon_info_index];
878 
879  // multiplayer clients bail here
880  if(MULTIPLAYER_CLIENT){
881  return -1;
882  }
883 
884  // we don't evaluate kills on anything except weapons
885  if(weapon_obj->type != OBJ_WEAPON){
886  return -1;
887  }
888 
889  // we don't evaluate kills on anything except bombs, currently. -Halleck
890  if(!(dead_wip->wi_flags & WIF_BOMB)) {
891  return -1;
892  }
893 
894  // clear out invalid damager ships
895  for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
896  if((dead_wp->damage_ship_id[idx] >= 0) && (ship_get_by_signature(dead_wp->damage_ship_id[idx]) < 0)){
897  dead_wp->damage_ship[idx] = 0.0f;
898  dead_wp->damage_ship_id[idx] = -1;
899  }
900  }
901 
902  // determine which object did the most damage to the dying object, and how much damage that was
903  max_damage_index = -1;
904  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
905  // bogus ship
906  if(dead_wp->damage_ship_id[idx] < 0){
907  continue;
908  }
909 
910  // if this slot did more damage then the next highest slot
911  if((max_damage_index == -1) || (dead_wp->damage_ship[idx] > dead_wp->damage_ship[max_damage_index])){
912  max_damage_index = idx;
913  }
914  }
915 
916  // doh
917  if((max_damage_index < 0) || (max_damage_index >= MAX_DAMAGE_SLOTS)){
918  return -1;
919  }
920 
921  // the pct of total damage applied to this ship
922  max_damage_pct = dead_wp->damage_ship[max_damage_index] / dead_wp->total_damage_received;
923 
924  CLAMP(max_damage_pct, 0.0f, 1.0f);
925 
926  // only evaluate if the max damage % is high enough to record a kill and it was done by a valid object
927  if((max_damage_pct >= Kill_percentage) && (dead_wp->damage_ship_id[max_damage_index] >= 0)){
928  // set killer_sig for this ship to the signature of the guy who gets credit for the kill
929  killer_sig = dead_wp->damage_ship_id[max_damage_index];
930 
931  // set the scale value if we only award 100% score for 100% damage
933  scoring_scale_by_damage = max_damage_pct;
934  }
935 
936  // null this out for now
937  plr = NULL;
938  net_plr = NULL;
939 
940  // get the player (whether single or multiplayer)
941  net_player_num = -1;
942 
944  net_player_num = multi_find_player_by_signature(killer_sig);
945  if(net_player_num != -1){
946  plr = Net_players[net_player_num].m_player;
947  net_plr = &Net_players[net_player_num];
948  }
949  } else {
950  if(Objects[Player->objnum].signature == killer_sig){
951  plr = Player;
952  }
953  }
954 
955  // if we found a valid player, evaluate some kill details
956  if(plr != NULL){
957  //int si_index;
958 
959  // bogus
960  if((plr->objnum < 0) || (plr->objnum >= MAX_OBJECTS)){
961  return -1;
962  }
963 
964  // get the ship info index of the ship type of this kill. we need to take ship
965  // copies into account here.
966  //si_index = dead_wp->weapon_info_index;
967 
968  // if he killed a guy on his own team increment his bonehead kills
969  if((Ships[Objects[plr->objnum].instance].team == dead_wp->team) && !MULTI_DOGFIGHT ){
971  plr->stats.m_bonehead_kills++;
972  kill_score = -(int)(dead_wip->score * scoring_get_scale_factor());
973  plr->stats.m_score += kill_score;
974 
975  if(net_plr != NULL ) {
976  multi_team_maybe_add_score(-(dead_wip->score), net_plr->p_info.team);
977  }
978  }
979  }
980  // otherwise increment his valid kill count and score
981  else {
982  // dogfight mode
983  if(MULTI_DOGFIGHT && (multi_find_player_by_object(weapon_obj) < 0)){
984  // don't add a kill for dogfight kills on non-players
985  } else {
986 
987  // bombs don't scale with difficulty at the moment. If we change this we want to *scoring_get_scale_factor() too
988  kill_score = (int)(dead_wip->score * scoring_scale_by_damage);
989 
990  plr->stats.m_score += kill_score;
992 
993 #ifdef SCORING_DEBUG
994  char kill_score_text[1024] = "";
995  sprintf(kill_score_text, "SCORING : %s killed a ship worth %i points and gets %i pts for the kill", plr->callsign, dead_wip->score, kill_score);
996  if (MULTIPLAYER_MASTER) {
998  }
999  HUD_printf(kill_score_text);
1000  mprintf((kill_score_text));
1001 #endif
1002 
1003  // multiplayer
1004  if(net_plr != NULL){
1005  multi_team_maybe_add_score(dead_wip->score , net_plr->p_info.team);
1006 
1007  // award teammates % of score value for big ship kills
1008  // not in dogfight tho
1009  // and not if there is no assist threshold (as otherwise assists could get higher scores than kills)
1010  /* Below is N/A. -Halleck
1011  if (!(Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[dead_wp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
1012  for (idx=0; idx<MAX_PLAYERS; idx++) {
1013  if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == net_plr->p_info.team) && (&Net_players[idx] != net_plr)) {
1014  assist_score = (int)(dead_wip->score * The_mission.ai_profile->assist_award_percentage_scale[Game_skill_level]);
1015  Net_players[idx].m_player->stats.m_score += assist_score;
1016 
1017 #ifdef SCORING_DEBUG
1018  // DEBUG CODE TO TEST NEW SCORING
1019  char score_text[1024] = "";
1020  sprintf(score_text, "SCORING : All team mates get %d pts for helping kill the capship", assist_score);
1021  send_game_chat_packet(Net_player, score_text, MULTI_MSG_ALL);
1022  HUD_printf(score_text);
1023  mprintf((score_text));
1024 #endif
1025  }
1026  }
1027  }
1028  */
1029 
1030  // death message
1031  if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (net_plr != NULL) && (dead_plr != NULL) && (net_plr->m_player != NULL) && (dead_plr->m_player != NULL)){
1032  char dead_text[1024] = "";
1033 
1034  sprintf(dead_text, "%s gets the kill for %s", net_plr->m_player->callsign, dead_plr->m_player->callsign);
1035  send_game_chat_packet(Net_player, dead_text, MULTI_MSG_ALL, NULL, NULL, 2);
1036  HUD_printf(dead_text);
1037  }
1038  }
1039  }
1040  }
1041 
1042  // increment his all-encompassing kills
1043  /*Not really a kill. -Halleck
1044  plr->stats.m_kills[si_index]++;
1045  plr->stats.m_kill_count++;
1046 
1047  // update everyone on this guy's kills if this is multiplayer
1048  if(MULTIPLAYER_MASTER && (net_player_num != -1)){
1049  // send appropriate stats
1050  if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
1051  // evaluate dogfight kills
1052  multi_df_eval_kill(&Net_players[net_player_num], weapon_obj);
1053 
1054  // update stats
1055  send_player_stats_block_packet(&Net_players[net_player_num], STATS_DOGFIGHT_KILLS);
1056  } else {
1057  send_player_stats_block_packet(&Net_players[net_player_num], STATS_MISSION_KILLS);
1058  }
1059  }
1060  */
1061  }
1062  } else {
1063  // set killer_sig for this ship to -1, indicating no one got the kill for it
1064  killer_sig = -1;
1065  }
1066 
1067  // pass in the guy who got the credit for the kill (if any), so that he doesn't also
1068  // get credit for an assist
1069  /* TODO: ADD ASSIST CALC BACK IN. -Halleck
1070  scoring_eval_assists(dead_wp,killer_sig, is_enemy_player);
1071  */
1072 
1073 #ifdef SCORING_DEBUG
1074 
1075  if (Game_mode & GM_MULTIPLAYER) {
1076  char buf[256];
1077  sprintf(Scoring_debug_text, "SCORING : %s killed.\nDamage by ship:\n\n", dead_wip->name);
1078 
1079  // show damage done by player
1080  for (int i=0; i<MAX_DAMAGE_SLOTS; i++) {
1081  int net_player_num = multi_find_player_by_signature(dead_wp->damage_ship_id[i]);
1082  if (net_player_num != -1) {
1083  plr = Net_players[net_player_num].m_player;
1084  sprintf(buf, "%s: %f", plr->callsign, dead_wp->damage_ship[i]);
1085 
1086  if (dead_wp->damage_ship_id[i] == killer_sig ) {
1087  strcat_s(buf, " KILLER\n");
1088  } else {
1089  strcat_s(buf, "\n");
1090  }
1091 
1093  }
1094  }
1096  }
1097 #endif
1098 
1099  return max_damage_index;
1100 }
1101 
1102 // kill_id is the object signature of the guy who got the credit for the kill (may be -1, if no one got it)
1103 // this is to ensure that you don't also get an assist if you get the kill.
1104 void scoring_eval_assists(ship *sp,int killer_sig, bool is_enemy_player)
1105 {
1106  int idx;
1107  player *plr;
1108  float scoring_scale_by_damage = 1; // percentage to scale the score by if we score based on the amount of damage caused
1109  int assist_score;
1110  int net_player_num;
1111  float scoring_scale_factor;
1112 
1113 
1114  // multiplayer clients bail here
1115  if(MULTIPLAYER_CLIENT){
1116  return;
1117  }
1118 
1119  // evaluate each damage slot to see if it did enough to give the assis
1120  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
1121  // if this slot did enough damage to get an assist
1123  // get the player which did this damage (if any)
1124  plr = NULL;
1125 
1126  // multiplayer
1127  if(Game_mode & GM_MULTIPLAYER){
1128  net_player_num = multi_find_player_by_signature(sp->damage_ship_id[idx]);
1129  if(net_player_num != -1){
1130  plr = Net_players[net_player_num].m_player;
1131  }
1132  }
1133  // single player
1134  else {
1135  if(Objects[Player->objnum].signature == sp->damage_ship_id[idx]){
1136  plr = Player;
1137  }
1138  }
1139 
1140  // if we found a player, give him the assist if he attacks it
1141  if ((plr != NULL) && (iff_x_attacks_y(Ships[Objects[plr->objnum].instance].team, sp->team)) && (killer_sig != Objects[plr->objnum].signature))
1142  {
1143  // player has to equal the threshold to get an assist
1144  if ((sp->damage_ship[idx]/sp->total_damage_received) >= Assist_percentage) {
1145  plr->stats.m_assists++;
1146  nprintf(("Network","-==============GAVE PLAYER %s AN ASSIST=====================-\n",plr->callsign));
1147  }
1148 
1149  // Don't scale in TvT and dogfight
1150  if (is_enemy_player) {
1151  Assert(Game_mode & GM_MULTIPLAYER);
1152  scoring_scale_factor = 1.0f;
1153  }
1154  else {
1155  scoring_scale_factor = scoring_get_scale_factor();
1156  }
1157 
1158 
1159  // maybe award assist points based on damage
1161  scoring_scale_by_damage = (sp->damage_ship[idx]/sp->total_damage_received);
1162  assist_score = (int)(sp->score * scoring_scale_factor * scoring_scale_by_damage);
1163  plr->stats.m_score += assist_score;
1164  }
1165  // otherwise give the points based on the percentage in the mission file
1166  else {
1167  assist_score = (int)(sp->score * sp->assist_score_pct * scoring_scale_factor );
1168  plr->stats.m_score += assist_score;
1169  }
1170 
1171 #ifdef SCORING_DEBUG
1172 
1173  // DEBUG CODE TO TEST NEW SCORING
1174  char score_text[1024] = "";
1175  sprintf(score_text, "SCORING : %s gets %d pts for getting an assist\n", plr->callsign, assist_score);
1176  if (MULTIPLAYER_MASTER) {
1178  }
1179  HUD_printf(score_text);
1180  mprintf ((score_text));
1181 #endif
1182  }
1183  }
1184  }
1185 }
1186 
1187 // eval a hit on an object (for primary and secondary hit purposes)
1188 void scoring_eval_hit(object *hit_obj, object *other_obj,int from_blast)
1189 {
1190  // multiplayer clients bail here
1191  if(MULTIPLAYER_CLIENT){
1192  return;
1193  }
1194 
1195  // only evaluate hits on ships, asteroids, and weapons! -Halleck
1196  if((hit_obj->type != OBJ_SHIP) && (hit_obj->type != OBJ_ASTEROID) && (hit_obj->type != OBJ_WEAPON)){
1197  return;
1198  }
1199 
1200  // if the other_obj == NULL, we can't evaluate where it came from, so bail here
1201  if(other_obj == NULL){
1202  return;
1203  }
1204 
1205  // other bogus situtations
1206  if(other_obj->instance < 0){
1207  return;
1208  }
1209 
1210  if((other_obj->type == OBJ_WEAPON) && !(Weapons[other_obj->instance].weapon_flags & WF_ALREADY_APPLIED_STATS)){
1211  // bogus weapon
1212  if(other_obj->instance >= MAX_WEAPONS){
1213  return;
1214  }
1215 
1216  // bogus parent
1217  if(other_obj->parent < 0){
1218  return;
1219  }
1220  if(other_obj->parent >= MAX_OBJECTS){
1221  return;
1222  }
1223  if(Objects[other_obj->parent].type != OBJ_SHIP){
1224  return;
1225  }
1226  if((Objects[other_obj->parent].instance < 0) || (Objects[other_obj->parent].instance >= MAX_SHIPS)){
1227  return;
1228  }
1229 
1230  //Only evaluate hits on bomb weapons. -Halleck
1231  bool hit_obj_is_bomb = false;
1232 
1233  if(hit_obj->type == OBJ_WEAPON){
1234 
1235  //Hit weapon is bogus
1236  if (hit_obj->instance >= MAX_WEAPONS) {
1237  return;
1238  }
1239 
1240  hit_obj_is_bomb = (Weapon_info[Weapons[hit_obj->instance].weapon_info_index].wi_flags & WIF_BOMB) ? true : false;
1241 
1242  //If it's not a bomb but just a regular weapon, we don't care about it (for now, at least.) -Halleck
1243  if (!hit_obj_is_bomb) {
1244  return;
1245  }
1246  }
1247 
1248  int is_bonehead = 0;
1249  int sub_type = Weapon_info[Weapons[other_obj->instance].weapon_info_index].subtype;
1250 
1251  // determine if this was a bonehead hit or not
1252  if(hit_obj->type == OBJ_SHIP){
1253  is_bonehead = Ships[hit_obj->instance].team==Ships[Objects[other_obj->parent].instance].team ? 1 : 0;
1254  } else if (hit_obj_is_bomb) {
1255  is_bonehead = Weapons[hit_obj->instance].team==Ships[Objects[other_obj->parent].instance].team ? 1 : 0;
1256  }
1257  // can't have a bonehead hit on an asteroid
1258  else {
1259  is_bonehead = 0;
1260  }
1261 
1262  // set the flag indicating that we've already applied a "stats" hit for this weapon
1263  // Weapons[other_obj->instance].weapon_flags |= WF_ALREADY_APPLIED_STATS;
1264 
1265  // in multiplayer -- only the server records the stats
1266  if( Game_mode & GM_MULTIPLAYER ) {
1268  int player_num;
1269 
1270  // get the player num of the parent object. A player_num of -1 means that the
1271  // parent of this object was not a player
1272  player_num = multi_find_player_by_object( &Objects[other_obj->parent] );
1273  if ( player_num != -1 ) {
1274  switch(sub_type) {
1275  case WP_LASER :
1276  if(is_bonehead){
1277  Net_players[player_num].m_player->stats.mp_bonehead_hits++;
1278  } else {
1279  Net_players[player_num].m_player->stats.mp_shots_hit++;
1280  }
1281 
1282  // Assert( Net_players[player_num].player->stats.mp_shots_hit <= Net_players[player_num].player->stats.mp_shots_fired );
1283  break;
1284  case WP_MISSILE :
1285  // friendly hit, once it hits a friendly, its done
1286  if(is_bonehead){
1287  if(!from_blast){
1288  Net_players[player_num].m_player->stats.ms_bonehead_hits++;
1289  }
1290  }
1291  // hostile hit
1292  else {
1293  // if its a bomb, count every bit of damage it does
1295  // once we get impact damage, stop keeping track of it
1296  Net_players[player_num].m_player->stats.ms_shots_hit++;
1297  }
1298  // if its not a bomb, only count impact damage
1299  else {
1300  if(!from_blast){
1301  Net_players[player_num].m_player->stats.ms_shots_hit++;
1302  }
1303  }
1304  }
1305  default :
1306  break;
1307  }
1308  }
1309  }
1310  } else {
1311  if(Player_obj == &(Objects[other_obj->parent])){
1312  switch(sub_type){
1313  case WP_LASER :
1314  if(is_bonehead){
1316  } else {
1318  }
1319  break;
1320  case WP_MISSILE :
1321  // friendly hit, once it hits a friendly, its done
1322  if(is_bonehead){
1323  if(!from_blast){
1325  }
1326  }
1327  // hostile hit
1328  else {
1329  // if its a bomb, count every bit of damage it does
1331  // once we get impact damage, stop keeping track of it
1333  }
1334  // if its not a bomb, only count impact damage
1335  else {
1336  if(!from_blast){
1338  }
1339  }
1340  }
1341  break;
1342  default :
1343  break;
1344  }
1345  }
1346  }
1347  }
1348 }
1349 
1350 // STATS damage, assists recording stuff
1351 void scoring_add_damage_to_weapon(object *weapon_obj,object *other_obj,float damage)
1352 {
1353  int found_slot, signature;
1354  int lowest_index,idx;
1355  object *use_obj;
1356  weapon *wp;
1357 
1358  // multiplayer clients bail here
1359  if(MULTIPLAYER_CLIENT){
1360  return;
1361  }
1362 
1363  // if we have no other object, bail
1364  if(other_obj == NULL){
1365  return;
1366  }
1367 
1368  // for player kill/assist evaluation, we have to know exactly how much damage really mattered. For example, if
1369  // a ship had 1 hit point left, and the player hit it with a nuke, it doesn't matter that it did 10,000,000
1370  // points of damage, only that 1 point would count
1371  float actual_damage = 0.0f;
1372 
1373  // other_obj might not always be the parent of other_obj (in the case of debug code for sure). See
1374  // if the other_obj has a parent, and if so, use the parent. If no parent, see if other_obj is a ship
1375  // and if so, use that ship.
1376  if ( other_obj->parent != -1 ){
1377  use_obj = &Objects[other_obj->parent];
1378  signature = use_obj->signature;
1379  } else {
1380  signature = other_obj->signature;
1381  use_obj = other_obj;
1382  }
1383 
1384  // don't count damage done to a ship by himself
1385  if(use_obj == weapon_obj){
1386  return;
1387  }
1388 
1389  // get a pointer to the ship and add the actual amount of damage done to it
1390  // get the ship object, and determine the _actual_ amount of damage done
1391  wp = &Weapons[weapon_obj->instance];
1392  // see comments at beginning of function
1393  if(weapon_obj->hull_strength < 0.0f){
1394  actual_damage = damage + weapon_obj->hull_strength;
1395  } else {
1396  actual_damage = damage;
1397  }
1398  if(actual_damage < 0.0f){
1399  actual_damage = 0.0f;
1400  }
1401  wp->total_damage_received += actual_damage;
1402 
1403  // go through and clear out all old damagers
1404  for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
1405  if((wp->damage_ship_id[idx] >= 0) && (ship_get_by_signature(wp->damage_ship_id[idx]) < 0)){
1406  wp->damage_ship_id[idx] = -1;
1407  wp->damage_ship[idx] = 0;
1408  }
1409  }
1410 
1411  // only evaluate possible kill/assist numbers if the hitting object (use_obj) is a piloted ship (ie, ignore asteroids, etc)
1412  // don't store damage a ship may do to himself
1413  if((weapon_obj->type == OBJ_WEAPON) && (use_obj->type == OBJ_SHIP)){
1414  found_slot = 0;
1415  // try and find an open slot
1416  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
1417  // if this ship object doesn't exist anymore, use the slot
1418  if((wp->damage_ship_id[idx] == -1) || (ship_get_by_signature(wp->damage_ship_id[idx]) < 0) || (wp->damage_ship_id[idx] == signature) ){
1419  found_slot = 1;
1420  break;
1421  }
1422  }
1423 
1424  // if not found (implying all slots are taken), then find the slot with the lowest damage % and use that
1425  if(!found_slot){
1426  lowest_index = 0;
1427  for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){
1428  if(wp->damage_ship[idx] < wp->damage_ship[lowest_index]){
1429  lowest_index = idx;
1430  }
1431  }
1432  } else {
1433  lowest_index = idx;
1434  }
1435 
1436  // fill in the slot damage and damager-index
1437  if(found_slot){
1438  wp->damage_ship[lowest_index] += actual_damage;
1439  } else {
1440  wp->damage_ship[lowest_index] = actual_damage;
1441  }
1442  wp->damage_ship_id[lowest_index] = signature;
1443  }
1444 }
1445 
1446 // get a scaling factor for adding/subtracting from mission score
1448 {
1449  // multiplayer dogfight. don't scale anything
1450  if(MULTI_DOGFIGHT){
1451  return 1.0f;
1452  }
1453 
1454  // check for bogus Skill_level values
1457  return Scoring_scale_factors[0];
1458  }
1459 
1460  // return the correct scale value
1462 }
1463 
1464 
1465 // ----------------------------------------------------------------------------------------
1466 // DCF functions
1467 //
1468 
1469 // bash the passed player to the specified rank
1470 void scoring_bash_rank(player *pl,int rank)
1471 {
1472  // if this is an invalid rank, do nothing
1473  if((rank < RANK_ENSIGN) || (rank > RANK_ADMIRAL)){
1474  nprintf(("General","Could not bash player rank - invalid value!!!\n"));
1475  return;
1476  }
1477 
1478  // set the player's score and rank
1479  pl->stats.score = Ranks[rank].points + 1;
1480  pl->stats.rank = rank;
1481 }
1482 
1483 DCF(rank, "changes player rank")
1484 {
1485  int rank;
1486 
1487  if (dc_optional_string_either("help", "--help")) {
1488  dc_printf("Usage: rank <index>\n");
1489  dc_printf(" <index> The rank index you wish to have. For retail ranks, these correspond to:\n");
1490  dc_printf("\t0 : Ensign\n");
1491  dc_printf("\t1 : Lieutenant Junior Grade\n");
1492  dc_printf("\t2 : Lietenant\n");
1493  dc_printf("\t3 : Lieutenant Commander\n");
1494  dc_printf("\t4 : Commander\n");
1495  dc_printf("\t5 : Captain\n");
1496  dc_printf("\t6 : Commodore\n");
1497  dc_printf("\t7 : Rear Admiral\n");
1498  dc_printf("\t8 : Vice Admiral\n");
1499  dc_printf("\t9 : Admiral\n\n");
1500  return;
1501  }
1502 
1503  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1504  if (Player != NULL) {
1505  dc_printf("Current rank is %i\n", Player->stats.rank);
1506  } else {
1507  dc_printf("Error! Current Player not active or loaded\n");
1508  }
1509  }
1510 
1511  dc_stuff_int(&rank);
1512 
1513  // parse the argument and change things around accordingly
1514  if (Player != NULL) {
1515  scoring_bash_rank(Player, rank);
1516  } else {
1517  dc_printf("Error! Current Player not active or loaded\n");
1518  }
1519 }
1520 
1522 {
1524  for(int i = 0; i<NUM_RANKS; i++) {
1525  for (it = Ranks[i].promotion_text.begin(); it != Ranks[i].promotion_text.end(); ++it) {
1526  if (it->second) {
1527  vm_free(it->second);
1528  }
1529  }
1530  Ranks[i].promotion_text.clear();
1531  }
1532 }
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
pilotfile Pilot
Definition: pilotfile.cpp:7
#define MULTIPLAYER_CLIENT
Definition: multi.h:132
int ship_info_lookup(const char *token)
Definition: ship.cpp:12772
int i
Definition: multi_pxo.cpp:466
fix Missiontime
Definition: systemvars.cpp:19
SCP_vector< medal_stuff > Medals
Definition: medals.cpp:33
#define vm_free(ptr)
Definition: pstypes.h:548
int kill_count_ok
Definition: scoring.h:90
weapon Weapons[MAX_WEAPONS]
Definition: weapons.cpp:78
void hud_gauge_popup_start(int gauge_index, int time)
Start a gauge to pop-up.
Definition: hud.cpp:3036
int team
Definition: ship.h:606
int flags
Definition: player.h:104
int kills[MAX_SHIP_CLASSES]
Definition: scoring.h:87
char name[NAME_LENGTH]
Definition: weapon.h:322
int check_for_string(const char *pstr)
Definition: parselo.cpp:517
int score
Definition: ship.h:542
int Game_mode
Definition: systemvars.cpp:24
int game_type
Definition: missionparse.h:138
void scoring_level_init(scoring_struct *scp)
Definition: scoring.cpp:223
weapon_info Weapon_info[MAX_WEAPON_TYPES]
Definition: weapons.cpp:79
net_player * Net_player
Definition: multi.cpp:94
void scoring_do_accept(scoring_struct *score)
Definition: scoring.cpp:329
Definition: weapon.h:163
player * m_player
Definition: multi.h:459
#define MAX_SHIPS
Definition: globals.h:37
char Scoring_debug_text[4096]
Definition: scoring.cpp:587
int points
Definition: scoring.h:68
GLsizei const GLfloat * points
Definition: Glext.h:7583
int scoring_eval_kill_on_weapon(object *weapon_obj, object *other_obj)
Definition: scoring.cpp:858
#define MULTI_STANDALONE(np)
Definition: multi.h:139
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
Assert(pm!=NULL)
#define mprintf(args)
Definition: pstypes.h:238
#define NUM_SKILL_LEVELS
Definition: systemvars.h:150
#define OBJ_ASTEROID
Definition: object.h:44
unsigned int mp_shots_fired
Definition: scoring.h:117
char * drop_white_space(char *str)
Definition: parselo.cpp:159
unsigned int mp_bonehead_hits
Definition: scoring.h:121
int m_kills[MAX_SHIP_CLASSES]
Definition: scoring.h:112
int multi_find_player_by_signature(int signature)
Definition: multiutil.cpp:520
int m_bonehead_kills
Definition: scoring.h:123
GLclampf f
Definition: Glext.h:7097
_fs_time_t last_flown
Definition: scoring.h:103
#define MAX_OBJECTS
Definition: globals.h:83
void compact_multitext_string(char *str)
Definition: parselo.cpp:1438
long _fs_time_t
Definition: pstypes.h:55
#define MISSION_FLAG_NO_PROMOTION
Definition: missionparse.h:69
int kill_count
Definition: scoring.h:89
void multi_team_maybe_add_score(int points, int team)
Definition: multi_team.cpp:105
float assist_award_percentage_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:139
#define f2fl(fx)
Definition: floating.h:37
#define RANK_ENSIGN
Definition: scoring.h:30
unsigned int ms_bonehead_hits
Definition: scoring.h:122
int flags
Definition: multi.h:463
unsigned int s_shots_fired
Definition: scoring.h:92
int subtype
Definition: weapon.h:326
#define Int3()
Definition: pstypes.h:292
int required_string_either(char *str1, char *str2)
Checks for one of two required strings.
Definition: parselo.cpp:673
#define AIPF_KILL_SCORING_SCALES_WITH_DAMAGE
Definition: ai_profiles.h:37
#define PLAYER_FLAGS_PROMOTED
Definition: player.h:41
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
char callsign[CALLSIGN_LEN+1]
Definition: player.h:91
unsigned int mp_shots_hit
Definition: scoring.h:119
int signature
Definition: object.h:145
SCP_vector< int > m_badge_earned
Definition: scoring.h:108
void multi_df_eval_kill(net_player *killer, object *dead_obj)
#define CLAMP(x, min, max)
Definition: pstypes.h:488
int weapon_info_index
Definition: weapon.h:164
int damage_ship_id[MAX_WEP_DAMAGE_SLOTS]
Definition: weapon.h:235
unsigned int ms_shots_fired
Definition: scoring.h:118
int m_kill_count_ok
Definition: scoring.h:115
#define SIF_BOMBER
Definition: ship.h:886
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
float Kill_percentage
Definition: scoring.cpp:41
unsigned int missions_flown
Definition: scoring.h:101
int instance
Definition: object.h:150
Definition: player.h:85
net_player_info p_info
Definition: multi.h:473
int m_okKills[MAX_SHIP_CLASSES]
Definition: scoring.h:113
int weapon_flags
Definition: weapon.h:176
int type_flags
Definition: multi.h:493
#define SIF_BIG_SHIP
Definition: ship.h:944
void scoring_backout_accept(scoring_struct *score)
Definition: scoring.cpp:378
#define NETINFO_FLAG_AM_MASTER
Definition: multi.h:599
#define HUD_KILLS_GAUGE
Definition: hudgauges.h:51
#define nprintf(args)
Definition: pstypes.h:239
int m_promotion_earned
Definition: scoring.h:109
int scoring_eval_kill(object *ship_objp)
Definition: scoring.cpp:590
#define GM_MULTIPLAYER
Definition: systemvars.h:18
int team
Definition: weapon.h:167
void scoring_level_close(int accepted)
Definition: scoring.cpp:432
#define MULTITEXT_LENGTH
Definition: globals.h:21
void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, int server_msg)
Definition: multimsgs.cpp:615
unsigned int p_shots_fired
Definition: scoring.h:91
#define MAX_SHIP_CLASSES
Definition: globals.h:48
ai_profile_t * ai_profile
Definition: missionparse.h:168
netgame_info Netgame
Definition: multi.cpp:97
#define OBJ_WEAPON
Definition: object.h:33
void stuff_string(char *outstr, int type, int len, char *terminators)
Definition: parselo.cpp:1189
#define RANK_ADMIRAL
Definition: scoring.h:39
#define WP_LASER
Definition: weapon.h:27
float hull_strength
Definition: object.h:160
int parent
Definition: object.h:147
#define CF_TYPE_TABLES
Definition: cfile.h:50
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
float damage_ship[MAX_DAMAGE_SLOTS]
Definition: ship.h:694
void scoring_add_damage_to_weapon(object *weapon_obj, object *other_obj, float damage)
Definition: scoring.cpp:1351
#define vm_strdup(ptr)
Definition: pstypes.h:549
int required_string(const char *pstr)
Definition: parselo.cpp:468
#define MAX_DAMAGE_SLOTS
Definition: ship.h:516
Definition: bmpman.h:101
float scoring_get_scale_factor()
Definition: scoring.cpp:1447
GLdouble s
Definition: Glext.h:5321
#define MAX_PLAYERS
Definition: pstypes.h:32
#define STATS_MISSION_KILLS
Definition: multi.h:369
int optional_string(const char *pstr)
Definition: parselo.cpp:539
#define MULTI_DOGFIGHT
Definition: multi.h:656
SCP_vector< int > medal_counts
Definition: scoring.h:85
bool dc_optional_string_either(const char *str1, const char *str2)
Searches for an optional string and it's alias.
void parse_rank_tbl()
Definition: scoring.cpp:58
Definition: ship.h:534
void scoreing_close()
Definition: scoring.cpp:1521
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
Definition: parselo.cpp:1995
int idx
Definition: multiui.cpp:761
void scoring_eval_badges(scoring_struct *sc)
Definition: scoring.cpp:296
int m_dogfight_kills[MAX_PLAYERS]
Definition: scoring.h:126
float assist_score_pct
Definition: ship.h:543
int iff_x_attacks_y(int team_x, int team_y)
Definition: iff_defs.cpp:605
int damage_ship_id[MAX_DAMAGE_SLOTS]
Definition: ship.h:695
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
int wi_flags
Definition: weapon.h:384
#define MULTI_MSG_ALL
Definition: multi_pmsg.h:27
int objnum
Definition: player.h:124
float total_damage_received
Definition: weapon.h:233
bool end_string_at_first_hash_symbol(char *src)
Definition: parselo.cpp:3833
#define WIF_BOMB
Definition: weapon.h:63
#define OBJ_SHIP
Definition: object.h:32
void scoring_eval_rank(scoring_struct *sc)
Definition: scoring.cpp:258
GLbitfield flags
Definition: Glext.h:6722
#define WF_ALREADY_APPLIED_STATS
Definition: weapon.h:137
#define SIF_HUGE_SHIP
Definition: ship.h:945
void reset_parse(char *text)
Definition: parselo.cpp:3305
void scoring_add_damage(object *ship_objp, object *other_obj, float damage)
Definition: scoring.cpp:492
void assign(const scoring_struct &s)
Definition: scoring.cpp:173
void dc_stuff_int(int *i)
Stuffs an int to the given variable. Supports binary (0b), hexadecimal (0x), and octal (0o) formats...
GLuint const GLchar * name
Definition: Glext.h:5608
#define AIPF_ASSIST_SCORING_SCALES_WITH_DAMAGE
Definition: ai_profiles.h:38
float total_damage_received
Definition: ship.h:693
#define MISSION_TYPE_TRAINING
Definition: missionparse.h:63
int score
Definition: weapon.h:550
void stuff_int(int *i)
Definition: parselo.cpp:2372
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
int ship_get_by_signature(int signature)
Definition: ship.cpp:16106
unsigned int flight_time
Definition: scoring.h:102
int m_medal_earned
Definition: scoring.h:107
void update_stats(scoring_struct *stats, bool training=false)
Definition: pilotfile.cpp:66
#define strcat_s(...)
Definition: safe_strings.h:68
#define NAME_LENGTH
Definition: globals.h:15
#define WP_MISSILE
Definition: weapon.h:28
#define MULTI_CONNECTED(np)
Definition: multi.h:136
player * Player
void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target, short offset)
Definition: multimsgs.cpp:6391
#define SIF_SHIP_COPY
Definition: ship.h:896
unsigned int p_shots_hit
Definition: scoring.h:94
void scoring_eval_assists(ship *sp, int killer_sig, bool is_enemy_player)
Definition: scoring.cpp:1104
float Scoring_scale_factors[NUM_SKILL_LEVELS]
Definition: scoring.cpp:48
int ship_info_index
Definition: ship.h:539
unsigned int p_bonehead_hits
Definition: scoring.h:97
#define MULTIPLAYER_MASTER
Definition: multi.h:130
SCP_map< int, char * > promotion_text
Definition: scoring.h:67
#define F_NAME
Definition: parselo.h:34
An overhauled/updated debug console to allow monitoring, testing, and general debugging of new featur...
SCP_vector< ship_info > Ship_info
Definition: ship.cpp:164
#define LOCATION
Definition: pstypes.h:245
#define SIF_FIGHTER
Definition: ship.h:885
#define NUM_RANKS
Definition: scoring.h:28
#define MAX_WEAPONS
Definition: globals.h:71
int Game_skill_level
Definition: fredstubs.cpp:170
void ignore_white_space()
Definition: parselo.cpp:77
#define STATS_DOGFIGHT_KILLS
Definition: multi.h:370
rank_stuff Ranks[NUM_RANKS]
Definition: scoring.cpp:45
float kill_percentage_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:135
object * Player_obj
Definition: object.cpp:56
void scoring_eval_hit(object *hit_obj, object *other_obj, int from_blast)
Definition: scoring.cpp:1188
int temp
Definition: lua.cpp:4996
void dc_printf(const char *format,...)
Prints the given char string to the debug console.
Definition: console.cpp:358
int m_player_deaths
Definition: scoring.h:124
float Assist_percentage
Definition: scoring.cpp:42
void HUD_printf(const char *format,...)
Definition: hudmessage.cpp:527
#define GM_NORMAL
Definition: systemvars.h:19
mission The_mission
int bonehead_kills
Definition: scoring.h:99
char type
Definition: object.h:146
#define MAX_FREESPACE2_RANK
Definition: scoring.h:42
int multi_find_player_by_object(object *objp)
Definition: multiutil.cpp:500
#define wp(p)
Definition: modelsinc.h:69
#define MISSION_FLAG_NO_TRAITOR
Definition: missionparse.h:72
int m_kill_count
Definition: scoring.h:114
float assist_percentage_scale[NUM_SKILL_LEVELS]
Definition: ai_profiles.h:136
net_player Net_players[MAX_PLAYERS]
Definition: multi.cpp:93
int Num_medals
Definition: medals.cpp:30
float damage_ship[MAX_WEP_DAMAGE_SLOTS]
Definition: weapon.h:234
unsigned int ms_shots_hit
Definition: scoring.h:120
#define F_MULTITEXT
Definition: parselo.h:43
#define GM_CAMPAIGN_MODE
Definition: systemvars.h:29
unsigned int s_shots_hit
Definition: scoring.h:95
int skip_to_string(char *pstr, char *end)
Definition: parselo.cpp:375
unsigned int s_bonehead_hits
Definition: scoring.h:98
DCF(rank,"changes player rank")
Definition: scoring.cpp:1483
_fs_time_t last_backup
Definition: scoring.h:104
#define NG_TYPE_DOGFIGHT
Definition: multi.h:651
scoring_struct stats
Definition: player.h:127
#define strcpy_s(...)
Definition: safe_strings.h:67
void scoring_bash_rank(player *pl, int rank)
Definition: scoring.cpp:1470