naev 0.12.5
player.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "physfs.h"
11#include <ctype.h>
12#include <stdlib.h>
13
14#include "naev.h"
16
17#include "player.h"
18
19#include "ai.h"
20#include "array.h"
21#include "board.h"
22#include "camera.h"
23#include "claim.h"
24#include "comm.h"
25#include "conf.h"
26#include "dialogue.h"
27#include "difficulty.h"
28#include "economy.h"
29#include "equipment.h"
30#include "escort.h"
31#include "event.h"
32#include "gui.h"
33#include "gui_omsg.h"
34#include "hook.h"
35#include "info.h"
36#include "input.h"
37#include "intro.h"
38#include "land.h"
39#include "land_outfits.h"
40#include "load.h"
41#include "log.h"
42#include "map.h"
43#include "map_overlay.h"
44#include "menu.h"
45#include "mission.h"
46#include "music.h"
47#include "ndata.h"
48#include "news.h"
49#include "nlua_outfit.h"
50#include "nlua_ship.h"
51#include "nlua_tk.h"
52#include "nlua_var.h"
53#include "nstring.h"
54#include "ntime.h"
55#include "ntracing.h"
56#include "nxml.h"
57#include "opengl.h"
58#include "pause.h"
59#include "pilot.h"
60#include "player.h"
61#include "player_autonav.h"
62#include "player_fleet.h"
63#include "player_gui.h"
64#include "player_inventory.h"
65#include "rng.h"
66#include "shiplog.h"
67#include "sound.h"
68#include "space.h"
69#include "start.h"
70#include "toolkit.h"
71#include "unidiff.h"
72#include "utf8.h"
73
74/*
75 * Player stuff
76 */
78static const Ship *player_ship =
79 NULL;
80static credits_t player_creds = 0;
81static credits_t player_payback = 0;
82static int player_ran_updater = 0;
84 NULL;
85static nlua_env scan_env = LUA_NOREF;
86
87/*
88 * Licenses.
89 */
90static char **player_licenses = NULL;
91
92/*
93 * Default radar resolution.
94 */
95#define RADAR_RES_DEFAULT 50.
96
97/*
98 * player sounds.
99 */
100static int player_engine_group = -1;
101static int player_hyper_group = -1;
102static int player_gui_group = -1;
103int snd_target = -1;
104int snd_jump = -1;
105int snd_nav = -1;
106int snd_hail = -1;
107/* Hyperspace sounds. */
108int snd_hypPowUp = -1;
109int snd_hypEng = -1;
112int snd_hypJump = -1;
113static int player_lastEngineSound = -1;
114static int player_hailCounter = 0;
115static double player_hailTimer = 0.;
116
117/*
118 * Player pilot stack (ships they have) and outfit (outfits they have) stacks
119 * (array.h)
120 */
122 NULL;
125
126/*
127 * player global properties
128 */
129/* used in input.c */
130double player_left = 0.;
131double player_right = 0.;
132double player_acc = 0.;
133/* for death and such */
134static double player_timer = 0.;
135
136/*
137 * unique mission and event stack.
138 */
139static int *missions_done =
140 NULL;
141static int *events_done =
142 NULL;
143
144/*
145 * prototypes
146 */
147/*
148 * internal
149 */
150static void player_checkHail( void );
151/* creation */
152static void player_newSetup();
153static int player_newMake( void );
154static PlayerShip_t *player_newShipMake( const char *name );
155/* sound */
156static void player_initSound( void );
157/* save/load */
158static int player_saveEscorts( xmlTextWriterPtr writer );
159static int player_saveShipSlot( xmlTextWriterPtr writer,
160 const PilotOutfitSlot *slot, int i );
161static int player_saveShip( xmlTextWriterPtr writer, PlayerShip_t *pship );
162static int player_saveMetadata( xmlTextWriterPtr writer );
163static Spob *player_parse( xmlNodePtr parent );
164static int player_parseDoneMissions( xmlNodePtr parent );
165static int player_parseDoneEvents( xmlNodePtr parent );
166static int player_parseLicenses( xmlNodePtr parent );
167static int player_parseInventory( xmlNodePtr parent );
168static void player_parseShipSlot( xmlNodePtr node, Pilot *ship,
169 PilotOutfitSlot *slot );
170static int player_parseShip( xmlNodePtr parent, int is_player );
171static int player_parseEscorts( xmlNodePtr parent );
172static int player_parseMetadata( xmlNodePtr parent );
173static void player_addOutfitToPilot( Pilot *pilot, const Outfit *outfit,
174 PilotOutfitSlot *s );
175static int player_runUpdaterScript( const char *type, const char *name, int q );
176static const Outfit *player_tryGetOutfit( const char *name, int q );
177static const Ship *player_tryGetShip( const char *name );
178static void player_tryAddLicense( const char *name );
179/* Render. */
180static void player_renderStealthUnderlay( double dt );
181static void player_renderStealthOverlay( double dt );
182static void player_renderAimHelper( double dt );
183/* Misc. */
184static int player_filterSuitableSpob( Spob *p );
185static void player_spobOutOfRangeMsg( void );
186static int player_outfitCompare( const void *arg1, const void *arg2 );
187static int player_thinkMouseFly( double dt );
188static int preemption = 0; /* Hyperspace target/untarget preemption. */
189
190/*
191 * externed
192 */
193int player_save( xmlTextWriterPtr writer ); /* save.c */
194Spob *player_load( xmlNodePtr parent ); /* save.c */
195
199int player_init( void )
200{
201 if (player_stack == NULL)
203 if (player_outfits == NULL)
206 memset( &player, 0, sizeof( PlayerShip_t ) );
207
209
210 return 0;
211}
212
216static void player_newSetup()
217{
218 double x, y;
219
220 /* Setup sound */
222
223 /* Clean up player stuff if we'll be recreating. */
225
226 /* Set up GUI. */
227 player.radar_res = RADAR_RES_DEFAULT;
229
230 /* Sane time defaults. */
231 player.last_played = time( NULL );
232 player.date_created = player.last_played;
233 player.time_since_save = player.last_played;
234 player.chapter = strdup( start_chapter() );
235
236 /* For pretty background. */
238 space_init( start_system(), 1 );
239 start_position( &x, &y );
240
241 cam_setTargetPos( x, y, 0 );
242 cam_setZoom( conf.zoom_far );
243
244 /* Clear the init message for new game. */
246}
247
256void player_new( void )
257{
258 int invalid = 1;
259
260 /* Set up new player. */
262
263 /* Some meta-data. */
264 player.date_created = time( NULL );
265
266 do {
267 const char *SAVEPATH = "_tmp";
268 char buf[PATH_MAX];
269 uint32_t c;
270 size_t i;
271 int badname;
272
273 /* Get the name. */
274 player.name = dialogue_input( _( "Player Name" ), 1, 60,
275 _( "Please write your name:" ) );
276
277 /* Player cancelled dialogue. */
278 if (player.name == NULL) {
279 menu_main();
280 return;
281 }
282
283 /* Warn about weird names, in this case, we only consider all spaces for
284 * now. */
285 badname = 1;
286 i = 0;
287 while (( c = u8_nextchar( player.name, &i ) )) {
288 if (!isspace( c )) {
289 badname = 0;
290 break;
291 }
292 }
293 if (badname &&
295 _( "Player Name" ),
296 _( "Your chosen name '%s' does not seem to be very good. Are you "
297 "sure you wish to proceed with this name?" ),
298 player.name )) {
299 player_new();
300 return;
301 }
302
303 /* Try to see if we can save the game for a valid player name. */
304 snprintf( buf, sizeof( buf ), "%s/%s", SAVEPATH, player.name );
305 if (PHYSFS_mkdir( buf ) !=
306 0) { /* In particular should be PHYSFS_ERR_BAD_FILENAME erro when
307 mkdir==0. */
308 PHYSFS_Stat stat;
309 int ret = PHYSFS_stat( buf, &stat );
310 /* When ret==0, we somehow created a directory, but we don't actually
311 * know the name nor where it is. This can happen on Windows when using
312 * a '.' as the final character. */
313 if (( ret != 0 ) && ( stat.filetype == PHYSFS_FILETYPE_DIRECTORY )) {
314 /* Here the directory should have been properly created, so we can
315 * tell the player it's good. */
316 ret = PHYSFS_delete( buf );
317 if (ret == 0)
318 WARN( _( "Unable to delete temporary file '%s': %s" ), buf,
319 PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) );
320 else {
321 ret = PHYSFS_delete( SAVEPATH );
322 if (ret == 0)
323 WARN( _( "Unable to delete temporary file '%s': %s" ),
324 SAVEPATH,
325 PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) );
326 else
327 invalid = 0;
328 }
329 }
330 }
331 if (invalid)
333 _( "'%s' is an invalid player name as it can not be saved to your "
334 "filesystem! Please choose another." ),
335 player.name );
336 PHYSFS_getLastErrorCode(); /* Clear error code. */
337 } while (invalid);
338
339 load_refresh();
340 if (array_size( load_getList( player.name ) ) > 0) {
341 int r = dialogue_YesNo(
342 _( "Overwrite" ),
343 _( "You already have a pilot named %s. Their autosave and backup save "
344 "will be overwritten. Do you wish to continue?" ),
345 player.name );
346 if (r == 0) { /* no */
347 player_new();
348 return;
349 }
350 }
351
352 if (player_newMake())
353 return;
354
355 /* Display the intro. */
356 intro_display( INTRO_PATH, "intro" );
357
358 /* Play music. */
359 music_choose( "ambient" );
360
361 /* Set loaded version. */
362 player.loaded_version = strdup( naev_version( 0 ) );
363
364 /* Add the mission if found. */
365 if (start_mission() != NULL) {
366 if (mission_start( start_mission(), NULL ) < 0)
367 WARN( _( "Failed to run start mission '%s'." ), start_mission() );
368 }
369
370 /* Add the event if found. */
371 if (start_event() != NULL) {
372 if (event_start( start_event(), NULL ))
373 WARN( _( "Failed to run start event '%s'." ), start_event() );
374 }
375
376 /* Run the load event trigger. */
377 events_trigger( EVENT_TRIGGER_LOAD );
378
379 /* Load the GUI. */
380 gui_load( gui_pick() );
381}
382
388static int player_newMake( void )
389{
390 const Ship *ship;
391 const char *shipname, *acquired;
392 double x, y;
393 PlayerShip_t *ps;
394
395 if (player_stack == NULL)
397 if (player_outfits == NULL)
399
400 /* Time. */
402 /* Clear known economy info */
404 /* Welcome message - must be before space_init. */
405 player_message( _( "#gWelcome to %s!" ), APPNAME );
406 player_message( "#g v%s", naev_version( 0 ) );
407
408 /* Try to create the pilot, if fails reask for player name. */
409 ship = ship_get( start_ship() );
410 shipname = _( start_shipname() );
411 if (ship == NULL) {
412 WARN( _( "Ship not properly set by module." ) );
413 return -1;
414 }
415 acquired = _( start_acquired() );
416 /* Setting a default name in the XML prevents naming prompt. */
417 ps = player_newShip( ship, shipname, 0, acquired,
418 ( shipname == NULL ) ? 0 : 1 );
419 if (ps == NULL) {
420 player_new();
421 return -1;
422 }
423 assert( &player.ps == ps );
424 start_position( &x, &y );
425 vec2_cset( &player.p->solid.pos, x, y );
426 vectnull( &player.p->solid.vel );
427 player.p->solid.dir = RNGF() * 2. * M_PI;
428 space_init( start_system(), 1 );
429
430 /* Bind camera. */
431 cam_setTargetPilot( player.p->id, 0 );
432
433 /* Set player speed to default 1 */
434 player.speed = conf.game_speed;
435
436 /* Reset speed (to make sure time dilation stuff is accounted for). */
438
439 /* Monies. */
440 player.p->credits = start_credits();
441
442 /* clear the map */
443 map_clear();
444
445 /* Start the economy. */
446 economy_init();
447
448 /* clear the shiplog*/
450
451 /* Start the news */
452 news_init();
453
454 return 0;
455}
456
470PlayerShip_t *player_newShip( const Ship *ship, const char *def_name, int trade,
471 const char *acquired, int noname )
472{
473 char *ship_name;
474 PlayerShip_t *ps;
475
476 /* temporary values while player doesn't exist */
477 player_creds = ( player.p != NULL ) ? player.p->credits : 0;
478 player_ship = ship;
479 if (!noname)
480 ship_name = dialogue_input( _( "Ship Name" ), 1, 60,
481 _( "Please name your new ship:" ) );
482 else
483 ship_name = NULL;
484
485 /* Dialogue cancelled. */
486 if (ship_name == NULL) {
487 int i, len;
488
489 /* No default name, fail. */
490 if (def_name == NULL)
491 return NULL;
492
493 /* Add default name. */
494 i = 2;
495 len = strlen( def_name ) + 10;
496 ship_name = malloc( len );
497 strcpy( ship_name, def_name );
498 while (player_hasShip( ship_name )) {
499 snprintf( ship_name, len, "%s %d", def_name, i );
500 i++;
501 }
502 }
503
504 /* Must not have same name. */
505 if (player_hasShip( ship_name )) {
506 dialogue_msg( _( "Name collision" ),
507 _( "Please do not give the ship the same name as another "
508 "of your ships." ) );
509 free( ship_name );
510 return NULL;
511 }
512 if (trade && player.p == NULL)
513 ERR( _( "Player ship isn't valid… This shouldn't happen!" ) );
514
515 ps = player_newShipMake( ship_name );
516 ps->autoweap = 1;
517 ps->favourite = 0;
518 ps->p->shipvar = array_create( lvar );
519 ps->acquired = ( acquired != NULL ) ? strdup( acquired ) : NULL;
520 ps->acquired_date = ntime_get();
521
522 /* Player is trading ship in. */
523 if (trade) {
524 const char *old_name = player.p->name;
525 player_swapShip( ship_name, 1 ); /* Move to the new ship. */
526 player_rmShip( old_name );
527 }
528
529 free( ship_name );
531
532 /* Update ship list if landed. */
533 if (landed && ( land_spob != NULL )) {
534 int w = land_getWid( LAND_WINDOW_EQUIPMENT );
535 equipment_regenLists( w, 0, 1 );
536 }
537
538 return ps;
539}
540
544static PlayerShip_t *player_newShipMake( const char *name )
545{
546 PilotFlags flags;
547 PlayerShip_t *ps;
548
549 /* store the current ship if it exists */
550 pilot_clearFlagsRaw( flags );
551 pilot_setFlagRaw( flags, PILOT_PLAYER );
552 pilot_setFlagRaw(
553 flags, PILOT_NO_EQUIP ); /* We want to give default outfits though. */
554
555 /* in case we're respawning */
556 player_rmFlag( PLAYER_CREATING );
557
558 /* Grow memory. */
559 ps = ( player.p == NULL ) ? &player.ps : &array_grow( &player_stack );
560 memset( ps, 0, sizeof( PlayerShip_t ) );
561 pilot_setFlagRaw( flags, PILOT_PLAYER_FLEET );
562 /* Create the ship. */
563 ps->p =
564 pilot_createEmpty( player_ship, name, faction_get( "Player" ), flags );
565 if (player.p == NULL) {
566 pilot_reset( ps->p );
567 pilot_setPlayer( ps->p );
568 }
569 /* Initialize parent weapon sets. */
570 ws_copy( ps->weapon_sets, ps->p->weapon_sets );
571
572 if (player.p == NULL)
573 ERR( _( "Something seriously wonky went on, newly created player does "
574 "not exist, bailing!" ) );
575
576 /* money. */
577 player.p->credits = player_creds;
578 player_creds = 0;
579 player_payback = 0;
580
581 return ps;
582}
583
590void player_swapShip( const char *shipname, int move_cargo )
591{
592 HookParam hparam[5];
593 Pilot *ship;
594 vec2 v;
595 double dir;
596 int removed, hyptarget;
597 PlayerShip_t *ps = NULL;
598 PlayerShip_t ptemp;
599
600 /* Try to find the ship. */
601 for (int i = 0; i < array_size( player_stack ); i++) {
602 if (strcmp( shipname, player_stack[i].p->name ) == 0) {
603 ps = &player_stack[i];
604 break;
605 }
606 }
607 if (ps == NULL) {
608 WARN( _( "Unable to swap player.p with ship '%s': ship does not exist!" ),
609 shipname );
610 return;
611 }
612
613 /* Save some variables to restore later. */
614 hyptarget = player.p->nav_hyperspace;
615
616 /* Run onremove hook for all old outfits. */
617 for (int i = 0; i < array_size( player.p->outfits ); i++)
618 pilot_outfitLRemove( player.p, player.p->outfits[i] );
619
620 /* Get rid of deployed escorts and swap existing escorts. */
622 escort_freeList( ps->p );
623 ps->p->escorts = array_create( Escort_t );
624 /* Just copying the array over has unforeseen consequences, so recreate. */
625 for (int i = 0; i < array_size( player.p->escorts ); i++) {
626 const Escort_t *e = &player.p->escorts[i];
627 Escort_t ne = *e;
628
629 /* Must not be new ship. */
630 if (e->id == ps->p->id)
631 continue;
632
633 ne.ship = e->ship; /* Might be worth having an escort_copy function. */
634 array_push_back( &ps->p->escorts, ne );
635 }
637
638 /* Swap information over. */
639 ptemp = player.ps;
640 player.ps = *ps;
641 *ps = ptemp;
642 ship = player.ps.p;
643
644 /* Move credits over */
645 ship->credits = player.p->credits;
646
647 /* Copy target info */
648 ship->target = player.p->target;
649 ship->nav_spob = player.p->nav_spob;
650 ship->nav_hyperspace = player.p->nav_hyperspace;
651 ship->nav_anchor = player.p->nav_anchor;
652 ship->nav_asteroid = player.p->nav_asteroid;
653
654 /* Store position. */
655 v = player.p->solid.pos;
656 dir = angle_clean( player.p->solid.dir );
657
658 /* Copy over weapon sets. */
659 ws_copy( player.ps.p->weapon_sets, player.ps.weapon_sets );
660
661 /* If the pilot is deployed, we must redeploy. */
662 removed = 0;
663 if (ps->p->id > 0) {
664 pilot_stackRemove( ps->p );
665 removed = 1;
666 }
667 pilot_setPlayer( ship );
668 player.ps.deployed = 0; /* Player themselves can't be deployed. */
669 if (ps->deployed)
670 pfleet_deploy( ps );
671
672 /* Extra pass to calculate stats */
674 pilot_calcStats( ps->p );
675
676 /* Run onadd hook for all new outfits. */
677 for (int j = 0; j < array_size( ship->outfits ); j++)
678 pilot_outfitLAdd( ship, ship->outfits[j] );
679
680 /* Move cargo over. */
681 if (move_cargo) {
682 pilot_cargoMoveRaw( player.p, ps->p );
683 pfleet_update(); /* Update fleet and move cargo. */
684 }
685
686 /* Clean up, AFTER cargo is updated. */
687 if (!ps->deployed && removed)
688 pilot_free( ps->p ); /* Has PILOT_NOFREE flag. */
689
690 /* Copy position back. */
691 player.p->solid.pos = v;
692 player.p->solid.dir = dir;
693
694 /* Fill the tank. */
695 if (landed && ( land_spob != NULL ))
696 land_refuel();
697
698 /* Clear targets. */
700 player.p->nav_hyperspace =
701 hyptarget; /* Special case restore hyperspace target. */
702
703 /* Set some gui stuff. */
704 gui_load( gui_pick() );
705
706 /* Bind camera. */
707 cam_setTargetPilot( player.p->id, 0 );
708
709 /* Recompute stuff if necessary. */
712
713 /* Run hook. */
714 hparam[0].type = HOOK_PARAM_STRING;
715 hparam[0].u.str = player.p->name;
716 hparam[1].type = HOOK_PARAM_SHIP;
717 hparam[1].u.ship = player.p->ship;
718 hparam[2].type = HOOK_PARAM_STRING;
719 hparam[2].u.str = ps->p->name;
720 hparam[3].type = HOOK_PARAM_SHIP;
721 hparam[3].u.ship = ps->p->ship;
722 hparam[4].type = HOOK_PARAM_SENTINEL;
723 hooks_runParam( "ship_swap", hparam );
724}
725
733credits_t player_shipPrice( const char *shipname, int count_unique )
734{
735 Pilot *ship = NULL;
736
737 if (strcmp( shipname, player.p->name ) == 0)
738 ship = player.p;
739 else {
740 /* Find the ship. */
741 for (int i = 0; i < array_size( player_stack ); i++) {
742 if (strcmp( shipname, player_stack[i].p->name ) == 0) {
743 ship = player_stack[i].p;
744 break;
745 }
746 }
747 }
748
749 /* Not found. */
750 if (ship == NULL) {
751 WARN( _( "Unable to find price for player's ship '%s': ship does not "
752 "exist!" ),
753 shipname );
754 return -1;
755 }
756
757 return pilot_worth( ship, count_unique );
758}
759
760void player_rmPlayerShip( PlayerShip_t *ps )
761{
762 if (ps->p != NULL) {
763 pilot_rmFlag( ps->p, PILOT_NOFREE );
764 pilot_free( ps->p );
765 }
766 ws_free( ps->weapon_sets );
767 free( ps->acquired );
768}
769
775void player_rmShip( const char *shipname )
776{
777 for (int i = 0; i < array_size( player_stack ); i++) {
778 PlayerShip_t *ps = &player_stack[i];
779
780 /* Not the ship we are looking for. */
781 if (strcmp( shipname, ps->p->name ) != 0)
782 continue;
783
784 /* Free player ship. */
785 player_rmPlayerShip( ps );
786
787 array_erase( &player_stack, ps, ps + 1 );
788 }
789
790 /* Update ship list if landed. */
791 if (landed && ( land_spob != NULL )) {
792 int w = land_getWid( LAND_WINDOW_EQUIPMENT );
793 equipment_regenLists( w, 0, 1 );
794 }
795}
796
800void player_cleanup( void )
801{
802 /* Enable all input. */
804
805 /* Clean up other stuff. */
806 land_cleanup(); /* Should be first. */
807 diff_clear();
808 var_cleanup();
812 map_cleanup();
814
815 /* Reset controls. */
817 player_left = 0.;
818 player_right = 0.;
819
820 /* Clear player. */
821 player_clear();
822
823 /* Clear hail timer. */
825 player_hailTimer = 0.;
826
827 /* Clear messages. */
829
830 /* Reset factions. */
832
833 /* Free stuff. */
834 free( player.name );
835 player.name = NULL;
836 free( player.ps.acquired );
837 player.ps.acquired = NULL;
838 ws_free( player.ps.weapon_sets );
839
840 free( player_message_noland );
842
843 /* Clean up gui. */
844 gui_cleanup();
846 ovr_setOpen( 0 );
847
848 /* Clear up info buttons. */
850
852 player_outfits = NULL;
853
855 missions_done = NULL;
856
858 events_done = NULL;
859
860 /* Clean up licenses. */
861 for (int i = 0; i < array_size( player_licenses ); i++)
862 free( player_licenses[i] );
864 player_licenses = NULL;
865
866 /* Clear claims. */
867 claim_clear();
868
869 /* Purge the pilot stack, and player.p. */
871
872 /* clean up the stack */
873 for (int i = 0; i < array_size( player_stack ); i++)
874 player_rmPlayerShip( &player_stack[i] );
876 player_stack = NULL;
877 /* nothing left */
878
879 /* Reset some player stuff. */
880 player_creds = 0;
881 player_payback = 0;
882 free( player.gui );
883 player.gui = NULL;
884 free( player.chapter );
885 player.chapter = NULL;
886 free( player.difficulty );
887 player.difficulty = NULL;
888
889 /* Clear omsg. */
890 omsg_cleanup();
891
892 /* Stop the sounds. */
894
895 /* Clean up local difficulty. */
896 difficulty_setLocal( NULL );
897
898 /* Reset time compression. */
900 pause_setSpeed( 1. );
901 sound_setSpeed( 1. );
902
903 free( player.loaded_version );
904 player.loaded_version = NULL;
905
906 /* Clean up. */
907 memset( &player, 0, sizeof( Player_t ) );
908 player_setFlag( PLAYER_CREATING );
909}
910
912 0;
916static void player_initSound( void )
917{
919 return;
920
921 /* Allocate channels. */
923 sound_createGroup( 1 ); /* Channel for engine noises. */
926 sound_speedGroup( player_gui_group, 0 ); /* Disable pitch shift. */
928
929 /* Get sounds. */
930 snd_target = sound_get( "target" );
931 snd_jump = sound_get( "jump" );
932 snd_nav = sound_get( "nav" );
933 snd_hail = sound_get( "hail" );
934 snd_hypPowUp = sound_get( "hyperspace_powerup" );
935 snd_hypEng = sound_get( "hyperspace_engine" );
936 snd_hypPowDown = sound_get( "hyperspace_powerdown" );
937 snd_hypPowUpJump = sound_get( "hyperspace_powerupjump" );
938 snd_hypJump = sound_get( "hyperspace_jump" );
939}
940
947void player_soundPlayGUI( int sound, int once )
948{
949 sound_playGroup( player_gui_group, sound, once );
950}
951
958void player_soundPlay( int sound, int once )
959{
960 sound_playGroup( player_hyper_group, sound, once );
961}
962
967{
968 if (player_gui_group >= 0)
970 if (player_engine_group >= 0)
972 if (player_hyper_group >= 0)
974
975 /* No last engine sound. */
977}
978
989
1000
1007void player_warp( double x, double y )
1008{
1009 unsigned int target = cam_getTarget();
1010 vec2_cset( &player.p->solid.pos, x, y );
1011 /* Have to move camera over to avoid moving stars when loading. */
1012 if (target == player.p->id)
1013 cam_setTargetPilot( target, 0 );
1014}
1015
1019void player_clear( void )
1020{
1021 if (player.p != NULL) {
1022 pilot_setTarget( player.p, player.p->id );
1023 gui_setTarget();
1024 }
1025
1026 /* Clear the noland flag. */
1027 player_rmFlag( PLAYER_NOLAND );
1028}
1029
1036int player_hasCredits( credits_t amount )
1037{
1038 return pilot_hasCredits( player.p, amount );
1039}
1040
1047credits_t player_modCredits( credits_t amount )
1048{
1049 return pilot_modCredits( player.p, amount );
1050}
1051
1055void player_render( double dt )
1056{
1057 /*
1058 * Check to see if the death menu should pop up.
1059 */
1060 if (player_isFlag( PLAYER_DESTROYED )) {
1061 player_timer -= dt;
1062 if (!toolkit_isOpen() && !player_isFlag( PLAYER_CREATING ) &&
1063 ( player_timer < 0. ))
1064 menu_death();
1065 }
1066
1067 /* Skip rendering. */
1068 if (( player.p == NULL ) || ( player.p->id == 0 ) ||
1069 player_isFlag( PLAYER_CREATING ) || pilot_isFlag( player.p, PILOT_HIDE ))
1070 return;
1071
1072 NTracingZone( _ctx, 1 );
1073
1074 /* Render stealth overlay. */
1075 if (pilot_isFlag( player.p, PILOT_STEALTH ))
1077
1078 /* Render the aiming lines. */
1079 if (( player.p->target != PLAYER_ID ) && player.p->aimLines &&
1080 !pilot_isFlag( player.p, PILOT_HYPERSPACE ) &&
1081 !pilot_isFlag( player.p, PILOT_DISABLED ) &&
1082 !pilot_isFlag( player.p, PILOT_LANDING ) &&
1083 !pilot_isFlag( player.p, PILOT_TAKEOFF ) &&
1084 !player_isFlag( PLAYER_CINEMATICS_GUI ))
1086
1087 /* Render the player's pilot. */
1088 pilot_render( player.p );
1089
1090 /* Render the player's overlay. */
1092
1093 NTracingZoneEnd( _ctx );
1094}
1095
1099void player_renderUnderlay( double dt )
1100{
1101 /* Skip rendering. */
1102 if (( player.p == NULL ) || player_isFlag( PLAYER_CREATING ) ||
1103 pilot_isFlag( player.p, PILOT_HIDE ))
1104 return;
1105
1106 if (pilot_isFlag( player.p, PILOT_STEALTH ))
1108}
1109
1113static void player_renderStealthUnderlay( double dt )
1114{
1115 (void)dt;
1116 double detectz;
1117 glColour col;
1118 Pilot *const *ps;
1119
1120 /* Don't display if overlay is open. */
1121 if (ovr_isOpen() || player_isFlag( PLAYER_CINEMATICS ))
1122 return;
1123
1124 /* Iterate and draw for all pilots. */
1125 detectz = player.p->ew_stealth * cam_getZoom();
1126 col = cRed;
1127 col.a = 0.3;
1128 ps = pilot_getAll();
1129 for (int i = 0; i < array_size( ps ); i++) {
1130 double x, y, r;
1131 Pilot *t = ps[i];
1132 if (pilot_isFriendly( t ))
1133 continue;
1134 if (pilot_isDisabled( t ))
1135 continue;
1136 /* Only show pilots the player can see. */
1137 if (!pilot_validTarget( player.p, t ))
1138 continue;
1139
1140 gl_gameToScreenCoords( &x, &y, t->solid.pos.x, t->solid.pos.y );
1141 r = detectz * t->stats.ew_detect;
1142 if (r > 0.) {
1143 glUseProgram( shaders.stealthaura.program );
1144 gl_renderShader( x, y, r, r, 0., &shaders.stealthaura, &col, 1 );
1145 }
1146 }
1147}
1148
1152static void player_renderStealthOverlay( double dt )
1153{
1154 (void)dt;
1155 double x, y, r, st, z;
1156 glColour col;
1157
1158 z = cam_getZoom();
1159 gl_gameToScreenCoords( &x, &y, player.p->solid.pos.x,
1160 player.p->solid.pos.y );
1161
1162 /* Determine the arcs. */
1163 st = player.p->ew_stealth_timer;
1164
1165 /* We do red to yellow. */
1166 col_blend( &col, &cYellow, &cRed, st );
1167 col.a = 0.5;
1168
1169 /* Determine size. */
1170 r = 1.2 / 2. * (double)player.p->ship->size;
1171
1172 /* Draw the main circle. */
1173 glUseProgram( shaders.stealthmarker.program );
1174 glUniform1f( shaders.stealthmarker.paramf, st );
1175 gl_renderShader( x, y, r * z, r * z, 0., &shaders.stealthmarker, &col, 1 );
1176}
1177
1181static void player_renderAimHelper( double dt )
1182{
1183 (void)dt;
1184 double a, b, d, x1, y1, x2, y2, r, theta;
1185 glColour c, c2;
1186 Pilot *target;
1187
1188 target = pilot_getTarget( player.p );
1189 if (target == NULL)
1190 return;
1191
1192 a = player.p->solid.dir;
1193 r = 200.;
1194 gl_gameToScreenCoords( &x1, &y1, player.p->solid.pos.x,
1195 player.p->solid.pos.y );
1196
1197 b = pilot_aimAngle( player.p, &target->solid.pos, &target->solid.vel );
1198
1199 theta = 22. * M_PI / 180.;
1200
1201 /* The angular error will give the exact colour that is used. */
1202 d = ABS( angle_diff( a, b ) / ( 2 * theta ) );
1203 d = MIN( 1, d );
1204
1205 c = cInert;
1206 c.a = 0.3;
1207 gl_gameToScreenCoords( &x2, &y2,
1208 player.p->solid.pos.x + r * cos( a + theta ),
1209 player.p->solid.pos.y + r * sin( a + theta ) );
1210 gl_renderLine( x1, y1, x2, y2, &c );
1211 gl_gameToScreenCoords( &x2, &y2,
1212 player.p->solid.pos.x + r * cos( a - theta ),
1213 player.p->solid.pos.y + r * sin( a - theta ) );
1214 gl_renderLine( x1, y1, x2, y2, &c );
1215
1216 c.r = d * 0.9;
1217 c.g = d * 0.2 + ( 1. - d ) * 0.8;
1218 c.b = ( 1 - d ) * 0.2;
1219 c.a = 0.7;
1220 col_gammaToLinear( &c );
1221 gl_gameToScreenCoords( &x2, &y2, player.p->solid.pos.x + r * cos( a ),
1222 player.p->solid.pos.y + r * sin( a ) );
1223
1224 gl_renderLine( x1, y1, x2, y2, &c );
1225
1226 c2 = cWhite;
1227 c2.a = 0.7;
1228 glUseProgram( shaders.crosshairs.program );
1229 glUniform1f( shaders.crosshairs.paramf, 1. );
1230 gl_renderShader( x2, y2, 7, 7, 0., &shaders.crosshairs, &c2, 1 );
1231
1232 gl_gameToScreenCoords( &x2, &y2, player.p->solid.pos.x + r * cos( b ),
1233 player.p->solid.pos.y + r * sin( b ) );
1234
1235 c.a = 0.4;
1236 gl_renderLine( x1, y1, x2, y2, &c );
1237
1238 /* TODO this should be converted into a single SDF call. */
1239 glColour c3 = cBlack;
1240 c3.a = c2.a;
1241 gl_renderCircle( x2, y2, 8., &c3, 0 );
1242 gl_renderCircle( x2, y2, 10., &c3, 0 );
1243 gl_renderCircle( x2, y2, 9., &c2, 0 );
1244}
1245
1252void player_think( Pilot *pplayer, const double dt )
1253{
1254 Pilot *target;
1255 int facing;
1256
1257 /* last i heard, the dead don't think */
1258 if (pilot_isFlag( pplayer, PILOT_DEAD )) {
1259 /* no sense in accelerating or turning */
1260 pilot_setAccel( pplayer, 0. );
1261 pilot_setTurn( pplayer, 0. );
1262 return;
1263 }
1264
1265 /* We always have to run ai_think in the case the player has escorts so that
1266 * they properly form formations, however, we only have to do the task under
1267 * manual control.. */
1268 ai_think( pplayer, dt, pilot_isFlag( pplayer, PILOT_MANUAL_CONTROL ) );
1269
1270 /* Under manual control is special. */
1271 if (pilot_isFlag( pplayer, PILOT_MANUAL_CONTROL ) ||
1272 pilot_isFlag( pplayer, PILOT_HIDE ))
1273 return;
1274
1275 /* Not facing anything yet. */
1276 facing = 0;
1277
1278 /* Autonav takes over normal controls. */
1279 if (player_isFlag( PLAYER_AUTONAV )) {
1280 player_thinkAutonav( pplayer, dt );
1281
1282 /* Disable turning. */
1283 facing = 1;
1284 }
1285
1286 /* Mouse-flying is enabled. */
1287 if (!facing && player_isFlag( PLAYER_MFLY ))
1288 facing = player_thinkMouseFly( dt );
1289
1290 /* turning taken over by PLAYER_FACE */
1291 if (!facing && player_isFlag( PLAYER_FACE )) {
1292 /* Try to face pilot target. */
1293 if (player.p->target != PLAYER_ID) {
1294 target = pilot_getTarget( player.p );
1295 if (target != NULL) {
1296 pilot_face( pplayer,
1297 vec2_angle( &player.p->solid.pos, &target->solid.pos ),
1298 dt );
1299
1300 /* Disable turning. */
1301 facing = 1;
1302 }
1303 }
1304 /* Try to face asteroid. */
1305 else if (player.p->nav_asteroid != -1) {
1306 AsteroidAnchor *field = &cur_system->asteroids[player.p->nav_anchor];
1307 Asteroid *ast = &field->asteroids[player.p->nav_asteroid];
1308 pilot_face( pplayer, vec2_angle( &player.p->solid.pos, &ast->sol.pos ),
1309 dt );
1310 /* Disable turning. */
1311 facing = 1;
1312 }
1313 /* If not try to face spob target. */
1314 else if (( player.p->nav_spob != -1 ) &&
1315 ( ( preemption == 0 ) || ( player.p->nav_hyperspace == -1 ) )) {
1316 pilot_face( pplayer,
1317 vec2_angle( &player.p->solid.pos,
1318 &cur_system->spobs[player.p->nav_spob]->pos ),
1319 dt );
1320 /* Disable turning. */
1321 facing = 1;
1322 } else if (player.p->nav_hyperspace != -1) {
1323 pilot_face(
1324 pplayer,
1325 vec2_angle( &player.p->solid.pos,
1326 &cur_system->jumps[player.p->nav_hyperspace].pos ),
1327 dt );
1328 /* Disable turning. */
1329 facing = 1;
1330 }
1331 }
1332
1333 /* turning taken over by PLAYER_REVERSE */
1334 if (player_isFlag( PLAYER_REVERSE )) {
1335 /*
1336 * If the player has reverse thrusters, fire those.
1337 */
1338 if (!player.p->stats.misc_reverse_thrust && !facing) {
1339 pilot_face( pplayer, VANGLE( player.p->solid.vel ) + M_PI, dt );
1340 /* Disable turning. */
1341 facing = 1;
1342 }
1343 }
1344
1345 /* Normal turning scheme */
1346 if (!facing) {
1347 double turn = 0;
1348 if (player_isFlag( PLAYER_TURN_LEFT ))
1349 turn -= player_left;
1350 if (player_isFlag( PLAYER_TURN_RIGHT ))
1351 turn += player_right;
1352 turn = CLAMP( -1., 1., turn );
1353 pilot_setTurn( pplayer, -turn );
1354 }
1355
1356 /*
1357 * Weapon shooting stuff
1358 */
1359 if (!player_isFlag( PLAYER_AUTONAV )) {
1360 double acc = player_acc;
1361 /* Have to handle the case the player is doing reverse. This takes
1362 * priority over normal accel. */
1363 if (player_isFlag( PLAYER_REVERSE ) &&
1364 player.p->stats.misc_reverse_thrust &&
1365 !pilot_isFlag( player.p, PILOT_HYP_PREP ) &&
1366 !pilot_isFlag( player.p, PILOT_HYPERSPACE ))
1367 acc = -PILOT_REVERSE_THRUST;
1368
1369 pilot_setAccel( pplayer, acc );
1370 }
1371}
1372
1379void player_update( Pilot *pplayer, const double dt )
1380{
1381 /* Update normally. */
1382 pilot_update( pplayer, dt );
1383
1384 /* Update player.p specific stuff. */
1385 if (!player_isFlag( PLAYER_DESTROYED ))
1386 player_updateSpecific( pplayer, dt );
1387}
1388
1395void player_updateSpecific( Pilot *pplayer, const double dt )
1396{
1397 int engsound;
1398 double pitch = 1.;
1399 Pilot *t = pilot_getTarget( pplayer );
1400
1401 /* Set special flag if scanned by player. */
1402 if (( t != NULL ) && !pilot_isFlag( t, PILOT_PLAYER_SCANNED ) &&
1403 pilot_ewScanCheck( pplayer )) {
1404 pilot_setFlag( t, PILOT_PLAYER_SCANNED );
1405 }
1406
1407 /* Calculate engine sound to use. */
1408 if (pilot_isFlag( pplayer, PILOT_AFTERBURNER ))
1409 engsound = pplayer->afterburner->outfit->u.afb.sound;
1410 else if (pilot_isFlag( pplayer, PILOT_HYPERSPACE ))
1411 engsound = snd_hypEng;
1412 else if (pplayer->engine_glow > 0.) {
1413 engsound = pplayer->ship->sound;
1414 pitch = pplayer->ship->engine_pitch;
1415 } else
1416 engsound = -1;
1417 if (engsound >= 0)
1419 conf.engine_vol * pplayer->engine_glow );
1420 /* See if sound must change. */
1421 if (player_lastEngineSound != engsound) {
1423 if (engsound >= 0) {
1425 sound_playGroup( player_engine_group, engsound, 0 );
1426 }
1427 }
1428 player_lastEngineSound = engsound;
1429
1430 /* Sound. */
1431 /*
1432 * Sound is now camera-specific and thus not player specific. A bit sad
1433 really. sound_updateListener( pplayer->solid.dir, pplayer->solid.pos.x,
1434 pplayer->solid.pos.y, pplayer->solid.vel.x, pplayer->solid.vel.y );
1435 */
1436
1437 /* See if must play hail sound. */
1438 if (player_hailCounter > 0) {
1439 player_hailTimer -= dt;
1440 if (player_hailTimer < 0.) {
1443 player_hailTimer = 3.;
1444 }
1445 }
1446
1447 /* Handle passive scanning of nearby asteroids. */
1448 /* TODO should probably handle player escorts in the future. */
1449 if (player.p->stats.asteroid_scan > 0.) {
1450 double range = player.p->stats.asteroid_scan;
1451 for (int i = 0; i < array_size( cur_system->asteroids ); i++) {
1452 double r2;
1453 AsteroidAnchor *ast = &cur_system->asteroids[i];
1454
1455 /* Field out of range. */
1456 if (vec2_dist2( &ast->pos, &player.p->solid.pos ) >
1457 pow2( range + ast->radius + ast->margin ))
1458 continue;
1459
1460 r2 = pow2( range );
1461 for (int j = 0; j < array_size( ast->asteroids ); j++) {
1462 HookParam hparam[2];
1463 Asteroid *a = &ast->asteroids[j];
1464
1465 if (a->scanned) /* Ignore scanned outfits. */
1466 continue;
1467
1468 if (vec2_dist2( &a->sol.pos, &player.p->solid.pos ) > r2)
1469 continue;
1470
1471 a->scanned = 1;
1472
1473 /* Run the hook. */
1474 hparam[0].type = HOOK_PARAM_ASTEROID;
1475 hparam[0].u.ast.parent = ast->id;
1476 hparam[0].u.ast.id = a->id;
1477 hparam[1].type = HOOK_PARAM_SENTINEL;
1478 hooks_runParamDeferred( "asteroid_scan", hparam );
1479 }
1480 }
1481 }
1482}
1483
1484/*
1485 * For use in keybindings
1486 */
1491void player_weapSetPress( int id, double value, int repeat )
1492{
1493 int type;
1494
1495 if (player.p == NULL)
1496 return;
1497
1498 type = ( value >= 0 ) ? +1 : -1;
1499 if (repeat)
1500 type = 2;
1501
1502 if (type > 0) {
1503 if (toolkit_isOpen())
1504 return;
1505
1506 if (( pilot_isFlag( player.p, PILOT_HYP_PREP ) ||
1507 pilot_isFlag( player.p, PILOT_HYPERSPACE ) ||
1508 pilot_isFlag( player.p, PILOT_LANDING ) ||
1509 pilot_isFlag( player.p, PILOT_TAKEOFF ) ))
1510 return;
1511 }
1512
1513 /* Only update if necessary. */
1514 if (pilot_weapSetPress( player.p, id, type ))
1516}
1517
1522{
1523 double spd = player.speed * player_dt_default();
1524 pause_setSpeed( spd );
1525 sound_setSpeed( spd / conf.game_speed );
1526 player.speed_autonav = 1.;
1527}
1528
1535void player_restoreControl( int reason, const char *str )
1536{
1537 if (player.p == NULL)
1538 return;
1539
1540 if (reason != PINPUT_AUTONAV) {
1541 /* Autonav should be harder to abort when paused. */
1542 if (( !paused || reason != PINPUT_MOVEMENT ))
1543 player_autonavAbort( str );
1544 }
1545
1546 if (reason != PINPUT_BRAKING) {
1547 pilot_rmFlag( player.p, PILOT_BRAKING );
1548 pilot_rmFlag( player.p, PILOT_COOLDOWN_BRAKE );
1549 if (pilot_isFlag( player.p, PILOT_COOLDOWN ))
1550 pilot_cooldownEnd( player.p, str );
1551 }
1552}
1553
1560{
1561 int old;
1562
1563 /* Player must exist. */
1564 if (player.p == NULL)
1565 return;
1566
1567 if (id >= array_size( cur_system->spobs )) {
1568 WARN( _( "Trying to set player's spob target to invalid ID '%d'" ), id );
1569 return;
1570 }
1571
1572 if (( player.p == NULL ) || pilot_isFlag( player.p, PILOT_LANDING ))
1573 return;
1574
1575 old = player.p->nav_spob;
1576 player.p->nav_spob = id;
1577 player_hyperspacePreempt( ( id < 0 ) ? 1 : 0 );
1578 if (old != id) {
1579 player_rmFlag( PLAYER_LANDACK );
1580 if (id >= 0)
1582 }
1584 gui_setNav();
1585
1586 if (player.autonav == AUTONAV_SPOB)
1587 player_autonavAbort( NULL );
1588}
1589
1596void player_targetAsteroidSet( int field, int id )
1597{
1598 int old;
1599
1600 if (( player.p == NULL ) || pilot_isFlag( player.p, PILOT_LANDING ))
1601 return;
1602
1603 old = player.p->nav_asteroid;
1604 player.p->nav_asteroid = id;
1605 if (old != id) {
1606 if (id >= 0) {
1608 }
1609 }
1610
1611 player.p->nav_anchor = field;
1612
1613 /* Untarget pilot. */
1614 player.p->target = player.p->id;
1615}
1616
1621{
1622 int id;
1623
1624 /* Not under manual control. */
1625 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ))
1626 return;
1627
1628 /* Find next spob target. */
1629 for (id = player.p->nav_spob + 1; id < array_size( cur_system->spobs ); id++)
1630 if (spob_isKnown( cur_system->spobs[id] ))
1631 break;
1632
1633 /* Try to select the lowest-indexed valid spob. */
1634 if (id >= array_size( cur_system->spobs )) {
1635 id = -1;
1636 for (int i = 0; i < array_size( cur_system->spobs ); i++)
1637 if (spob_isKnown( cur_system->spobs[i] )) {
1638 id = i;
1639 break;
1640 }
1641 }
1642
1643 /* Untarget if out of range. */
1645}
1646
1653int player_land( int loud )
1654{
1655 Spob *spob;
1656 int silent = 0; /* Whether to suppress the land ack noise. */
1657
1658 if (landed) { /* player is already landed */
1659 takeoff( 1, 0 );
1660 return PLAYER_LAND_DENIED;
1661 }
1662
1663 /* Not under manual control or disabled. */
1664 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) ||
1665 pilot_isDisabled( player.p ))
1666 return PLAYER_LAND_DENIED;
1667
1668 /* Already landing. */
1669 if (( pilot_isFlag( player.p, PILOT_LANDING ) ||
1670 pilot_isFlag( player.p, PILOT_TAKEOFF ) ))
1671 return PLAYER_LAND_DENIED;
1672
1673 /* Check if there are spobs to land on. */
1674 if (array_size( cur_system->spobs ) == 0) {
1675 player_message( "#r%s", _( "There are no spobs to land on." ) );
1676 return PLAYER_LAND_DENIED;
1677 }
1678
1679 if (player_isFlag( PLAYER_NOLAND )) {
1681 return PLAYER_LAND_DENIED;
1682 } else if (pilot_isFlag( player.p, PILOT_NOLAND )) {
1683 player_message( "#r%s",
1684 _( "Docking stabilizers malfunctioning, cannot land." ) );
1685 return PLAYER_LAND_DENIED;
1686 }
1687
1688 /* No target means no land. */
1689 if (player.p->nav_spob == -1)
1690 return PLAYER_LAND_DENIED;
1691 /* Check if spob is in range when not uninhabited. */
1692 else if (!spob_isFlag( cur_system->spobs[player.p->nav_spob],
1693 SPOB_UNINHABITED ) &&
1694 !pilot_inRangeSpob( player.p, player.p->nav_spob )) {
1696 return PLAYER_LAND_AGAIN;
1697 }
1698
1699 /* attempt to land at selected spob */
1700 spob = cur_system->spobs[player.p->nav_spob];
1701 spob_updateLand( spob ); /* Update if necessary. */
1702 if (( spob->lua_can_land == LUA_NOREF ) &&
1703 !spob_hasService( spob, SPOB_SERVICE_LAND )) {
1704 player_message( "#r%s", _( "You can't land here." ) );
1705 return PLAYER_LAND_DENIED;
1706 } else if (( spob->lua_can_land != LUA_NOREF ) && !spob->can_land) {
1707 if (spob->land_msg)
1708 player_message( _( "#%c%s>#0 %s" ), spob_getColourChar( spob ),
1709 spob_name( spob ), spob->land_msg );
1710 else
1711 player_message( "#r%s", _( "You can't land here." ) );
1712 return PLAYER_LAND_DENIED;
1713 } else if (!player_isFlag( PLAYER_LANDACK )) { /* no landing authorization */
1714 if (spob_hasService( spob,
1715 SPOB_SERVICE_INHABITED )) { /* Basic services */
1716 if (spob->can_land)
1717 player_message( _( "#%c%s>#0 %s" ), spob_getColourChar( spob ),
1718 spob_name( spob ), spob->land_msg );
1719 else if (spob->land_override > 0)
1720 player_message( _( "#%c%s>#0 %s" ), spob_getColourChar( spob ),
1721 spob_name( spob ), _( "Landing authorized." ) );
1722 else { /* Hostile */
1723 player_message( _( "#%c%s>#0 %s" ), spob_getColourChar( spob ),
1724 spob_name( spob ), spob->land_msg );
1725 return PLAYER_LAND_DENIED;
1726 }
1727 } else /* No shoes, no shirt, no lifeforms, no service. */
1728 player_message( _( "#oReady to land on %s." ), spob_name( spob ) );
1729
1730 player_setFlag( PLAYER_LANDACK );
1731 if (!silent)
1733
1734 return player_land( loud );
1735 } else if (vec2_dist2( &player.p->solid.pos, &spob->pos ) >
1736 pow2( spob->radius )) {
1737 if (loud)
1738 player_message( _( "#rYou are too far away to land on %s." ),
1739 spob_name( spob ) );
1740 return PLAYER_LAND_AGAIN;
1741 } else if (vec2_odist2( &player.p->solid.vel ) >
1742 pow2( MAX_HYPERSPACE_VEL )) {
1743 if (loud)
1744 player_message( _( "#rYou are going too fast to land on %s." ),
1745 spob_name( spob ) );
1746 return PLAYER_LAND_AGAIN;
1747 }
1748
1749 /* End autonav. */
1751
1752 /* Stop afterburning. */
1754 /* Stop accelerating. */
1756 /* Stop stealth. */
1758
1759 /* Stop all on outfits. */
1760 if (pilot_outfitOffAll( player.p ) > 0)
1762
1763 /* Do whatever the spob wants to do. */
1764 if (spob->lua_land != LUA_NOREF) {
1765 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spob->lua_land ); /* f */
1766 lua_pushspob( naevL, spob_index( spob ) );
1767 lua_pushpilot( naevL, player.p->id );
1768 if (nlua_pcall( spob->lua_env, 2, 0 )) {
1769 WARN( _( "Spob '%s' failed to run '%s':\n%s" ), spob->name, "land",
1770 lua_tostring( naevL, -1 ) );
1771 lua_pop( naevL, 1 );
1772 }
1773
1774 return PLAYER_LAND_OK;
1775 }
1776
1777 /* Start landing. */
1779 player.p->landing_delay = PILOT_LANDING_DELAY * player_dt_default();
1780 player.p->ptimer = player.p->landing_delay;
1781 pilot_setFlag( player.p, PILOT_LANDING );
1782 pilot_setAccel( player.p, 0. );
1783 pilot_setTurn( player.p, 0. );
1784
1785 return PLAYER_LAND_OK;
1786}
1787
1792{
1793 Spob *p;
1794
1795 /* No authorization to revoke. */
1796 if (( player.p == NULL ) || !player_isFlag( PLAYER_LANDACK ))
1797 return;
1798
1799 /* Avoid a potential crash if PLAYER_LANDACK is set inappropriately. */
1800 if (player.p->nav_spob < 0) {
1801 WARN( _( "Player has landing permission, but no valid spob targeted." ) );
1802 return;
1803 }
1804
1805 p = cur_system->spobs[player.p->nav_spob];
1806
1807 /* Player can still land. */
1808 if (p->can_land || ( p->land_override > 0 ))
1809 return;
1810
1811 player_rmFlag( PLAYER_LANDACK );
1812 player_message( _( "#%c%s>#0 \"Landing permission revoked.\"" ),
1813 spob_getColourChar( p ), spob_name( p ) );
1814}
1815
1822void player_nolandMsg( const char *str )
1823{
1824 free( player_message_noland );
1825
1826 /* Duplicate so that Lua memory which might be garbage-collected isn't relied
1827 * on. */
1828 if (str != NULL)
1829 player_message_noland = strdup( str );
1830 else
1832 strdup( _( "You are not allowed to land at this moment." ) );
1833}
1834
1840{
1841 int plt = ( player.p->target != PLAYER_ID );
1842 int lnd = ( player.p->nav_spob != -1 );
1843
1844 if (plt && ( player_canBoard( 0 ) != PLAYER_BOARD_IMPOSSIBLE )) {
1845 if (player_tryBoard( 1 ) == PLAYER_BOARD_RETRY)
1846 player_autonavBoard( player.p->target );
1847 return;
1848 } else if (lnd) {
1849 int canland = player_land( 1 );
1850 if (canland == PLAYER_LAND_AGAIN)
1851 player_autonavSpob( cur_system->spobs[player.p->nav_spob]->name, 1 );
1852 else if (canland == PLAYER_LAND_DENIED)
1853 player_autonavSpob( cur_system->spobs[player.p->nav_spob]->name, 0 );
1854 return;
1855 } else {
1856 /* In the case they have no target already, we just try to find a target
1857 * first, with priority for boarding. */
1858 if (!plt) {
1859 Pilot *nearp;
1860 double d = pilot_getNearestPosPilot(
1861 player.p, &nearp, player.p->solid.pos.x, player.p->solid.pos.y, 1 );
1862 if (( nearp != NULL ) && !pilot_isFlag( nearp, PILOT_NOBOARD ) &&
1863 ( d < pow2( 5e3 ) ) &&
1864 ( pilot_isDisabled( nearp ) ||
1865 pilot_isFlag( nearp, PILOT_BOARDABLE ) )) {
1866 player_targetSet( nearp->id );
1867 player_tryBoard( 0 ); /* Try to board if can. */
1868 return;
1869 }
1870 }
1871
1872 /* Now try to find a landing target. */
1873 if (!lnd) {
1874 double td = -1.; /* temporary distance */
1875 int tp = -1; /* temporary spob */
1876 for (int i = 0; i < array_size( cur_system->spobs ); i++) {
1877 const Spob *spob = cur_system->spobs[i];
1878 double d = vec2_dist( &player.p->solid.pos, &spob->pos );
1879 if (!pilot_inRangeSpob( player.p, i ))
1880 continue;
1881 if (!spob_hasService( spob, SPOB_SERVICE_LAND ))
1882 continue;
1883 if (( tp == -1 ) || ( ( td == -1 ) || ( td > d ) )) {
1884 tp = i;
1885 td = d;
1886 }
1887 }
1888 if (tp >= 0) {
1891 player_land( 0 ); /* Try to land if can. */
1892 return;
1893 }
1894 }
1895 }
1896}
1897
1905void player_targetHyperspaceSet( int id, int nomsg )
1906{
1907 int old;
1908
1909 /* Player must exist. */
1910 if (player.p == NULL)
1911 return;
1912
1913 if (id >= array_size( cur_system->jumps )) {
1914 WARN( _( "Trying to set player's hyperspace target to invalid ID '%d'" ),
1915 id );
1916 return;
1917 }
1918
1919 if (pilot_isFlag( player.p, PILOT_HYP_PREP ) ||
1920 pilot_isFlag( player.p, PILOT_HYP_BEGIN ) ||
1921 pilot_isFlag( player.p, PILOT_HYPERSPACE ))
1922 return;
1923
1924 old = player.p->nav_hyperspace;
1925 player.p->nav_hyperspace = id;
1926 player_hyperspacePreempt( ( id < 0 ) ? 0 : 1 );
1927 if (( old != id ) && ( id >= 0 ))
1929 gui_setNav();
1930
1931 if (!nomsg && ( old != id ) && ( player.autonav == AUTONAV_JUMP ))
1932 player_autonavAbort( NULL );
1933
1934 hooks_run( "target_hyperspace" );
1935}
1936
1941{
1942 int id;
1943
1944 /* Not under manual control. */
1945 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ))
1946 return;
1947
1948 map_clear(); /* clear the current map path */
1949
1950 for (id = player.p->nav_hyperspace + 1; id < array_size( cur_system->jumps );
1951 id++)
1952 if (jp_isKnown( &cur_system->jumps[id] ))
1953 break;
1954
1955 /* Try to find the lowest-indexed valid jump. */
1956 if (id >= array_size( cur_system->jumps )) {
1957 id = -1;
1958 for (int i = 0; i < array_size( cur_system->jumps ); i++)
1959 if (jp_isUsable( &cur_system->jumps[i] )) {
1960 id = i;
1961 break;
1962 }
1963 }
1964
1966
1967 /* Map gets special treatment if open. */
1968 if (id == -1)
1969 map_select( NULL, 0 );
1970 else
1971 map_select( cur_system->jumps[id].target, 0 );
1972}
1973
1980void player_hyperspacePreempt( int preempt )
1981{
1982 preemption = preempt;
1983}
1984
1991{
1992 return preemption;
1993}
1994
2001double player_dt_default( void )
2002{
2003 if (player.p != NULL && player.p->ship != NULL)
2004 return player.p->stats.time_mod * player.p->ship->dt_default;
2005 return 1.;
2006}
2007
2012{
2013 char buf[128];
2014
2016
2017 input_getKeybindDisplay( KST_AUTOHAIL, buf, sizeof( buf ) );
2018 player_message( _( "#rReceiving hail! Press #b%s#r to respond.#0" ), buf );
2019
2020 /* Reset speed. */
2021 player_autonavReset( 10. );
2022}
2023
2029int player_jump( void )
2030{
2031 int h;
2032
2033 /* Must have a jump target and not be already jumping. */
2034 if (pilot_isFlag( player.p, PILOT_HYPERSPACE ))
2035 return 0;
2036
2037 /* Not under manual control or disabled. */
2038 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) ||
2039 pilot_isDisabled( player.p ))
2040 return 0;
2041
2042 /* Select nearest jump if not target. */
2043 if (player.p->nav_hyperspace == -1) {
2044 int j = -1;
2045 double mindist = INFINITY;
2046 for (int i = 0; i < array_size( cur_system->jumps ); i++) {
2047 double dist =
2048 vec2_dist2( &player.p->solid.pos, &cur_system->jumps[i].pos );
2049 if (dist < mindist && jp_isUsable( &cur_system->jumps[i] )) {
2050 mindist = dist;
2051 j = i;
2052 }
2053 }
2054 if (j < 0)
2055 return 0;
2056
2057 player.p->nav_hyperspace = j;
2059 map_select( cur_system->jumps[player.p->nav_hyperspace].target, 0 );
2060 gui_setNav();
2061
2062 /* Only follow through if within range. */
2063 if (mindist > pow2( cur_system->jumps[j].radius ))
2064 return 0;
2065 }
2066
2067 /* Already jumping, so we break jump. */
2068 if (pilot_isFlag( player.p, PILOT_HYP_PREP )) {
2070 player_message( "#r%s", _( "Aborting hyperspace sequence." ) );
2071 return 0;
2072 }
2073
2074 /* Try to hyperspace. */
2075 h = space_hyperspace( player.p );
2076 if (h == -1) {
2079 // player_message( "#r%s", _("You are too far from a jump point to
2080 // initiate hyperspace."));
2081 } else if (h == -2)
2082 player_message( "#r%s", _( "Hyperspace drive is offline." ) );
2083 else if (h == -3)
2084 player_message( "#r%s",
2085 _( "You do not have enough fuel to hyperspace jump." ) );
2086 else {
2087 player_message( "#o%s", _( "Preparing for hyperspace." ) );
2088 /* Stop acceleration noise. */
2090
2091 /* Order escorts to jump; just for aesthetics (for now) */
2092 escorts_jump( player.p, &cur_system->jumps[player.p->nav_hyperspace] );
2093 return 1;
2094 }
2095
2096 return 0;
2097}
2098
2103{
2104 ntime_t t;
2105 StarSystem *sys;
2106 JumpPoint *jp;
2107 Pilot *const *pilot_stack;
2108
2109 /* First run jump hook. */
2110 hooks_run( "jumpout" );
2111
2112 /* Prevent targeted spob # from carrying over. */
2113 gui_setNav();
2114 gui_setTarget();
2116 player_targetAsteroidSet( -1, -1 );
2117
2118 /* calculates the time it takes, call before space_init */
2120 ntime_inc( t );
2121
2122 /* Save old system. */
2123 sys = cur_system;
2124
2125 /* Free old graphics. */
2126 space_gfxUnload( sys );
2127
2128 /* Enter the new system. */
2129 jp = &cur_system->jumps[player.p->nav_hyperspace];
2130 space_init( jp->target->name, 1 );
2131
2132 /* Set jumps as known. */
2133 if (!pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ))
2134 jp_setFlag( jp->returnJump, JP_KNOWN );
2135
2136 /* Set up the overlay. */
2137 ovr_initAlpha();
2138
2139 /* set position, the pilot_update will handle lowering vel */
2140 space_calcJumpInPos( cur_system, sys, &player.p->solid.pos,
2141 &player.p->solid.vel, &player.p->solid.dir, player.p );
2142 cam_setTargetPilot( player.p->id, 0 );
2143
2144 /* reduce fuel */
2145 player.p->fuel -= player.p->fuel_consumption;
2146
2147 /* Set the ptimer. */
2148 player.p->ptimer = HYPERSPACE_FADEIN;
2149
2150 /* Update the map, we have to remove the player flags first or it breaks
2151 * down. */
2152 pilot_rmFlag( player.p, PILOT_HYPERSPACE );
2153 pilot_rmFlag( player.p, PILOT_HYP_BEGIN );
2154 pilot_rmFlag( player.p, PILOT_HYP_BRAKE );
2155 pilot_rmFlag( player.p, PILOT_HYP_PREP );
2156 map_jump();
2157
2158 /* Add persisted pilots */
2160 for (int i = 0; i < array_size( pilot_stack ); i++) {
2161 Pilot *p = pilot_stack[i];
2162
2163 if (pilot_isFlag( p, PILOT_PERSIST ) || pilot_isFlag( p, PILOT_PLAYER )) {
2164 if (p != player.p)
2165 space_calcJumpInPos( cur_system, sys, &p->solid.pos, &p->solid.vel,
2166 &p->solid.dir, player.p );
2167
2168 /* Run Lua stuff for all persistant pilots. */
2171
2172 /* Invulnerable delay too. */
2173 p->itimer = PILOT_PLAYER_NONTARGETABLE_JUMPIN_DELAY;
2174 pilot_setFlag( p, PILOT_NONTARGETABLE );
2175
2176 /* Clear flags as necessary. */
2177 pilot_rmFlag( p, PILOT_HYPERSPACE );
2178 pilot_rmFlag( p, PILOT_HYP_BEGIN );
2179 pilot_rmFlag( p, PILOT_HYP_BRAKE );
2180 pilot_rmFlag( p, PILOT_HYP_PREP );
2181 }
2182 }
2183
2184 /* Disable autonavigation if arrived. */
2186
2187 /* Safe since this is run in the player hook section. */
2188 hooks_run( "jumpin" );
2189 hooks_run( "enter" );
2190 events_trigger( EVENT_TRIGGER_ENTER );
2191 missions_run( MIS_AVAIL_ENTER, -1, NULL, NULL );
2192
2193 /* Player sound. */
2195
2196 /* Increment times jumped. */
2197 player.jumped_times++;
2198 player.ps.jumped_times++;
2199}
2200
2206void player_accel( double acc )
2207{
2208 if (( player.p == NULL ) || pilot_isFlag( player.p, PILOT_HYP_PREP ) ||
2209 pilot_isFlag( player.p, PILOT_HYPERSPACE ))
2210 return;
2211
2212 player_acc = acc;
2213 if (toolkit_isOpen() || paused)
2215}
2216
2221{
2222 player_acc = 0.;
2223}
2224
2230void player_targetSet( unsigned int id )
2231{
2232 unsigned int old;
2233 old = player.p->target;
2234 pilot_setTarget( player.p, id );
2235 if (( old != id ) && ( player.p->target != PLAYER_ID )) {
2238 }
2239 gui_setTarget();
2240
2241 /* Clear the asteroid target. */
2242 player.p->nav_asteroid = -1;
2243 player.p->nav_anchor = -1;
2244
2245 /* The player should not continue following if the target pilot has been
2246 * changed. */
2247 if (( old != id ) && player_isFlag( PLAYER_AUTONAV ) &&
2248 ( player.autonav == AUTONAV_PILOT ))
2249 player_autonavAbort( NULL );
2250}
2251
2259{
2260 unsigned int tp;
2261 double d, td;
2262 Pilot *const *pilot_stack;
2263
2264 tp = PLAYER_ID;
2265 d = 0;
2267 for (int i = 0; i < array_size( pilot_stack ); i++) {
2268 /* Shouldn't be disabled. */
2269 if (pilot_isDisabled( pilot_stack[i] ))
2270 continue;
2271
2272 /* Must be a valid target. */
2273 if (!pilot_canTarget( pilot_stack[i] ))
2274 continue;
2275
2276 /* Must be hostile. */
2277 if (!pilot_isHostile( pilot_stack[i] ))
2278 continue;
2279
2280 if (pilot_inRangePilot( player.p, pilot_stack[i], &td ) != 1)
2281 continue;
2282
2283 if (tp == PLAYER_ID || ( ( td < d ) )) {
2284 d = td;
2285 tp = pilot_stack[i]->id;
2286 }
2287 }
2288
2289 player_targetSet( tp );
2290}
2291
2297void player_targetNext( int mode )
2298{
2299 player_targetSet( pilot_getNextID( player.p->target, mode ) );
2300}
2301
2307void player_targetPrev( int mode )
2308{
2309 player_targetSet( pilot_getPrevID( player.p->target, mode ) );
2310}
2311
2316{
2318 if (player.p->target != PLAYER_ID)
2319 player_targetSet( PLAYER_ID );
2320 else if (player.p->nav_asteroid >= 0)
2321 player_targetAsteroidSet( -1, -1 );
2322 else if (player.p->nav_spob >= 0)
2324 else if (( preemption == 1 || player.p->nav_spob == -1 ) &&
2325 !pilot_isFlag( player.p, PILOT_HYP_PREP )) {
2326 player.p->nav_hyperspace = -1;
2328 map_clear();
2329 }
2330 gui_setNav();
2331}
2332
2337{
2340 player_targetAsteroidSet( -1, -1 );
2341 player_targetSet( PLAYER_ID );
2342}
2343
2349void player_targetEscort( int prev )
2350{
2351 int i;
2352 /* Check if current target is an escort. */
2353 for (i = 0; i < array_size( player.p->escorts ); i++) {
2354 if (player.p->target == player.p->escorts[i].id) {
2355
2356 /* Cycle targets. */
2357 if (prev)
2358 pilot_setTarget( player.p, ( i > 0 ) ? player.p->escorts[i - 1].id
2359 : player.p->id );
2360 else
2362 ( i < array_size( player.p->escorts ) - 1 )
2363 ? player.p->escorts[i + 1].id
2364 : player.p->id );
2365
2366 break;
2367 }
2368 }
2369
2370 /* Not found in loop. */
2371 if (i >= array_size( player.p->escorts )) {
2372 /* Check to see if he actually has escorts. */
2373 if (array_size( player.p->escorts ) > 0) {
2374 /* Cycle forward or backwards. */
2375 if (prev)
2376 pilot_setTarget( player.p, array_back( player.p->escorts ).id );
2377 else
2378 pilot_setTarget( player.p, array_front( player.p->escorts ).id );
2379 } else
2380 pilot_setTarget( player.p, player.p->id );
2381 }
2382
2383 if (player.p->target != PLAYER_ID) {
2386 }
2387 gui_setTarget();
2388}
2389
2394{
2395 unsigned int t, dt;
2396 double d = pilot_getNearestPos( player.p, &dt, player.p->solid.pos.x,
2397 player.p->solid.pos.y, 1 );
2398 t = dt;
2399
2400 /* Disabled ships are typically only valid if within 500 px of the player. */
2401 if (( d > pow2( 500. ) ) && ( pilot_isDisabled( pilot_get( dt ) ) )) {
2403 /* Try to target a disabled ship if there are no active ships in range. */
2404 if (t == PLAYER_ID)
2405 t = dt;
2406 }
2407
2408 player_targetSet( t );
2409}
2410
2411static int screenshot_cur = 0;
2416{
2417 char filename[PATH_MAX];
2418
2419 if (PHYSFS_mkdir( "screenshots" ) == 0) {
2420 WARN( _( "Aborting screenshot" ) );
2421 return;
2422 }
2423
2424 /* Try to find current screenshots. */
2425 for (; screenshot_cur < 1000; screenshot_cur++) {
2426 snprintf( filename, sizeof( filename ), "screenshots/screenshot%03d.png",
2428 if (!PHYSFS_exists( filename ))
2429 break;
2430 }
2431
2432 if (screenshot_cur >= 999) { /* in case the crap system breaks :) */
2433 WARN( _( "You have reached the maximum amount of screenshots [999]" ) );
2434 return;
2435 }
2436
2437 /* now proceed to take the screenshot */
2438 DEBUG( _( "Taking screenshot [%03d]..." ), screenshot_cur );
2439 gl_screenshot( filename );
2440}
2441
2446static void player_checkHail( void )
2447{
2448 Pilot *const *pilot_stack = pilot_getAll();
2449 for (int i = 0; i < array_size( pilot_stack ); i++) {
2450 const Pilot *p = pilot_stack[i];
2451
2452 /* Must be hailing. */
2453 if (pilot_isFlag( p, PILOT_HAILING ))
2454 return;
2455 }
2456
2457 /* Clear hail timer. */
2459 player_hailTimer = 0.;
2460}
2461
2466static void player_spobOutOfRangeMsg( void )
2467{
2468 const Spob *spob = cur_system->spobs[player.p->nav_spob];
2469 const char *name = spob_name( spob );
2470 player_message( _( "#r%s is out of comm range, unable to contact." ), name );
2471}
2472
2476void player_hail( void )
2477{
2478 /* Not under manual control or disabled. */
2479 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) ||
2480 pilot_isDisabled( player.p ))
2481 return;
2482
2483 if (player.p->target != player.p->id)
2484 comm_openPilot( player.p->target );
2485 else if (player.p->nav_spob != -1) {
2486 Spob *spob = cur_system->spobs[player.p->nav_spob];
2487 if (spob_isFlag( spob, SPOB_UNINHABITED ))
2488 player_message( _( "#r%s does not respond." ), spob_name( spob ) );
2489 else if (pilot_inRangeSpob( player.p, player.p->nav_spob ))
2490 comm_openSpob( spob );
2491 else
2493 } else
2494 player_message( "#r%s", _( "No target selected to hail." ) );
2495
2496 /* Clear hails if none found. */
2498}
2499
2504{
2505 /* Not under manual control. */
2506 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ))
2507 return;
2508
2509 if (player.p->nav_spob != -1) {
2510 if (pilot_inRangeSpob( player.p, player.p->nav_spob ))
2511 comm_openSpob( cur_system->spobs[player.p->nav_spob] );
2512 else
2514 } else
2515 player_message( "#r%s", _( "No target selected to hail." ) );
2516}
2517
2522{
2523 Pilot *const *pilot_stack;
2524
2525 /* Not under manual control or disabled. */
2526 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) ||
2527 pilot_isDisabled( player.p ))
2528 return;
2529
2530 /* Find pilot to autohail. */
2532 for (int i = 0; i < array_size( pilot_stack ); i++) {
2533 const Pilot *p = pilot_stack[i];
2534
2535 /* Must be hailing. */
2536 if (pilot_isFlag( p, PILOT_HAILING )) {
2537 /* Try to hail. */
2538 pilot_setTarget( player.p, p->id );
2539 gui_setTarget();
2540 player_hail();
2541
2542 /* Clear hails if none found. */
2544 return;
2545 }
2546 }
2547
2548 player_message( "#r%s", _( "You haven't been hailed by any pilots." ) );
2549}
2550
2551void player_scan( void )
2552{
2553 const Pilot *t = pilot_getTarget( player.p );
2554 if (t == NULL) {
2555 player_message( "#r%s", _( "You need a target to scan." ) );
2556 return;
2557 }
2558 if (!pilot_isFlag( t, PILOT_PLAYER_SCANNED )) {
2559 player_message( "#o%s", _( "You are not ready to scan yet." ) );
2560 return;
2561 }
2562 if (pilot_inRangePilot( player.p, t, NULL ) <= 0) {
2564 "#o%s", _( "You can not identify the target at this distance." ) );
2565 return;
2566 }
2567
2568 /* Have to lead scirpt. */
2569 if (scan_env == LUA_NOREF) {
2570 scan_env = nlua_newEnv( "scanner" );
2573 size_t bufsize;
2574 char *buf = ndata_read( SCAN_PATH, &bufsize );
2575 if (buf == NULL) {
2576 WARN( _( "File '%s' not found!" ), SCAN_PATH );
2577 return;
2578 }
2579 if (nlua_dobufenv( scan_env, buf, bufsize, SCAN_PATH ) != 0) {
2580 WARN( _( "Error loading file: %s\n"
2581 "%s\n"
2582 "Most likely Lua file has improper syntax, please check" ),
2583 SCAN_PATH, lua_tostring( naevL, -1 ) );
2584 free( buf );
2585 return;
2586 }
2587 free( buf );
2588 }
2589
2590 /* Run Lua. */
2591 nlua_getenv( naevL, scan_env, "scan" );
2592 if (nlua_pcall( scan_env, 0, 0 )) { /* error has occurred */
2593 WARN( _( "Scan: '%s' : '%s'" ), "scan", lua_tostring( naevL, -1 ) );
2594 lua_pop( naevL, 1 );
2595 }
2596}
2597
2602{
2603 if (!conf.mouse_fly)
2604 return;
2605
2606 if (!player_isFlag( PLAYER_MFLY )) {
2608 player_message( "#o%s", _( "Mouse flying enabled." ) );
2609 player_setFlag( PLAYER_MFLY );
2610 } else {
2612 player_rmFlag( PLAYER_MFLY );
2613 player_message( "#o%s", _( "Mouse flying disabled." ) );
2614
2615 if (conf.mouse_accel)
2617 }
2618}
2619
2624{
2625 int stopped;
2626
2627 if (pilot_isFlag( player.p, PILOT_TAKEOFF ) ||
2628 pilot_isFlag( player.p, PILOT_LANDING ))
2629 return;
2630
2631 /* Not under manual control or disabled. */
2632 if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) ||
2633 pilot_isDisabled( player.p ))
2634 return;
2635
2636 /* Shouldn't be jumping. */
2637 if (pilot_isFlag( player.p, PILOT_HYPERSPACE ))
2638 return;
2639
2640 stopped = pilot_isStopped( player.p );
2641 if (stopped && !pilot_isFlag( player.p, PILOT_COOLDOWN ))
2642 pilot_cooldown( player.p, 1 );
2643 else {
2644 pilot_setFlag( player.p, PILOT_BRAKING );
2645 pilot_setFlag( player.p, PILOT_COOLDOWN_BRAKE );
2646 }
2647}
2648
2655static int player_thinkMouseFly( double dt )
2656{
2657 double px, py, r, x, y;
2658
2659 px = player.p->solid.pos.x;
2660 py = player.p->solid.pos.y;
2661 gl_screenToGameCoords( &x, &y, player.mousex, player.mousey );
2662 r = sqrt( pow2( x - px ) + pow2( y - py ) );
2663 if (r > 50.) { /* Ignore mouse input within a 50 px radius of the centre. */
2664 pilot_face( player.p, atan2( y - py, x - px ), dt );
2665 if (conf.mouse_accel) { /* Only alter thrust if option is enabled. */
2666 double acc = CLAMP( 0., 1., ( r - 100. ) / 200. );
2667 acc = 3. * pow2( acc ) - 2. * pow( acc, 3. );
2668 /* Only accelerate when within 180 degrees of the intended direction.
2669 */
2670 if (ABS( angle_diff( atan2( y - py, x - px ), player.p->solid.dir ) ) <
2671 M_PI_2)
2672 player_accel( acc );
2673 else
2674 player_accel( 0. );
2675 }
2676 return 1;
2677 } else
2678 return 0;
2679}
2680
2684void player_dead( void )
2685{
2686 /* Explode at normal speed. */
2688 pause_setSpeed( 1. );
2689 sound_setSpeed( 1. );
2690
2691 /* Close the overlay. */
2692 ovr_setOpen( 0 );
2693}
2694
2699{
2700 if (player_isFlag( PLAYER_DESTROYED ))
2701 return;
2702
2703 /* Mark as destroyed. */
2704 player_setFlag( PLAYER_DESTROYED );
2705
2706 /* Set timer for death menu. */
2707 player_timer = 5.;
2708
2709 /* Stop sounds. */
2711
2712 /* Stop autonav */
2714
2715 /* Reset time compression when player dies. */
2717 pause_setSpeed( 1. );
2718 sound_setSpeed( 1. );
2719}
2720
2724static int player_shipsCompare( const void *arg1, const void *arg2 )
2725{
2726 const PlayerShip_t *ps1 = arg1;
2727 const PlayerShip_t *ps2 = arg2;
2728 int ret;
2729
2730 if (ps1->deployed && !ps2->deployed)
2731 return -1;
2732 else if (ps2->deployed && !ps1->deployed)
2733 return +1;
2734
2735 if (ps1->favourite && !ps2->favourite)
2736 return -1;
2737 else if (ps2->favourite && !ps1->favourite)
2738 return +1;
2739
2740 if (ps1->p->ship->points < ps2->p->ship->points)
2741 return +1;
2742 else if (ps1->p->ship->points > ps2->p->ship->points)
2743 return -1;
2744
2745#if 0
2746 credits_t p1, p2;
2747 /* Get prices. */
2748 p1 = pilot_worth( ps1->p, 0 );
2749 p2 = pilot_worth( ps2->p, 0 );
2750
2751 /* Compare price INVERSELY */
2752 if (p1 < p2)
2753 return +1;
2754 else if (p1 > p2)
2755 return -1;
2756#endif
2757 ret = strcmp( ship_classDisplay( ps1->p->ship ),
2758 ship_classDisplay( ps2->p->ship ) );
2759 if (ret != 0)
2760 return ret;
2761
2762 /* In case of tie sort by name so they don't flip or something. */
2763 return strcmp( ps1->p->name, ps2->p->name );
2764}
2765
2770{
2771 if (array_size( player_stack ) == 0)
2772 return;
2773
2774 /* Sort. */
2775 qsort( player_stack, array_size( player_stack ), sizeof( PlayerShip_t ),
2777}
2778
2787int player_ships( char **sships, glTexture **tships )
2788{
2789 /* Sort. */
2791
2792 /* Create the struct. */
2793 for (int i = 0; i < array_size( player_stack ); i++) {
2794 sships[i] = strdup( player_stack[i].p->name );
2795 tships[i] = ship_gfxStore( player_stack[i].p->ship, 256, 0., 0., 0. );
2796 }
2797
2798 return array_size( player_stack );
2799}
2800
2805{
2806 return player_stack;
2807}
2808
2814int player_nships( void )
2815{
2816 return array_size( player_stack );
2817}
2818
2825int player_hasShip( const char *shipname )
2826{
2827 /* Check current ship. */
2828 if (( player.p != NULL ) && ( strcmp( player.p->name, shipname ) == 0 ))
2829 return 1;
2830
2831 /* Check stocked ships. */
2832 for (int i = 0; i < array_size( player_stack ); i++)
2833 if (strcmp( player_stack[i].p->name, shipname ) == 0)
2834 return 1;
2835 return 0;
2836}
2837
2844Pilot *player_getShip( const char *shipname )
2845{
2846 if (( player.p != NULL ) && ( strcmp( shipname, player.p->name ) == 0 ))
2847 return player.p;
2848
2849 for (int i = 0; i < array_size( player_stack ); i++)
2850 if (strcmp( player_stack[i].p->name, shipname ) == 0)
2851 return player_stack[i].p;
2852
2853 WARN( _( "Player ship '%s' not found in stack" ), shipname );
2854 return NULL;
2855}
2856
2863PlayerShip_t *player_getPlayerShip( const char *shipname )
2864{
2865 if (( player.p != NULL ) && ( strcmp( shipname, player.p->name ) == 0 ))
2866 return NULL;
2867
2868 for (int i = 0; i < array_size( player_stack ); i++)
2869 if (strcmp( player_stack[i].p->name, shipname ) == 0)
2870 return &player_stack[i];
2871
2872 WARN( _( "Player ship '%s' not found in stack" ), shipname );
2873 return NULL;
2874}
2875
2883{
2884 /* Special case map. */
2885 if (( outfit_isMap( o ) && map_isUseless( o ) ) ||
2886 ( outfit_isLocalMap( o ) && localmap_isUseless( o ) ))
2887 return 1;
2888
2889 /* Special case license. */
2891 return 1;
2892
2893 /* Special case GUI. */
2894 if (outfit_isGUI( o ) && player_guiCheck( o->u.gui.gui ))
2895 return 1;
2896
2897 /* Special case intrinsics. */
2898 if (o->slot.type == OUTFIT_SLOT_INTRINSIC)
2899 return pilot_hasIntrinsic( player.p, o );
2900
2901 /* Try to find it. */
2902 for (int i = 0; i < array_size( player_outfits ); i++)
2903 if (player_outfits[i].o == o)
2904 return player_outfits[i].q;
2905
2906 return 0;
2907}
2908
2913{
2914 int q = player_outfitOwned( o );
2915 q += pilot_numOutfit( player.p, o );
2916 for (int i = 0; i < array_size( player_stack ); i++)
2917 q += pilot_numOutfit( player_stack[i].p, o );
2918
2919 return q;
2920}
2921
2925static int player_outfitCompare( const void *arg1, const void *arg2 )
2926{
2927 const PlayerOutfit_t *po1, *po2;
2928
2929 /* Get type. */
2930 po1 = (const PlayerOutfit_t *)arg1;
2931 po2 = (const PlayerOutfit_t *)arg2;
2932
2933 /* Compare. */
2934 return outfit_compareTech( &po1->o, &po2->o );
2935}
2936
2941{
2942 return player_outfits;
2943}
2944
2953int player_getOutfitsFiltered( const Outfit ***outfits,
2954 int ( *filter )( const Outfit *o ),
2955 const char *name )
2956{
2957 if (array_size( player_outfits ) == 0)
2958 return 0;
2959
2960 /* We'll sort. */
2963
2964 for (int i = 0; i < array_size( player_outfits ); i++)
2965 array_push_back( outfits, (Outfit *)player_outfits[i].o );
2966
2967 return outfits_filter( *outfits, array_size( player_outfits ), filter,
2968 name );
2969}
2970
2977{
2978 return array_size( player_outfits );
2979}
2980
2988int player_addOutfit( const Outfit *o, int quantity )
2989{
2990 PlayerOutfit_t *po;
2991
2992 /* Validity check. */
2993 if (quantity == 0)
2994 return 0;
2995
2996 /* Don't readd uniques. */
2997 if (outfit_isProp( o, OUTFIT_PROP_UNIQUE ) &&
2998 ( player_outfitOwned( o ) > 0 ))
2999 return 0;
3000
3001 /* special case if it's a map */
3002 if (outfit_isMap( o )) {
3003 map_map( o );
3004 return 1; /* Success. */
3005 } else if (outfit_isLocalMap( o )) {
3006 localmap_map( o );
3007 return 1;
3008 }
3009 /* special case if it's an outfit */
3010 else if (outfit_isGUI( o )) {
3011 player_guiAdd( o->u.gui.gui );
3012 return 1; /* Success. */
3013 }
3014 /* special case if it's a license. */
3015 else if (outfit_isLicense( o )) {
3017 return 1; /* Success. */
3018 }
3019 /* intrinsic outfits get added as intinsics. */
3020 else if (o->slot.type == OUTFIT_SLOT_INTRINSIC) {
3021 int ret;
3022 if (pilot_hasOutfitLimit( player.p, o->limit ))
3023 return 0;
3024 ret = pilot_addOutfitIntrinsic( player.p, o );
3026 return ret;
3027 }
3028
3029 /* Try to find it. */
3030 for (int i = 0; i < array_size( player_outfits ); i++) {
3031 if (player_outfits[i].o == o) {
3032 player_outfits[i].q += quantity;
3033 return quantity;
3034 }
3035 }
3036
3037 /* Allocate if needed. */
3038 po = &array_grow( &player_outfits );
3039
3040 /* Add the outfit. */
3041 po->o = o;
3042 po->q = quantity;
3043 return quantity;
3044}
3045
3053int player_rmOutfit( const Outfit *o, int quantity )
3054{
3055 if (o->slot.type == OUTFIT_SLOT_INTRINSIC)
3056 return pilot_rmOutfitIntrinsic( player.p, o );
3057
3058 /* Try to find it. */
3059 for (int i = 0; i < array_size( player_outfits ); i++) {
3060 if (player_outfits[i].o != o)
3061 continue;
3062 /* See how many to remove. */
3063 int q = MIN( player_outfits[i].q, quantity );
3064 player_outfits[i].q -= q;
3065
3066 /* See if must remove element. */
3067 if (player_outfits[i].q <= 0)
3069 &player_outfits[i + 1] );
3070
3071 /* Return removed outfits. */
3072 return q;
3073 }
3074
3075 /* Nothing removed. */
3076 return 0;
3077}
3078
3079/*
3080 * Trivial sorting function for arrays of integers.
3081 */
3082static int cmp_int( const void *p1, const void *p2 )
3083{
3084 const int *i1 = (const int *)p1;
3085 const int *i2 = (const int *)p2;
3086 return ( *i1 ) - ( *i2 );
3087}
3088
3095{
3096 HookParam h[2];
3097 const MissionData *m;
3098
3099 /* Make sure not already marked. */
3100 if (player_missionAlreadyDone( id ))
3101 return;
3102
3103 /* Mark as done. */
3104 if (missions_done == NULL)
3105 missions_done = array_create( int );
3107
3108 qsort( missions_done, array_size( missions_done ), sizeof( int ), cmp_int );
3109
3110 /* Run the completion hook. */
3111 m = mission_get( id );
3112 mission_toLuaTable( naevL, m ); /* Push to stack. */
3113 h[0].type = HOOK_PARAM_REF;
3114 h[0].u.ref = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* Pops from stack. */
3115 h[1].type = HOOK_PARAM_SENTINEL;
3116 hooks_runParam( "mission_done", h );
3117}
3118
3126{
3127 if (missions_done == NULL)
3128 return 0;
3129
3130 const int *i = bsearch( &id, missions_done, array_size( missions_done ),
3131 sizeof( int ), cmp_int );
3132 return i != NULL;
3133}
3134
3142{
3143 return missions_done;
3144}
3145
3152{
3153 HookParam h[2];
3154
3155 /* Make sure not already done. */
3156 if (player_eventAlreadyDone( id ))
3157 return;
3158
3159 /* Mark as done. */
3160 if (events_done == NULL)
3161 events_done = array_create( int );
3163
3164 qsort( events_done, array_size( events_done ), sizeof( int ), cmp_int );
3165
3166 /* Run the completion hook. */
3167 event_toLuaTable( naevL, id ); /* Push to stack. */
3168 h[0].type = HOOK_PARAM_REF;
3169 h[0].u.ref = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* Pops from stack. */
3170 h[1].type = HOOK_PARAM_SENTINEL;
3171 hooks_runParam( "event_done", h );
3172}
3173
3181{
3182 if (events_done == NULL)
3183 return 0;
3184
3185 const int *i = bsearch( &id, events_done, array_size( events_done ),
3186 sizeof( int ), cmp_int );
3187 return i != NULL;
3188}
3189
3197{
3198 return events_done;
3199}
3200
3207int player_hasLicense( const char *license )
3208{
3209 if (license == NULL)
3210 return 1;
3211 if (player_licenses == NULL)
3212 return 0;
3213
3214 const char *s =
3215 bsearch( &license, player_licenses, array_size( player_licenses ),
3216 sizeof( char * ), strsort );
3217 return s != NULL;
3218}
3219
3225void player_addLicense( const char *license )
3226{
3227 if (player_hasLicense( license ))
3228 return;
3229 if (player_licenses == NULL)
3230 player_licenses = array_create( char * );
3231 array_push_back( &player_licenses, strdup( license ) );
3232
3233 qsort( player_licenses, array_size( player_licenses ), sizeof( char * ),
3234 strsort );
3235}
3236
3241{
3242 return (const char **)player_licenses;
3243}
3244
3249{
3250 if (player_isFlag( PLAYER_HOOK_HYPER )) {
3251 player_rmFlag( PLAYER_HOOK_HYPER );
3253 }
3254 if (player_isFlag( PLAYER_HOOK_JUMPIN )) {
3255 player_rmFlag( PLAYER_HOOK_JUMPIN );
3257 hooks_run( "jumpin" );
3258 hooks_run( "enter" );
3259 events_trigger( EVENT_TRIGGER_ENTER );
3260 missions_run( MIS_AVAIL_ENTER, -1, NULL, NULL );
3261 }
3262 if (player_isFlag( PLAYER_HOOK_LAND )) {
3263 player_rmFlag( PLAYER_HOOK_LAND );
3264 if (player.p->nav_spob >= 0)
3265 land( cur_system->spobs[player.p->nav_spob], 0 );
3266 }
3267}
3268
3272static void player_clearEscorts( void )
3273{
3274 for (int i = 0; i < array_size( player.p->outfits ); i++) {
3275 if (player.p->outfits[i]->outfit == NULL)
3276 continue;
3277
3278 if (outfit_isFighterBay( player.p->outfits[i]->outfit ))
3279 player.p->outfits[i]->u.ammo.deployed = 0;
3280 }
3281}
3282
3289{
3290 /* Clear escorts first. */
3292
3293 /* Go over escorts. */
3294 for (int i = 0; i < array_size( player.p->escorts ); i++) {
3295 int q;
3296 PilotOutfitSlot *po;
3297 const Escort_t *e = &player.p->escorts[i];
3298 Pilot *pe = pilot_get( e->id );
3299
3300 /* Non-persistent pilots should have been wiped already. */
3301 if (pe == NULL) {
3302 escort_rmListIndex( player.p, i );
3303 i--;
3304 continue;
3305 }
3306
3307 /* Update to random position. */
3308 pe->solid.dir = RNGF() * 2. * M_PI;
3309 vec2_cset( &pe->solid.pos,
3310 player.p->solid.pos.x + 50. * cos( pe->solid.dir ),
3311 player.p->solid.pos.y + 50. * sin( pe->solid.dir ) );
3312 vec2_cset( &pe->solid.vel, 0., 0. );
3313
3314 /* Update outfit if needed. */
3315 if (e->type != ESCORT_TYPE_BAY)
3316 continue;
3317
3318 po = pilot_getDockSlot( pe );
3319 if (po == NULL) {
3320 /* We just want to delete the pilot and not trigger other stuff. */
3321 pilot_setFlag( pe, PILOT_DELETE );
3322 WARN( _( "Escort is undeployed, removing." ) );
3323 escort_rmListIndex( player.p, i );
3324 i--;
3325 continue;
3326 }
3327
3328 po->u.ammo.deployed++;
3329 q = po->u.ammo.deployed + po->u.ammo.quantity;
3330 if (q > pilot_maxAmmoO( player.p, po->outfit )) {
3331 /* We just want to delete the pilot and not trigger other stuff. */
3332 pilot_setFlag( pe, PILOT_DELETE );
3333 WARN( _( "Escort is deployed past outfit limits, removing." ) );
3334 escort_rmListIndex( player.p, i );
3335 i--;
3336 continue;
3337 }
3338 }
3339
3340 /* Add the player fleets. */
3341 for (int i = 0; i < array_size( player_stack ); i++) {
3342 PlayerShip_t *ps = &player_stack[i];
3343
3344 /* Already exists. */
3345 if (ps->p->id)
3346 continue;
3347
3348 /* Only deploy escorts that are deployed. */
3349 if (!ps->deployed)
3350 continue;
3351
3352 /* Only deploy spaceworthy escorts. */
3353 if (!pilot_isSpaceworthy( ps->p ))
3354 continue;
3355
3356 pfleet_deploy( ps );
3357 }
3358
3359 return 0;
3360}
3361
3365static int player_saveEscorts( xmlTextWriterPtr writer )
3366{
3367 for (int i = 0; i < array_size( player.p->escorts ); i++) {
3368 Escort_t *e = &player.p->escorts[i];
3369 Pilot *pe;
3370 if (!e->persist)
3371 continue;
3372 switch (e->type) {
3373 case ESCORT_TYPE_BAY:
3374 xmlw_startElem( writer, "escort" );
3375 xmlw_attr( writer, "type", "bay" );
3376 xmlw_attr( writer, "name", "%s", e->ship->name );
3377 xmlw_endElem( writer ); /* "escort" */
3378 break;
3379
3380 case ESCORT_TYPE_FLEET:
3381 pe = pilot_get( e->id );
3382 if (pe != NULL) {
3383 xmlw_startElem( writer, "escort" );
3384 xmlw_attr( writer, "type", "fleet" );
3385 xmlw_attr( writer, "name", "%s", pe->name );
3386 xmlw_endElem( writer ); /* "escort" */
3387 }
3388 break;
3389
3390 default:
3391 break;
3392 }
3393 }
3394
3395 return 0;
3396}
3397
3404int player_save( xmlTextWriterPtr writer )
3405{
3406 const char **guis;
3407 int cycles, periods, seconds;
3408 double rem;
3409 const PlayerItem *inventory;
3410
3411 xmlw_startElem( writer, "player" );
3412
3413 /* Standard player details. */
3414 xmlw_attr( writer, "name", "%s", player.name );
3415 xmlw_elem( writer, "credits", "%" CREDITS_PRI, player.p->credits );
3416 xmlw_elem( writer, "chapter", "%s", player.chapter );
3417 if (player.difficulty != NULL)
3418 xmlw_elem( writer, "difficulty", "%s", player.difficulty );
3419 if (player.gui != NULL)
3420 xmlw_elem( writer, "gui", "%s", player.gui );
3421 xmlw_elem( writer, "mapOverlay", "%d", ovr_isOpen() );
3422 gui_radarGetRes( &player.radar_res );
3423 xmlw_elem( writer, "radar_res", "%f", player.radar_res );
3424 xmlw_elem( writer, "eq_outfitMode", "%d", player.eq_outfitMode );
3425 xmlw_elem( writer, "map_minimal", "%d", player.map_minimal );
3426 xmlw_elem( writer, "fleet_capacity", "%d", player.fleet_capacity );
3427
3428 /* Time. */
3429 xmlw_startElem( writer, "time" );
3430 ntime_getR( &cycles, &periods, &seconds, &rem );
3431 xmlw_elem( writer, "SCU", "%d", cycles );
3432 xmlw_elem( writer, "STP", "%d", periods );
3433 xmlw_elem( writer, "STU", "%d", seconds );
3434 xmlw_elem( writer, "Remainder", "%lf", rem );
3435 xmlw_endElem( writer ); /* "time" */
3436
3437 /* Current ship. */
3438 xmlw_elem( writer, "location", "%s", land_spob->name );
3439 player_saveShip( writer, &player.ps ); /* current ship */
3440
3441 /* Ships. */
3442 xmlw_startElem( writer, "ships" );
3443 for (int i = 0; i < array_size( player_stack ); i++)
3444 player_saveShip( writer, &player_stack[i] );
3445 xmlw_endElem( writer ); /* "ships" */
3446
3447 /* GUIs. */
3448 xmlw_startElem( writer, "guis" );
3449 guis = player_guiList();
3450 for (int i = 0; i < array_size( guis ); i++)
3451 xmlw_elem( writer, "gui", "%s", guis[i] );
3452 xmlw_endElem( writer ); /* "guis" */
3453
3454 /* Outfits. */
3455 xmlw_startElem( writer, "outfits" );
3456 for (int i = 0; i < array_size( player_outfits ); i++) {
3457 xmlw_startElem( writer, "outfit" );
3458 xmlw_attr( writer, "quantity", "%d", player_outfits[i].q );
3459 xmlw_str( writer, "%s", player_outfits[i].o->name );
3460 xmlw_endElem( writer ); /* "outfit" */
3461 }
3462 xmlw_endElem( writer ); /* "outfits" */
3463
3464 /* Licenses. */
3465 xmlw_startElem( writer, "licenses" );
3466 for (int i = 0; i < array_size( player_licenses ); i++)
3467 xmlw_elem( writer, "license", "%s", player_licenses[i] );
3468 xmlw_endElem( writer ); /* "licenses" */
3469
3470 /* Inventory. */
3471 xmlw_startElem( writer, "inventory" );
3473 for (int i = 0; i < array_size( inventory ); i++) {
3474 const PlayerItem *pi = &inventory[i];
3475 xmlw_startElem( writer, "item" );
3476 xmlw_attr( writer, "quantity", "%d", pi->quantity );
3477 xmlw_str( writer, "%s", pi->name );
3478 xmlw_endElem( writer ); /* "item" */
3479 }
3480 xmlw_endElem( writer ); /* "inventory" */
3481
3482 xmlw_endElem( writer ); /* "player" */
3483
3484 /* Mission the player has done. */
3485 xmlw_startElem( writer, "missions_done" );
3486 for (int i = 0; i < array_size( missions_done ); i++) {
3487 const MissionData *m = mission_get( missions_done[i] );
3488 if (m != NULL) /* In case mission name changes between versions */
3489 xmlw_elem( writer, "done", "%s", m->name );
3490 }
3491 xmlw_endElem( writer ); /* "missions_done" */
3492
3493 /* Events the player has done. */
3494 xmlw_startElem( writer, "events_done" );
3495 for (int i = 0; i < array_size( events_done ); i++) {
3496 const char *ev = event_dataName( events_done[i] );
3497 if (ev != NULL) /* In case mission name changes between versions */
3498 xmlw_elem( writer, "done", "%s", ev );
3499 }
3500 xmlw_endElem( writer ); /* "events_done" */
3501
3502 /* Escorts. */
3503 xmlw_startElem( writer, "escorts" );
3504 player_saveEscorts( writer );
3505 xmlw_endElem( writer ); /* "escorts" */
3506
3507 /* Metadata. */
3508 xmlw_startElem( writer, "metadata" );
3509 player_saveMetadata( writer );
3510 xmlw_endElem( writer ); /* "metadata" */
3511
3512 return 0;
3513}
3514
3518static int player_saveShipSlot( xmlTextWriterPtr writer,
3519 const PilotOutfitSlot *slot, int i )
3520{
3521 const Outfit *o = slot->outfit;
3522 xmlw_startElem( writer, "outfit" );
3523 xmlw_attr( writer, "slot", "%d", i );
3524 if (outfit_isLauncher( o ) || outfit_isFighterBay( o ))
3525 xmlw_attr( writer, "quantity", "%d", slot->u.ammo.quantity );
3526 xmlw_str( writer, "%s", o->name );
3527 xmlw_endElem( writer ); /* "outfit" */
3528
3529 return 0;
3530}
3531
3539static int player_saveShip( xmlTextWriterPtr writer, PlayerShip_t *pship )
3540{
3541 Pilot *ship = pship->p;
3542 xmlw_startElem( writer, "ship" );
3543 xmlw_attr( writer, "name", "%s", ship->name );
3544 xmlw_attr( writer, "model", "%s", ship->ship->name );
3545 xmlw_attr( writer, "favourite", "%d", pship->favourite );
3546 xmlw_attr( writer, "deployed", "%d", pship->deployed );
3547
3548 /* Metadata. */
3549 if (pship->acquired)
3550 xmlw_elem( writer, "acquired", "%s", pship->acquired );
3551 xmlw_saveNTime( writer, "acquired_date", pship->acquired_date );
3552 xmlw_elem( writer, "time_played", "%f", pship->time_played );
3553 xmlw_elem( writer, "dmg_done_shield", "%f", pship->dmg_done_shield );
3554 xmlw_elem( writer, "dmg_done_armour", "%f", pship->dmg_done_armour );
3555 xmlw_elem( writer, "dmg_taken_shield", "%f", pship->dmg_taken_shield );
3556 xmlw_elem( writer, "dmg_taken_armour", "%f", pship->dmg_taken_armour );
3557 xmlw_elem( writer, "jumped_times", "%u", pship->jumped_times );
3558 xmlw_elem( writer, "landed_times", "%u", pship->landed_times );
3559 xmlw_elem( writer, "death_counter", "%u", pship->death_counter );
3560
3561 /* Ships destroyed by class. */
3562 xmlw_startElem( writer, "ships_destroyed" );
3563 for (int i = SHIP_CLASS_NULL + 1; i < SHIP_CLASS_TOTAL; i++) {
3564 char buf[STRMAX_SHORT];
3565 strncpy( buf, ship_classToString( i ), sizeof( buf ) - 1 );
3566 for (size_t j = 0; j < strlen( buf ); j++)
3567 if (buf[j] == ' ')
3568 buf[j] = '_';
3569 xmlw_elem( writer, buf, "%u", pship->ships_destroyed[i] );
3570 }
3571 xmlw_endElem( writer ); /* "ships_destroyed" */
3572
3573 /* save the fuel */
3574 xmlw_elem( writer, "fuel", "%f", ship->fuel );
3575
3576 /* save the outfits */
3577 xmlw_startElem( writer, "outfits_intrinsic" ); /* Want them to be first. */
3578 for (int i = 0; i < array_size( ship->outfit_intrinsic ); i++)
3579 player_saveShipSlot( writer, &ship->outfit_intrinsic[i], i );
3580 xmlw_endElem( writer ); /* "outfits_intrinsic" */
3581 xmlw_startElem( writer, "outfits_structure" );
3582 for (int i = 0; i < array_size( ship->outfit_structure ); i++) {
3583 if (ship->outfit_structure[i].outfit == NULL)
3584 continue;
3585 player_saveShipSlot( writer, &ship->outfit_structure[i], i );
3586 }
3587 xmlw_endElem( writer ); /* "outfits_structure" */
3588 xmlw_startElem( writer, "outfits_utility" );
3589 for (int i = 0; i < array_size( ship->outfit_utility ); i++) {
3590 if (ship->outfit_utility[i].outfit == NULL)
3591 continue;
3592 player_saveShipSlot( writer, &ship->outfit_utility[i], i );
3593 }
3594 xmlw_endElem( writer ); /* "outfits_utility" */
3595 xmlw_startElem( writer, "outfits_weapon" );
3596 for (int i = 0; i < array_size( ship->outfit_weapon ); i++) {
3597 if (ship->outfit_weapon[i].outfit == NULL)
3598 continue;
3599 player_saveShipSlot( writer, &ship->outfit_weapon[i], i );
3600 }
3601 xmlw_endElem( writer ); /* "outfits_weapon" */
3602
3603 /* save the commodities */
3604 xmlw_startElem( writer, "commodities" );
3605 for (int i = 0; i < array_size( ship->commodities ); i++) {
3606 PilotCommodity *pc = &ship->commodities[i];
3607 /* Remove cargo with id and no mission. */
3608 if (pc->id > 0) {
3609 int found = 0;
3610 for (int j = 0; j < array_size( player_missions ); j++) {
3611 /* Only check active missions. */
3612 if (player_missions[j]->id > 0) {
3613 /* Now check if it's in the cargo list. */
3614 for (int k = 0; k < array_size( player_missions[j]->cargo );
3615 k++) {
3616 /* See if it matches a cargo. */
3617 if (player_missions[j]->cargo[k] == pc->id) {
3618 found = 1;
3619 break;
3620 }
3621 }
3622 }
3623 if (found)
3624 break;
3625 }
3626
3627 if (!found) {
3628 WARN( _( "Found mission cargo '%s' without associated mission." ),
3629 pc->commodity->name );
3630 WARN( _( "Please reload save game to remove the dead cargo." ) );
3631 continue;
3632 }
3633 } else if (pc->quantity == 0) {
3634 WARN( _( "Found cargo '%s' with 0 quantity." ), pc->commodity->name );
3635 WARN( _( "Please reload save game to remove the dead cargo." ) );
3636 continue;
3637 }
3638
3639 xmlw_startElem( writer, "commodity" );
3640
3641 xmlw_attr( writer, "quantity", "%d", pc->quantity );
3642 if (pc->id > 0)
3643 xmlw_attr( writer, "id", "%d", pc->id );
3644 xmlw_str( writer, "%s", pc->commodity->name );
3645
3646 xmlw_endElem( writer ); /* commodity */
3647 }
3648 xmlw_endElem( writer ); /* "commodities" */
3649
3650 xmlw_startElem( writer, "weaponsets" );
3651 xmlw_attr( writer, "autoweap", "%d", ship->autoweap );
3652 xmlw_attr( writer, "advweap", "%d", ship->advweap );
3653 xmlw_attr( writer, "aim_lines", "%d", ship->aimLines );
3654 for (int i = 0; i < PILOT_WEAPON_SETS; i++) {
3655 PilotWeaponSet *ws = &pship->weapon_sets[i];
3656 PilotWeaponSetOutfit *weaps = ws->slots;
3657 xmlw_startElem( writer, "weaponset" );
3658 /* Inrange isn't handled by autoweap for the player. */
3659 xmlw_attr( writer, "inrange", "%d", ws->inrange );
3660 xmlw_attr( writer, "manual", "%d", ws->manual );
3661 xmlw_attr( writer, "volley", "%d", ws->volley );
3662 xmlw_attr( writer, "id", "%d", i );
3663 if (!ship->autoweap) {
3664 xmlw_attr( writer, "type", "%d", ws->type );
3665 for (int j = 0; j < array_size( weaps ); j++) {
3666 xmlw_startElem( writer, "weapon" );
3667 xmlw_str( writer, "%d", weaps[j].slotid );
3668 xmlw_endElem( writer ); /* "weapon" */
3669 }
3670 }
3671 xmlw_endElem( writer ); /* "weaponset" */
3672 }
3673 xmlw_endElem( writer ); /* "weaponsets" */
3674
3675 /* Ship variables. */
3676 xmlw_startElem( writer, "vars" );
3677 lvar_save( pship->p->shipvar, writer );
3678 xmlw_endElem( writer ); /* "vars" */
3679
3680 xmlw_endElem( writer ); /* "ship" */
3681
3682 return 0;
3683}
3684
3691static int player_saveMetadata( xmlTextWriterPtr writer )
3692{
3693 time_t t = time( NULL );
3694 double diff = difftime( t, player.time_since_save );
3695
3696 /* Compute elapsed time. */
3697 player.time_played += diff;
3698 player.ps.time_played += diff;
3699 player.time_since_save = t;
3700
3701 /* Save the stuff. */
3702 xmlw_saveTime( writer, "last_played", time( NULL ) );
3703 xmlw_saveTime( writer, "date_created", player.date_created );
3704
3705 /* Meta-data. */
3706 xmlw_elem( writer, "time_played", "%f", player.time_played );
3707 xmlw_elem( writer, "dmg_done_shield", "%f", player.dmg_done_shield );
3708 xmlw_elem( writer, "dmg_done_armour", "%f", player.dmg_done_armour );
3709 xmlw_elem( writer, "dmg_taken_shield", "%f", player.dmg_taken_shield );
3710 xmlw_elem( writer, "dmg_taken_armour", "%f", player.dmg_taken_armour );
3711 xmlw_elem( writer, "jumped_times", "%u", player.jumped_times );
3712 xmlw_elem( writer, "landed_times", "%u", player.landed_times );
3713 xmlw_elem( writer, "death_counter", "%u", player.death_counter );
3714
3715 /* Ships destroyed by class. */
3716 xmlw_startElem( writer, "ships_destroyed" );
3717 for (int i = SHIP_CLASS_NULL + 1; i < SHIP_CLASS_TOTAL; i++) {
3718 char buf[STRMAX_SHORT];
3719 strncpy( buf, ship_classToString( i ), sizeof( buf ) - 1 );
3720 for (size_t j = 0; j < strlen( buf ); j++)
3721 if (buf[j] == ' ')
3722 buf[j] = '_';
3723 xmlw_elem( writer, buf, "%u", player.ships_destroyed[i] );
3724 }
3725 xmlw_endElem( writer ); /* "ships_destroyed" */
3726
3727 return 0;
3728}
3729
3736Spob *player_load( xmlNodePtr parent )
3737{
3738 xmlNodePtr node;
3739 Spob *pnt;
3740
3741 /* some cleaning up */
3742 memset( &player, 0, sizeof( Player_t ) );
3743 player.speed = conf.game_speed;
3744 pnt = NULL;
3745 map_cleanup();
3746
3747 /* Sane time defaults. */
3748 player.last_played = time( NULL );
3749 player.date_created = player.last_played;
3750 player.time_since_save = player.last_played;
3751
3752 if (player_stack == NULL)
3754 if (player_outfits == NULL)
3756
3757 node = parent->xmlChildrenNode;
3758 do {
3759 if (xml_isNode( node, "metadata" ))
3760 player_parseMetadata( node );
3761 else if (xml_isNode( node, "player" ))
3762 pnt = player_parse( node );
3763 else if (xml_isNode( node, "missions_done" ))
3765 else if (xml_isNode( node, "events_done" ))
3766 player_parseDoneEvents( node );
3767 else if (xml_isNode( node, "escorts" ))
3768 player_parseEscorts( node );
3769 } while (xml_nextNode( node ));
3770
3771 /* Set up meta-data. */
3772 player.time_since_save = time( NULL );
3773
3774 /* Defaults as necessary. */
3775 if (player.chapter == NULL)
3776 player.chapter = strdup( start_chapter() );
3777 if (player.difficulty != NULL)
3778 difficulty_setLocal( difficulty_get( player.difficulty ) );
3779 else
3780 difficulty_setLocal( NULL ); /* Sets the default. */
3781
3782 /* Updates the fleet internals. */
3783 pfleet_update();
3784
3785 return pnt;
3786}
3787
3799static int player_runUpdaterScript( const char *type, const char *name, int q )
3800{
3801 static nlua_env player_updater_env = LUA_NOREF;
3802
3804
3805 /* Load env if necessary. */
3806 if (player_updater_env == LUA_NOREF) {
3807 player_updater_env = nlua_newEnv( "updater" );
3808 size_t bufsize;
3809 char *buf = ndata_read( SAVE_UPDATER_PATH, &bufsize );
3810 if (nlua_dobufenv( player_updater_env, buf, bufsize,
3811 SAVE_UPDATER_PATH ) != 0) {
3812 WARN( _( "Error loading file: %s\n"
3813 "%s\n"
3814 "Most likely Lua file has improper syntax, please check" ),
3815 SAVE_UPDATER_PATH, lua_tostring( naevL, -1 ) );
3816 free( buf );
3817 return 0;
3818 }
3819 free( buf );
3820 }
3821
3822 /* Try to find out equivalent. */
3823 nlua_getenv( naevL, player_updater_env, type );
3824 lua_pushstring( naevL, name );
3825 if (nlua_pcall( player_updater_env, 1, 1 )) { /* error has occurred */
3826 WARN( _( "Board: '%s'" ), lua_tostring( naevL, -1 ) );
3827 lua_pop( naevL, 1 );
3828 return 0;
3829 }
3830 if (lua_type( naevL, -1 ) == LUA_TNUMBER) {
3831 player_payback += q * round( lua_tonumber( naevL, -1 ) );
3832 lua_pop( naevL, 1 );
3833 return 0;
3834 }
3835
3836 return 1;
3837}
3838
3842static const Outfit *player_tryGetOutfit( const char *name, int q )
3843{
3844 const Outfit *o = outfit_getW( name );
3845
3846 /* Outfit was found normally. */
3847 if (o != NULL)
3848 return o;
3850
3851 /* Try to find out equivalent. */
3852 if (player_runUpdaterScript( "outfit", name, q ) == 0)
3853 return NULL;
3854 else if (lua_type( naevL, -1 ) == LUA_TSTRING)
3855 o = outfit_get( lua_tostring( naevL, -1 ) );
3856 else if (lua_isoutfit( naevL, -1 ))
3857 o = lua_tooutfit( naevL, -1 );
3858 else
3859 WARN( _( "Outfit '%s' in player save not found!" ), name );
3860
3861 lua_pop( naevL, 1 );
3862
3863 return o;
3864}
3865
3869static const Ship *player_tryGetShip( const char *name )
3870{
3871 const Ship *s = ship_getW( name );
3872
3873 /* Ship was found normally. */
3874 if (s != NULL)
3875 return s;
3877
3878 /* Try to find out equivalent. */
3879 if (player_runUpdaterScript( "ship", name, 1 ) == 0)
3880 return NULL;
3881 else if (lua_type( naevL, -1 ) == LUA_TSTRING)
3882 s = ship_get( lua_tostring( naevL, -1 ) );
3883 else if (lua_isship( naevL, -1 ))
3884 s = lua_toship( naevL, -1 );
3885 else
3886 WARN( _( "Ship '%s' in player save not found!" ), name );
3887
3888 lua_pop( naevL, 1 );
3889
3890 return s;
3891}
3892
3896static void player_tryAddLicense( const char *name )
3897{
3898 /* Found normally. */
3899 if (outfit_licenseExists( name )) {
3900 player_addLicense( name );
3901 return;
3902 }
3904
3905 /* Try to find out equivalent. */
3906 if (player_runUpdaterScript( "license", name, 1 ) == 0)
3907 return;
3908 else if (lua_type( naevL, -1 ) == LUA_TSTRING)
3909 player_addLicense( lua_tostring( naevL, -1 ) );
3910 else
3911 WARN( _( "Saved license does not exist and could not be found or "
3912 "updated: '%s'!" ),
3913 name );
3914 lua_pop( naevL, 1 );
3915}
3916
3923static Spob *player_parse( xmlNodePtr parent )
3924{
3925 const char *spob = NULL;
3926 Spob *pnt = NULL;
3927 xmlNodePtr node, cur;
3928 int map_overlay_enabled = 0;
3929 StarSystem *sys;
3930 double a, r;
3931 int time_set = 0;
3932
3933 xmlr_attr_strd( parent, "name", player.name );
3934 assert( player.p == NULL );
3936
3937 player.radar_res = RADAR_RES_DEFAULT;
3938
3939 /* Must get spob first. */
3940 node = parent->xmlChildrenNode;
3941 do {
3942 xmlr_str( node, "location", spob );
3943 } while (xml_nextNode( node ));
3944
3945 /* Parse rest. */
3946 node = parent->xmlChildrenNode;
3947 do {
3948 /* global stuff */
3949 xmlr_ulong( node, "credits", player_creds );
3950 xmlr_strd( node, "gui", player.gui );
3951 xmlr_strd( node, "chapter", player.chapter );
3952 xmlr_int( node, "mapOverlay", map_overlay_enabled );
3953 xmlr_float( node, "radar_res", player.radar_res );
3954 xmlr_int( node, "eq_outfitMode", player.eq_outfitMode );
3955 xmlr_int( node, "map_minimal", player.map_minimal );
3956 xmlr_int( node, "fleet_capacity", player.fleet_capacity );
3957
3958 /* Time. */
3959 if (xml_isNode( node, "time" )) {
3960 double rem = -1.;
3961 int cycles = -1, periods = -1, seconds = -1;
3962 cur = node->xmlChildrenNode;
3963 do {
3964 xmlr_int( cur, "SCU", cycles );
3965 xmlr_int( cur, "STP", periods );
3966 xmlr_int( cur, "STU", seconds );
3967 xmlr_float( cur, "Remainder", rem );
3968 } while (xml_nextNode( cur ));
3969 if (( cycles < 0 ) || ( periods < 0 ) || ( seconds < 0 ) ||
3970 ( rem < 0. ))
3971 WARN( _( "Malformed time in save game!" ) );
3972 ntime_setR( cycles, periods, seconds, rem );
3973 if (( cycles >= 0 ) || ( periods >= 0 ) || ( seconds >= 0 ))
3974 time_set = 1;
3975 }
3976
3977 if (xml_isNode( node, "ship" ))
3978 player_parseShip( node, 1 );
3979
3980 /* Parse ships. */
3981 else if (xml_isNode( node, "ships" )) {
3982 cur = node->xmlChildrenNode;
3983 do {
3984 if (xml_isNode( cur, "ship" ))
3985 player_parseShip( cur, 0 );
3986 } while (xml_nextNode( cur ));
3987 }
3988
3989 /* Parse GUIs. */
3990 else if (xml_isNode( node, "guis" )) {
3991 cur = node->xmlChildrenNode;
3992 do {
3993 if (xml_isNode( cur, "gui" ))
3994 player_guiAdd( xml_get( cur ) );
3995 } while (xml_nextNode( cur ));
3996 }
3997
3998 /* Parse outfits. */
3999 else if (xml_isNode( node, "outfits" )) {
4000 cur = node->xmlChildrenNode;
4001 do {
4002 if (xml_isNode( cur, "outfit" )) {
4003 int q;
4004 const Outfit *o;
4005 const char *oname = xml_get( cur );
4006 xmlr_attr_float( cur, "quantity", q );
4007 if (q == 0) {
4008 WARN( _( "Outfit '%s' was saved without quantity!" ),
4009 ( oname != NULL ) ? oname : "NULL" );
4010 continue;
4011 }
4012
4013 o = player_tryGetOutfit( oname, q );
4014 if (o == NULL)
4015 continue;
4016
4017 player_addOutfit( o, q );
4018 }
4019 } while (xml_nextNode( cur ));
4020 }
4021
4022 /* Parse licenses. */
4023 else if (xml_isNode( node, "licenses" ))
4024 player_parseLicenses( node );
4025
4026 else if (xml_isNode( node, "inventory" ))
4027 player_parseInventory( node );
4028
4029 } while (xml_nextNode( node ));
4030
4031 /* Handle cases where ship is missing. */
4032 if (player.p == NULL) {
4033 PilotFlags flags;
4034 pilot_clearFlagsRaw( flags );
4035 pilot_setFlagRaw( flags, PILOT_PLAYER );
4036 pilot_setFlagRaw( flags, PILOT_NO_OUTFITS );
4037 WARN( _( "Player ship does not exist! Giving starting ship." ) );
4038 player_newShip( ship_get( start_ship() ), "MIA", 0,
4039 _( "You acquired this ship through save corruption." ),
4040 1 );
4041 }
4042
4043 /* Check. */
4044 if (player.p == NULL) {
4045 const char *err =
4046 _( "Something went horribly wrong, player does not exist after "
4047 "load..." );
4048 WARN( err );
4049 dialogue_alertRaw( err );
4050 return NULL;
4051 }
4052
4053 /* Threaded loading of graphics for speed. */
4054 int needsgfx = 0;
4055 for (int i = 0; i < array_size( player_stack ); i++) {
4056 Ship *s = (Ship *)player_stack[i].p->ship;
4057 if (!ship_gfxLoaded( s )) {
4058 s->flags |= SHIP_NEEDSGFX;
4059 needsgfx = 1;
4060 }
4061 }
4062 if (needsgfx)
4064
4065 /* Reset player speed */
4066 player.speed = conf.game_speed;
4067
4068 /* set global thingies */
4069 player.p->credits = player_creds + player_payback;
4070 if (!time_set) {
4071 WARN(
4072 _( "Save has no time information, setting to start information." ) );
4073 ntime_set( start_date() );
4074 }
4075
4076 /* Updater message. */
4077 if (player_ran_updater) {
4078 DEBUG( _( "Player save was updated!" ) );
4079 dialogue_msg( _( "Save Game Updated" ),
4080 _( "The loaded save games has had outfits and ships "
4081 "updated to the current Naev version. You will find "
4082 "that some outfits and ships you have had have been "
4083 "changed. In the case no equivalent outfit or ship was "
4084 "found, you have been refunded the cost in credits." ) );
4085 }
4086
4087 /* set player in system */
4088 pnt = spob_get( spob );
4089 /* Get random spob if it's NULL. */
4090 if (( pnt == NULL ) || ( spob_getSystemName( spob ) == NULL ) ||
4091 !spob_hasService( pnt, SPOB_SERVICE_LAND )) {
4092 WARN( _( "Player starts out in non-existent or invalid spob '%s',"
4093 "trying to find a suitable one instead." ),
4094 spob );
4095
4096 /* Find a landable, inhabited spob that's in a system, offers refueling
4097 * and meets the following additional criteria:
4098 *
4099 * 0: Shipyard, outfitter, non-hostile
4100 * 1: Outfitter, non-hostile
4101 * 2: None
4102 *
4103 * If no spob meeting the current criteria can be found, the next
4104 * set of criteria is tried until none remain.
4105 */
4106 const char *found = NULL;
4107 for (int i = 0; i < 3; i++) {
4108 unsigned int services =
4109 SPOB_SERVICE_LAND | SPOB_SERVICE_INHABITED | SPOB_SERVICE_REFUEL;
4110
4111 if (i == 0)
4112 services |= SPOB_SERVICE_SHIPYARD;
4113
4114 if (i != 2)
4115 services |= SPOB_SERVICE_OUTFITS;
4116
4117 found = space_getRndSpob(
4118 1, services, ( i != 2 ) ? player_filterSuitableSpob : NULL );
4119 if (found != NULL)
4120 break;
4121
4122 WARN( _( "Could not find a spob satisfying criteria %d." ), i );
4123 }
4124
4125 if (found == NULL) {
4126 WARN( _( "Could not find a suitable spob. Choosing a random spob." ) );
4127 found =
4128 space_getRndSpob( 0, 0, NULL ); /* This should never, ever fail. */
4129 }
4130 pnt = spob_get( found );
4131 }
4132
4133 /* Initialize system. */
4134 sys = system_get( spob_getSystemName( pnt->name ) );
4135 space_gfxLoad( sys );
4136 a = RNGF() * 2. * M_PI;
4137 r = RNGF() * pnt->radius * 0.8;
4138 player_warp( pnt->pos.x + r * cos( a ), pnt->pos.y + r * sin( a ) );
4139 player.p->solid.dir = 2. * M_PI * RNGF();
4140
4141 /* Initialize outfits. */
4143
4144 /* initialize the system */
4145 space_init( sys->name, 0 );
4146 map_clear(); /* sets the map up */
4147 ovr_setOpen( map_overlay_enabled );
4148
4149 /* initialize the sound */
4151
4152 return pnt;
4153}
4154
4162{
4163 return !areEnemies( p->presence.faction, FACTION_PLAYER );
4164}
4165
4172static int player_parseDoneMissions( xmlNodePtr parent )
4173{
4174 xmlNodePtr node = parent->xmlChildrenNode;
4175 do {
4176 xml_onlyNodes( node );
4177
4178 if (!xml_isNode( node, "done" ))
4179 continue;
4180
4181 int id = mission_getID( xml_get( node ) );
4182 if (id < 0)
4183 DEBUG( _( "Mission '%s' doesn't seem to exist anymore, removing from "
4184 "save." ),
4185 xml_get( node ) );
4186 else
4188 } while (xml_nextNode( node ));
4189 return 0;
4190}
4191
4198static int player_parseDoneEvents( xmlNodePtr parent )
4199{
4200 xmlNodePtr node = parent->xmlChildrenNode;
4201 do {
4202 xml_onlyNodes( node );
4203
4204 if (!xml_isNode( node, "done" ))
4205 continue;
4206
4207 int id = event_dataID( xml_get( node ) );
4208 if (id < 0)
4209 DEBUG( _( "Event '%s' doesn't seem to exist anymore, removing from "
4210 "save." ),
4211 xml_get( node ) );
4212 else
4214 } while (xml_nextNode( node ));
4215 return 0;
4216}
4217
4224static int player_parseLicenses( xmlNodePtr parent )
4225{
4226 xmlNodePtr node = parent->xmlChildrenNode;
4227 do {
4228 xml_onlyNodes( node );
4229
4230 if (!xml_isNode( node, "license" ))
4231 continue;
4232
4233 const char *name = xml_get( node );
4234 if (name == NULL) {
4235 WARN( _( "License node is missing name." ) );
4236 continue;
4237 }
4238 player_tryAddLicense( name );
4239 } while (xml_nextNode( node ));
4240 return 0;
4241}
4242
4249static int player_parseInventory( xmlNodePtr parent )
4250{
4251 xmlNodePtr node = parent->xmlChildrenNode;
4252 do {
4253 int q;
4254 xml_onlyNodes( node );
4255
4256 if (!xml_isNode( node, "item" ))
4257 continue;
4258
4259 xmlr_attr_int_def( node, "quantity", q, 1 );
4260 const char *name = xml_get( node );
4261 if (name == NULL) {
4262 WARN( _( "Inventory item node is missing name." ) );
4263 continue;
4264 }
4265 player_inventoryAdd( name, q );
4266 } while (xml_nextNode( node ));
4267 return 0;
4268}
4269
4276static int player_parseEscorts( xmlNodePtr parent )
4277{
4278 xmlNodePtr node = parent->xmlChildrenNode;
4279 do {
4280 char *buf, *name;
4281
4282 /* Skip non-escorts. */
4283 if (!xml_isNode( node, "escort" ))
4284 continue;
4285
4286 xmlr_attr_strd( node, "type", buf );
4287 xmlr_attr_strd( node, "name", name );
4288 if (name == NULL) /* Workaround for < 0.10.0 old saves, TODO remove
4289 around 0.12.0 or 0.13.0. */
4290 name = xml_getStrd( node );
4291 if (strcmp( buf, "bay" ) == 0)
4292 escort_addList( player.p, ship_get( name ), ESCORT_TYPE_BAY, 0, 1 );
4293
4294 else if (strcmp( buf, "fleet" ) == 0) {
4295 PlayerShip_t *ps = player_getPlayerShip( name );
4296
4297 /* Only deploy escorts that are deployed. */
4298 if (!ps->deployed)
4299 WARN( _( "Fleet ship '%s' is deployed despite not being marked for "
4300 "deployal!" ),
4301 ps->p->name );
4302
4303 /* Only deploy spaceworthy escorts. */
4304 else if (!pilot_isSpaceworthy( ps->p ))
4305 /* Player deployed without a spaceworthy one. We'll just print some
4306 * debug info instead of a warning here. */
4307 DEBUG( _( "Fleet ship '%s' is deployed despite not being space "
4308 "worthy!" ),
4309 ps->p->name );
4310 else
4311 pfleet_deploy( ps ); /* Can deploy. */
4312 } else
4313 WARN( _( "Escort has invalid type '%s'." ), buf );
4314 free( buf );
4315 free( name );
4316 } while (xml_nextNode( node ));
4317 return 0;
4318}
4319
4326static int player_parseMetadata( xmlNodePtr parent )
4327{
4328 xmlNodePtr node = parent->xmlChildrenNode;
4329 do {
4330 xml_onlyNodes( node );
4331
4332 xmlr_float( node, "dmg_done_shield", player.dmg_done_shield );
4333 xmlr_float( node, "dmg_done_armour", player.dmg_done_armour );
4334 xmlr_float( node, "dmg_taken_shield", player.dmg_taken_shield );
4335 xmlr_float( node, "dmg_taken_armour", player.dmg_taken_armour );
4336 xmlr_uint( node, "jumped_times", player.jumped_times );
4337 xmlr_uint( node, "landed_times", player.landed_times );
4338 xmlr_uint( node, "death_counter", player.death_counter );
4339 xmlr_float( node, "time_played", player.time_played );
4340
4341 if (xml_isNode( node, "last_played" )) {
4342 xml_parseTime( node, &player.last_played );
4343 continue;
4344 } else if (xml_isNode( node, "date_created" )) {
4345 xml_parseTime( node, &player.date_created );
4346 continue;
4347 } else if (xml_isNode( node, "ships_destroyed" )) {
4348 xmlNodePtr cur = node->xmlChildrenNode;
4349 do {
4350 char buf[STRMAX_SHORT];
4351 int class;
4352
4353 xml_onlyNodes( cur );
4354
4355 strncpy( buf, (const char *)cur->name, sizeof( buf ) - 1 );
4356 for (size_t i = 0; i < strlen( buf ); i++)
4357 if (buf[i] == '_')
4358 buf[i] = ' ';
4359
4360 class = ship_classFromString( buf );
4361 if (class == SHIP_CLASS_NULL) {
4362 WARN( _( "Unknown ship class '%s' when parsing "
4363 "'ships_destroyed' node!" ),
4364 (const char *)cur->name );
4365 continue;
4366 }
4367
4368 player.ships_destroyed[class] = xml_getULong( cur );
4369 } while (xml_nextNode( cur ));
4370 }
4371 } while (xml_nextNode( node ));
4372 return 0;
4373}
4374
4378static void player_addOutfitToPilot( Pilot *pilot, const Outfit *outfit,
4379 PilotOutfitSlot *s )
4380{
4381 int ret;
4382
4383 if (!outfit_fitsSlot( outfit, &s->sslot->slot )) {
4384 DEBUG( _( "Outfit '%s' does not fit designated slot on player's pilot "
4385 "'%s', adding to stock." ),
4386 outfit->name, pilot->name );
4387 player_addOutfit( outfit, 1 );
4388 return;
4389 }
4390
4391 ret = pilot_addOutfitRaw( pilot, outfit, s );
4392 if (ret != 0) {
4393 DEBUG( _( "Outfit '%s' does not fit on player's pilot '%s', adding to "
4394 "stock." ),
4395 outfit->name, pilot->name );
4396 player_addOutfit( outfit, 1 );
4397 return;
4398 }
4399
4400 /* Update stats. */
4401 pilot_calcStats( pilot );
4402}
4403
4407static void player_parseShipSlot( xmlNodePtr node, Pilot *ship,
4408 PilotOutfitSlot *slot )
4409{
4410 const Outfit *o;
4411 int q;
4412 const char *name = xml_get( node );
4413 if (name == NULL) {
4414 WARN( _( "Empty ship slot node found, skipping." ) );
4415 return;
4416 }
4417
4418 /* Add the outfit. */
4419 o = player_tryGetOutfit( name, 1 );
4420 if (o == NULL)
4421 return;
4422 player_addOutfitToPilot( ship, o, slot );
4423
4424 /* Doesn't have ammo. */
4425 if (!outfit_isLauncher( o ) && !outfit_isFighterBay( o ))
4426 return;
4427
4428 /* See if has quantity. */
4429 xmlr_attr_int( node, "quantity", q );
4430 if (q > 0)
4431 pilot_addAmmo( ship, slot, q );
4432}
4433
4441static int player_parseShip( xmlNodePtr parent, int is_player )
4442{
4443 char *name, *model;
4444 int id, autoweap, fuel, aim_lines;
4445 const Ship *ship_parsed;
4446 Pilot *ship;
4447 xmlNodePtr node;
4448 Commodity *com;
4449 PilotFlags flags;
4450 PlayerShip_t ps;
4451
4452 memset( &ps, 0, sizeof( PlayerShip_t ) );
4453
4454 /* Parse attributes. */
4455 xmlr_attr_strd( parent, "name", name );
4456 xmlr_attr_strd( parent, "model", model );
4457 xmlr_attr_int_def( parent, "favourite", ps.favourite, 0 );
4458 xmlr_attr_int_def( parent, "deployed", ps.deployed, 0 );
4459
4460 /* Safe defaults. */
4461 pilot_clearFlagsRaw( flags );
4462 if (is_player)
4463 pilot_setFlagRaw( flags, PILOT_PLAYER );
4464 pilot_setFlagRaw( flags, PILOT_NO_OUTFITS );
4465
4466 /* Handle certain 0.10.0-alpha saves where it's possible that... */
4467 if (!is_player && ( player.p != NULL ) &&
4468 strcmp( name, player.p->name ) == 0) {
4469 DEBUG( _( "Ignoring player-owned ship '%s': duplicate of player's "
4470 "current ship." ),
4471 name );
4472 free( name );
4473 free( model );
4474 return 0;
4475 }
4476
4477 /* Get the ship. */
4478 ship_parsed = player_tryGetShip( model );
4479 if (ship_parsed == NULL) {
4480 WARN( _( "Player ship '%s' not found!" ), model );
4481
4482 /* TODO we should probably parse the outfits and give them to the player.
4483 */
4484
4485 /* Clean up. */
4486 free( name );
4487 free( model );
4488
4489 return -1;
4490 }
4491
4492 /* Create the ship. */
4493 ship =
4494 pilot_createEmpty( ship_parsed, name, faction_get( "Player" ), flags );
4495 /* Player is currently on this ship */
4496 if (is_player)
4497 ps.deployed = 0; /* Current ship can't be deployed. */
4498 ps.p = ship;
4499
4500 /* Ship should not have default outfits. */
4501 for (int i = 0; i < array_size( ship->outfits ); i++)
4502 pilot_rmOutfitRaw( ship, ship->outfits[i] );
4503
4504 /* Clean up. */
4505 free( name );
4506 free( model );
4507
4508 /* Defaults. */
4509 fuel = -1;
4510 autoweap = 1;
4511 aim_lines = 0;
4512
4513 /* Start parsing. */
4514 node = parent->xmlChildrenNode;
4515 do {
4516 xml_onlyNodes( node );
4517
4518 /* Meta-data. */
4519 xmlr_strd( node, "acquired", ps.acquired );
4520 if (xml_isNode( node, "acquired_date" )) {
4521 xml_parseNTime( node, &ps.acquired_date );
4522 continue;
4523 }
4524 xmlr_float( node, "time_played", ps.time_played );
4525 xmlr_float( node, "dmg_done_shield", ps.dmg_done_shield );
4526 xmlr_float( node, "dmg_done_armour", ps.dmg_done_armour );
4527 xmlr_float( node, "dmg_taken_shield", ps.dmg_taken_shield );
4528 xmlr_float( node, "dmg_taken_armour", ps.dmg_taken_armour );
4529 xmlr_uint( node, "jumped_times", ps.jumped_times );
4530 xmlr_uint( node, "landed_times", ps.landed_times );
4531 xmlr_uint( node, "death_counter", ps.death_counter );
4532 if (xml_isNode( node, "ships_destroyed" )) {
4533 xmlNodePtr cur = node->xmlChildrenNode;
4534 do {
4535 char buf[STRMAX_SHORT];
4536 int class;
4537
4538 xml_onlyNodes( cur );
4539
4540 strncpy( buf, (const char *)cur->name, sizeof( buf ) - 1 );
4541 for (size_t i = 0; i < strlen( buf ); i++)
4542 if (buf[i] == '_')
4543 buf[i] = ' ';
4544
4545 class = ship_classFromString( buf );
4546 if (class == SHIP_CLASS_NULL) {
4547 WARN( _( "Unknown ship class '%s' when parsing "
4548 "'ships_destroyed' node!" ),
4549 (const char *)cur->name );
4550 continue;
4551 }
4552
4553 ps.ships_destroyed[class] = xml_getULong( cur );
4554 } while (xml_nextNode( cur ));
4555 }
4556
4557 /* Get fuel. */
4558 xmlr_int( node, "fuel", fuel );
4559
4560 /* New outfit loading. */
4561 if (xml_isNode( node, "outfits_structure" )) {
4562 xmlNodePtr cur = node->xmlChildrenNode;
4563 do { /* load each outfit */
4564 int n;
4565 xml_onlyNodes( cur );
4566 if (!xml_isNode( cur, "outfit" )) {
4567 WARN( _( "Save has unknown '%s' tag!" ), xml_get( cur ) );
4568 continue;
4569 }
4570 xmlr_attr_int_def( cur, "slot", n, -1 );
4571 if (( n < 0 ) || ( n >= array_size( ship->outfit_structure ) )) {
4572 WARN( _( "Outfit slot out of range, not adding to ship." ) );
4573 continue;
4574 }
4575 player_parseShipSlot( cur, ship, &ship->outfit_structure[n] );
4576 } while (xml_nextNode( cur ));
4577 continue;
4578 } else if (xml_isNode( node, "outfits_utility" )) {
4579 xmlNodePtr cur = node->xmlChildrenNode;
4580 do { /* load each outfit */
4581 int n;
4582 xml_onlyNodes( cur );
4583 if (!xml_isNode( cur, "outfit" )) {
4584 WARN( _( "Save has unknown '%s' tag!" ), xml_get( cur ) );
4585 continue;
4586 }
4587 xmlr_attr_int_def( cur, "slot", n, -1 );
4588 if (( n < 0 ) || ( n >= array_size( ship->outfit_utility ) )) {
4589 WARN( _( "Outfit slot out of range, not adding." ) );
4590 continue;
4591 }
4592 player_parseShipSlot( cur, ship, &ship->outfit_utility[n] );
4593 } while (xml_nextNode( cur ));
4594 continue;
4595 } else if (xml_isNode( node, "outfits_weapon" )) {
4596 xmlNodePtr cur = node->xmlChildrenNode;
4597 do { /* load each outfit */
4598 int n;
4599 xml_onlyNodes( cur );
4600 if (!xml_isNode( cur, "outfit" )) {
4601 WARN( _( "Save has unknown '%s' tag!" ), xml_get( cur ) );
4602 continue;
4603 }
4604 xmlr_attr_int_def( cur, "slot", n, -1 );
4605 if (( n < 0 ) || ( n >= array_size( ship->outfit_weapon ) )) {
4606 WARN( _( "Outfit slot out of range, not adding." ) );
4607 continue;
4608 }
4609 player_parseShipSlot( cur, ship, &ship->outfit_weapon[n] );
4610 } while (xml_nextNode( cur ));
4611 continue;
4612 } else if (xml_isNode( node, "outfits_intrinsic" )) {
4613 xmlNodePtr cur = node->xmlChildrenNode;
4614 do { /* load each outfit */
4615 xml_onlyNodes( cur );
4616 if (!xml_isNode( cur, "outfit" )) {
4617 WARN( _( "Save has unknown '%s' tag!" ), xml_get( cur ) );
4618 continue;
4619 }
4620 const Outfit *o = player_tryGetOutfit( xml_get( cur ), 1 );
4621 if (o != NULL) {
4622 if (pilot_hasOutfitLimit( ship, o->limit ))
4623 WARN( _( "Player ship '%s' has intrinsic outfit '%s' "
4624 "exceeding limits! Removing." ),
4625 ship->name, o->name );
4626 else
4627 pilot_addOutfitIntrinsic( ship, o );
4628 }
4629 } while (xml_nextNode( cur ));
4630 continue;
4631 } else if (xml_isNode( node, "commodities" )) {
4632 xmlNodePtr cur = node->xmlChildrenNode;
4633 do {
4634 if (xml_isNode( cur, "commodity" )) {
4635 int cid, quantity;
4636
4637 xmlr_attr_int( cur, "quantity", quantity );
4638 xmlr_attr_int_def( cur, "id", cid, 0 );
4639
4640 /* Get the commodity. */
4641 com = commodity_get( xml_get( cur ) );
4642 if (com == NULL) {
4643 WARN( _( "Unknown commodity '%s' detected, removing." ),
4644 xml_get( cur ) );
4645 continue;
4646 }
4647
4648 /* actually add the cargo with id hack
4649 * Note that the player's cargo_free is ignored here. */
4650 if (( quantity == 0 ) && ( cid == 0 ))
4651 WARN( _( "Adding cargo '%s' to ship '%s' that is not a "
4652 "mission cargo with quantity=0!" ),
4653 com->name, ship->name );
4654 pilot_cargoAddRaw( ship, com, quantity, cid );
4655 }
4656 } while (xml_nextNode( cur ));
4657 continue;
4658 }
4659 // WARN(_("Save has unknown '%s' tag!"),xml_get(node));
4660 } while (xml_nextNode( node ));
4661
4662 /* Update stats. */
4663 pilot_calcStats( ship );
4664
4665 /* Test for validity. */
4666 if (fuel >= 0)
4667 ship->fuel = MIN( ship->fuel_max, fuel );
4668 /* ships can now be non-spaceworthy on save
4669 * str = pilot_isSpaceworthy( ship ); */
4670 if (!pilot_slotsCheckSafety( ship )) {
4671 DEBUG( _( "Player ship '%s' failed slot validity check , removing all "
4672 "outfits and adding to stock." ),
4673 ship->name );
4674 /* Remove all outfits. */
4675 for (int i = 0; i < array_size( ship->outfits ); i++) {
4676 const Outfit *o = ship->outfits[i]->outfit;
4677 int ret = pilot_rmOutfitRaw( ship, ship->outfits[i] );
4678 if (ret == 0)
4679 player_addOutfit( o, 1 );
4680 }
4681 pilot_calcStats( ship );
4682 }
4683
4684 /* Sets inrange by default if weapon sets are missing. */
4685 for (int i = 0; i < PILOT_WEAPON_SETS; i++)
4686 pilot_weapSetInrange( ship, i, WEAPSET_INRANGE_PLAYER_DEF );
4687
4688 /* Second pass for weapon sets. */
4689 node = parent->xmlChildrenNode;
4690 do {
4691 xmlNodePtr cur;
4692
4693 if (xml_isNode( node, "vars" )) {
4694 ps.p->shipvar = lvar_load( node );
4695 continue;
4696 } else if (!xml_isNode( node, "weaponsets" ))
4697 continue;
4698
4699 /* Check for autoweap. */
4700 xmlr_attr_int( node, "autoweap", autoweap );
4701 xmlr_attr_int( node, "advweap", ship->advweap );
4702
4703 /* Check for aim_lines. */
4704 xmlr_attr_int( node, "aim_lines", aim_lines );
4705
4706 /* Parse weapon sets. */
4707 cur = node->xmlChildrenNode;
4708 do { /* Load each weapon set. */
4709 int in_range, manual, weap_type, volley;
4710 xmlNodePtr ccur;
4711
4712 xml_onlyNodes( cur );
4713 if (!xml_isNode( cur, "weaponset" )) {
4714 WARN( _( "Player ship '%s' has unknown node '%s' in 'weaponsets' "
4715 "(expected 'weaponset')." ),
4716 ship->name, cur->name );
4717 continue;
4718 }
4719
4720 /* Get id. */
4721 xmlr_attr_int_def( cur, "id", id, -1 );
4722 if (id == -1) {
4723 WARN( _( "Player ship '%s' missing 'id' tag for weapon set." ),
4724 ship->name );
4725 continue;
4726 }
4727 if (( id < 0 ) || ( id >= PILOT_WEAPON_SETS )) {
4728 WARN(
4729 _( "Player ship '%s' has invalid weapon set id '%d' [max %d]." ),
4730 ship->name, id, PILOT_WEAPON_SETS - 1 );
4731 continue;
4732 }
4733
4734 /* Clean up weapon set. */
4735 pilot_weapSetClear( ship, id );
4736
4737 /* Set inrange mode. */
4738 xmlr_attr_int( cur, "inrange", in_range );
4739 if (in_range > 0)
4740 pilot_weapSetInrange( ship, id, in_range );
4741
4742 /* Set manual mode. */
4743 xmlr_attr_int( cur, "manual", manual );
4744 if (manual > 0)
4745 pilot_weapSetManual( ship, id, manual );
4746
4747 /* Set volley mode. */
4748 xmlr_attr_int( cur, "volley", volley );
4749 if (volley > 0)
4750 pilot_weapSetVolley( ship, id, volley );
4751
4752 if (autoweap) /* Autoweap handles everything except inrange and
4753 manual. */
4754 continue;
4755
4756 /* Set type mode. */
4757 xmlr_attr_int_def( cur, "type", weap_type, -1 );
4758 if (weap_type == -1) {
4759 WARN( _( "Player ship '%s' missing 'type' tag for weapon set." ),
4760 ship->name );
4761 continue;
4762 }
4763 pilot_weapSetType( ship, id, weap_type );
4764
4765 /* Parse individual weapons. */
4766 ccur = cur->xmlChildrenNode;
4767 do {
4768 int weapid;
4769 /* Only nodes. */
4770 xml_onlyNodes( ccur );
4771
4772 /* Only weapon nodes. */
4773 if (!xml_isNode( ccur, "weapon" )) {
4774 WARN( _( "Player ship '%s' has unknown 'weaponset' child node "
4775 "'%s' (expected 'weapon')." ),
4776 ship->name, ccur->name );
4777 continue;
4778 }
4779
4780 weapid = xml_getInt( ccur );
4781 if (( weapid < 0 ) || ( weapid >= array_size( ship->outfits ) )) {
4782 WARN( _( "Player ship '%s' has invalid weapon id %d [max %d]." ),
4783 ship->name, weapid, array_size( ship->outfits ) - 1 );
4784 continue;
4785 }
4786
4787 /* Add the weapon set. */
4788 pilot_weapSetAdd( ship, id, ship->outfits[weapid] );
4789
4790 } while (xml_nextNode( ccur ));
4791 } while (xml_nextNode( cur ));
4792 } while (xml_nextNode( node ));
4793
4794 /* If we set the flag during creation, it changes the ID and behaviour, so
4795 * we set it after creation. It is necessary to set or the auto weapons uses
4796 * the non-player variant for the player ships. */
4797 pilot_setFlag( ship, PILOT_PLAYER );
4798
4799 /* Set up autoweap if necessary. */
4800 ship->autoweap = autoweap;
4801 if (autoweap)
4802 pilot_weaponAuto( ship );
4803 pilot_weaponSafe( ship );
4804 /* Copy the weapon set over to the player ship, where we store it. */
4805 ws_copy( ps.weapon_sets, ship->weapon_sets );
4806
4807 /* Set aimLines */
4808 ship->aimLines = aim_lines;
4809
4810 /* Add it to the stack if it's not what the player is in */
4811 if (is_player == 0)
4813 else {
4814 pilot_setPlayer( ship );
4815 player.ps = ps;
4816 }
4817
4818 return 0;
4819}
4820
4824void player_stealth( void )
4825{
4826 if (player.p == NULL)
4827 return;
4828
4829 /* Handle destealth first. */
4830 if (pilot_isFlag( player.p, PILOT_STEALTH )) {
4832 player_message( _( "You have destealthed." ) );
4833 return;
4834 }
4835
4836 /* Stealth case. */
4837 if (pilot_stealth( player.p )) {
4838 player_message( "#g%s", _( "You have entered stealth mode." ) );
4839 } else {
4840 /* Stealth failed. */
4841 /*
4842 if ( player.p->lockons > 0 )
4843 player_message( "#r%s",
4844 _( "Unable to stealth: missiles locked on!" ) );
4845 else
4846 */
4847 player_message( "#r%s", _( "Unable to stealth: other pilots nearby!" ) );
4848 }
4849}
void ai_think(Pilot *pilot, double dt, int dotask)
Heart of the AI, brains of the pilot.
Definition ai.c:812
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_front(ptr_array)
Returns the first element in the array.
Definition array.h:221
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_back(ptr_array)
Returns the last element in the array.
Definition array.h:228
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
int player_canBoard(int noisy)
Sees if the pilot can board a pilot.
Definition board.c:110
int player_tryBoard(int noisy)
Attempt to board the player's target.
Definition board.c:152
void cam_setZoom(double zoom)
Sets the camera zoom.
Definition camera.c:77
void cam_setTargetPilot(unsigned int follow, int soft_over)
Sets the target to follow.
Definition camera.c:158
void cam_setTargetPos(double x, double y, int soft_over)
Sets the camera target to a position.
Definition camera.c:194
double cam_getZoom(void)
Gets the camera zoom.
Definition camera.c:101
int cam_getTarget(void)
Returns the camera's current target.
Definition camera.c:222
void claim_clear(void)
Clears the claims on all systems.
Definition claim.c:222
void col_blend(glColour *blend, const glColour *fg, const glColour *bg, float alpha)
Blends two colours.
Definition colour.c:206
int comm_openPilot(unsigned int pilot)
Opens the communication dialogue with a pilot.
Definition comm.c:60
int comm_openSpob(Spob *spob)
Opens a communication dialogue with a spob.
Definition comm.c:187
Commodity * commodity_get(const char *name)
Gets a commodity by name.
Definition commodity.c:151
void dialogue_alert(const char *fmt,...)
Displays an alert popup with only an ok button and a message.
Definition dialogue.c:130
char * dialogue_input(const char *title, int min, int max, const char *fmt,...)
Creates a dialogue that allows the player to write a message.
Definition dialogue.c:441
void dialogue_msg(const char *caption, const char *fmt,...)
Opens a dialogue window with an ok button and a message.
Definition dialogue.c:227
void dialogue_alertRaw(const char *msg)
Displays an alert popup with only an ok button and a message.
Definition dialogue.c:149
int dialogue_YesNo(const char *caption, const char *fmt,...)
Runs a dialogue with both yes and no options.
Definition dialogue.c:352
int economy_init(void)
Initializes the economy.
Definition economy.c:438
void economy_clearKnown(void)
Clears all system knowledge.
Definition economy.c:979
void equipment_regenLists(unsigned int wid, int outfits, int ships)
Regenerates the equipment window lists.
Definition equipment.c:1429
void escort_freeList(Pilot *p)
Remove all escorts from a pilot.
Definition escort.c:57
int escort_clearDeployed(Pilot *p, int slot)
Clears deployed escorts of a pilot.
Definition escort.c:243
int escort_addList(Pilot *p, const Ship *ship, EscortType_t type, unsigned int id, int persist)
Adds an escort to the escort list of a pilot.
Definition escort.c:37
void escort_rmListIndex(Pilot *p, int i)
Remove from escorts list.
Definition escort.c:70
int escorts_jump(const Pilot *parent, const JumpPoint *jp)
Have a pilot order its escorts to jump.
Definition escort.c:430
int event_start(const char *name, unsigned int *id)
Starts an event.
Definition event.c:123
const char * event_dataName(int dataid)
Gets the event data name from id.
Definition event.c:781
void events_cleanup(void)
Cleans up and removes active events.
Definition event.c:737
int event_dataID(const char *evdata)
Gets the event data id from name.
Definition event.c:766
void events_trigger(EventTrigger_t trigger)
Runs all the events matching a trigger.
Definition event.c:319
void factions_reset(void)
Resets player standing and flags of factions to default.
Definition faction.c:1858
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1450
int faction_get(const char *name)
Gets a faction ID by name.
Definition faction.c:209
void gui_clearMessages(void)
Clears the GUI messages.
Definition gui.c:1037
void gui_setTarget(void)
Player just changed their pilot target.
Definition gui.c:1872
void gui_setDefaults(void)
Definition gui.c:198
const char * gui_pick(void)
Determines which GUI should be used.
Definition gui.c:1933
int gui_load(const char *name)
Attempts to load the actual GUI.
Definition gui.c:1965
void gui_radarGetRes(double *res)
Outputs the radar's resolution.
Definition gui.c:1029
void gui_forceBlink(void)
Force sets the spob and pilot radar blink.
Definition gui.c:1395
void gui_cleanup(void)
Cleans up the GUI.
Definition gui.c:2047
void gui_setNav(void)
Player just changed their nav computer target.
Definition gui.c:1864
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition gui.c:353
int hooks_runParam(const char *stack, const HookParam *param)
Runs all the hooks of stack.
Definition hook.c:1029
int hooks_runParamDeferred(const char *stack, const HookParam *param)
Runs all the hooks of stack in the next frame. Does not trigger right away.
Definition hook.c:996
int hooks_run(const char *stack)
Runs all the hooks of stack.
Definition hook.c:1049
Handles the info menu.
void info_buttonClear(void)
Clears all the registered buttons.
Definition info.c:244
double player_right
Definition player.c:131
void input_mouseShow(void)
Shows the mouse.
Definition input.c:453
double player_left
Definition player.c:130
void input_enableAll(void)
Enables all the keybinds.
Definition input.c:427
void input_mouseHide(void)
Hides the mouse.
Definition input.c:462
void input_getKeybindDisplay(KeySemanticType keybind, char *buf, int len)
Gets the display name (translated and human-readable) of a keybind.
Definition input.c:547
int intro_display(const char *text, const char *mus)
Displays the introduction sequence.
Definition intro.c:323
void takeoff(int delay, int nosave)
Makes the player take off if landed.
Definition land.c:1690
unsigned int land_getWid(int window)
Gets the WID of a window by type.
Definition land.c:1162
void land_cleanup(void)
Cleans up some land-related variables.
Definition land.c:1952
int landed
Definition land.c:78
void land(Spob *p, int load)
Opens up all the land dialogue stuff.
Definition land.c:1413
Spob * land_spob
Definition land.c:87
void land_refuel(void)
Refuels the player's current ship, if possible.
Definition land.c:995
int outfits_filter(const Outfit **outfits, int n, int(*filter)(const Outfit *), const char *name)
Applies a filter function and string to a list of outfits.
int load_refresh(void)
Loads or refreshes saved games for the player.
Definition load.c:233
const nsave_t * load_getList(const char *name)
Gets the array (array.h) of loaded saves.
Definition load.c:612
int lvar_save(const lvar *arr, xmlTextWriterPtr writer)
Saves the mission variables.
Definition lvar.c:200
lvar * lvar_load(xmlNodePtr parent)
Loads the vars from XML file.
Definition lvar.c:241
Handles the important game menus.
void menu_main(void)
Opens the main menu (titlescreen).
Definition menu.c:170
void menu_death(void)
Player death menu, appears when player got creamed.
Definition menu.c:633
Mission ** player_missions
Definition mission.c:45
void missions_run(MissionAvailability loc, int faction, const Spob *pnt, const StarSystem *sys)
Runs missions matching location, all Lua side and one-shot.
Definition mission.c:326
int mission_start(const char *name, unsigned int *id)
Starts a mission.
Definition mission.c:365
int mission_getID(const char *name)
Gets id from mission name.
Definition mission.c:99
void missions_cleanup(void)
Cleans up all the player's active missions.
Definition mission.c:1327
const MissionData * mission_get(int id)
Gets a MissionData based on ID.
Definition mission.c:115
int music_choose(const char *situation)
Actually runs the music stuff, based on situation.
Definition music.c:426
Header file with generic functions and naev-specifics.
#define APPNAME
Definition naev.h:30
const char * naev_version(int long_version)
Returns the version in a human readable string.
#define MIN(x, y)
Definition naev.h:39
#define CLAMP(a, b, x)
Definition naev.h:41
#define ABS(x)
Definition naev.h:32
#define pow2(x)
Definition naev.h:53
#define PATH_MAX
Definition naev.h:57
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
int news_init(void)
Initiate news linked list with a stack.
Definition news.c:123
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:914
lua_State * naevL
Definition nlua.c:54
int lua_isoutfit(lua_State *L, int ind)
Checks to see if ind is a outfit.
const Outfit * lua_tooutfit(lua_State *L, int ind)
Lua bindings to interact with outfits.
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
int lua_isship(lua_State *L, int ind)
Checks to see if ind is a ship.
Definition nlua_ship.c:209
const Ship * lua_toship(lua_State *L, int ind)
Lua bindings to interact with ships.
Definition nlua_ship.c:143
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:203
int nlua_loadTk(nlua_env env)
Loads the Toolkit Lua library.
Definition nlua_tk.c:98
void var_cleanup(void)
Cleans up all the mission variables.
Definition nlua_var.c:194
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition nstring.c:83
void ntime_set(ntime_t t)
Sets the time absolutely, does NOT generate an event, used at init.
Definition ntime.c:222
ntime_t ntime_get(void)
Gets the current time.
Definition ntime.c:113
void ntime_inc(ntime_t t)
Sets the time relatively.
Definition ntime.c:243
void ntime_getR(int *cycles, int *periods, int *seconds, double *rem)
Gets the current time broken into individual components.
Definition ntime.c:121
void ntime_setR(int cycles, int periods, int seconds, double rem)
Loads time including remainder.
Definition ntime.c:231
void gl_screenshot(const char *filename)
Takes a screenshot.
Definition opengl.c:106
void gl_renderShader(double x, double y, double w, double h, double r, const SimpleShader *shd, const glColour *c, int center)
Renders a simple shader.
void gl_renderLine(double x1, double y1, double x2, double y2, const glColour *c)
Draws a line.
void gl_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_screenToGameCoords(double *nx, double *ny, int bx, int by)
Converts screen coordinates to in-game coordinates.
void gl_renderCircle(double cx, double cy, double r, const glColour *c, int filled)
Draws a circle.
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
Definition outfit.c:223
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:649
int outfit_isLocalMap(const Outfit *o)
Checks if outfit is a local space map.
Definition outfit.c:719
int outfit_compareTech(const void *outfit1, const void *outfit2)
Function meant for use with C89, C99 algorithm qsort().
Definition outfit.c:302
const Outfit * outfit_getW(const char *name)
Gets an outfit by name without warning on no-find.
Definition outfit.c:237
int outfit_fitsSlot(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot.
Definition outfit.c:1180
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition outfit.c:701
int outfit_isMap(const Outfit *o)
Checks if outfit is a space map.
Definition outfit.c:710
int outfit_isLicense(const Outfit *o)
Checks if outfit is a license.
Definition outfit.c:728
int outfit_isGUI(const Outfit *o)
Checks if outfit is a GUI.
Definition outfit.c:737
int outfit_licenseExists(const char *name)
Checks to see if a license exists.
Definition outfit.c:3321
void pause_setSpeed(double mod)
Adjusts the game's dt modifier.
Definition pause.c:61
int paused
Definition pause.c:18
void pilot_free(Pilot *p)
Frees and cleans up a pilot.
Definition pilot.c:3902
void pilot_stackRemove(Pilot *p)
Tries to remove a pilot from the stack.
Definition pilot.c:3992
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition pilot.c:699
void pilot_cooldown(Pilot *p, int dochecks)
Begins active cooldown, reducing hull and outfit temperatures.
Definition pilot.c:918
unsigned int pilot_getNearestPilot(const Pilot *p)
Get the nearest pilot to a pilot.
Definition pilot.c:437
Pilot * pilot_getTarget(Pilot *p)
Gets the target of a pilot using a fancy caching system.
Definition pilot.c:655
Pilot * pilot_createEmpty(const Ship *ship, const char *name, int faction, PilotFlags flags)
Creates a pilot without adding it to the stack.
Definition pilot.c:3627
double pilot_face(Pilot *p, double dir, double dt)
Tries to turn the pilot to face dir.
Definition pilot.c:827
double pilot_getNearestPos(const Pilot *p, unsigned int *tp, double x, double y, int disabled)
Get the nearest pilot to a pilot from a certain position.
Definition pilot.c:564
Pilot * pilot_setPlayer(Pilot *after)
Replaces the player's pilot with an alternate ship with the same ID.
Definition pilot.c:3735
void pilot_renderOverlay(Pilot *p)
Renders the pilot overlay.
Definition pilot.c:2235
void pilot_setAccel(Pilot *p, double accel)
Sets the pilot's accel.
Definition pilot.c:680
void pilot_cooldownEnd(Pilot *p, const char *reason)
Terminates active cooldown.
Definition pilot.c:986
credits_t pilot_modCredits(Pilot *p, credits_t amount)
Modifies the amount of credits the pilot has.
Definition pilot.c:3300
void pilot_hyperspaceAbort(Pilot *p)
Stops the pilot from hyperspacing.
Definition pilot.c:3140
void pilot_setTurn(Pilot *p, double turn)
Sets the pilot's turn.
Definition pilot.c:688
int pilot_validTarget(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid target for another pilot.
Definition pilot.c:235
void pilot_reset(Pilot *pilot)
Resets a pilot.
Definition pilot.c:3491
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition pilot.c:728
PilotOutfitSlot * pilot_getDockSlot(Pilot *p)
Gets the dock slot of the pilot.
Definition pilot.c:793
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition pilot.c:640
ntime_t pilot_hyperspaceDelay(const Pilot *p)
Calculates the hyperspace delay for a pilot.
Definition pilot.c:3243
double pilot_getNearestPosPilot(const Pilot *p, Pilot **tp, double x, double y, int disabled)
Get the nearest pilot to a pilot from a certain position.
Definition pilot.c:518
static Pilot ** pilot_stack
Definition pilot.c:51
void pilot_render(Pilot *p)
Renders the pilot.
Definition pilot.c:1958
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:93
int pilot_canTarget(const Pilot *p)
Same as pilot_validTarget but without the range check.
Definition pilot.c:270
credits_t pilot_worth(const Pilot *p, int count_unique)
Gets the price or worth of a pilot in credits.
Definition pilot.c:4482
double pilot_aimAngle(Pilot *p, const vec2 *pos, const vec2 *vel)
Returns the angle for a pilot to aim at another pilot.
Definition pilot.c:1025
unsigned int pilot_getNextID(unsigned int id, int mode)
Gets the next pilot based on id.
Definition pilot.c:135
void pilots_cleanAll(void)
Even cleans up the player.
Definition pilot.c:4148
void pilot_update(Pilot *pilot, double dt)
Updates the pilot.
Definition pilot.c:2328
unsigned int pilot_getPrevID(unsigned int id, int mode)
Gets the previous pilot based on ID.
Definition pilot.c:183
int pilot_numOutfit(const Pilot *p, const Outfit *o)
Checks to see how many of an outfit a pilot has.
Definition pilot.c:3269
int pilot_hasCredits(const Pilot *p, credits_t amount)
Checks to see if the pilot has at least a certain amount of credits.
Definition pilot.c:3286
void pilot_setTarget(Pilot *p, unsigned int id)
Sets the target of the pilot.
Definition pilot.c:1352
int pilot_cargoMoveRaw(Pilot *dest, Pilot *src)
Moves cargo from one pilot to another without any checks.
Definition pilot_cargo.c:68
int pilot_cargoAddRaw(Pilot *pilot, const Commodity *cargo, int quantity, unsigned int id)
Adds cargo without checking the pilot's free space.
int pilot_inRangePilot(const Pilot *p, const Pilot *target, double *dist2)
Check to see if a pilot is in sensor range of another.
Definition pilot_ew.c:256
int pilot_ewScanCheck(const Pilot *p)
Checks to see if a scan is done.
Definition pilot_ew.c:74
int pilot_inRangeSpob(const Pilot *p, int target)
Check to see if a spob is in sensor range of the pilot.
Definition pilot_ew.c:293
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition pilot_ew.c:585
int pilot_stealth(Pilot *p)
Stealths a pilot.
Definition pilot_ew.c:543
int pilot_hasOutfitLimit(const Pilot *p, const char *limit)
Checks to see if a pilot has an outfit with a specific outfit type.
int pilot_slotsCheckSafety(const Pilot *p)
Pilot slot safety check - makes sure stats are safe.
int pilot_outfitLAdd(const Pilot *pilot, PilotOutfitSlot *po)
Outfit is added to a ship.
int pilot_maxAmmoO(const Pilot *p, const Outfit *o)
Gets the maximum available ammo for a pilot for a specific outfit.
void pilot_outfitLOnjumpin(Pilot *pilot)
Runs Lua outfits when pilot jumps into a system.
void pilot_calcStats(Pilot *pilot)
Recalculates the pilot's stats based on his outfits.
int pilot_addAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Adds some ammo to the pilot stock.
int pilot_hasIntrinsic(const Pilot *pilot, const Outfit *outfit)
Gets how many copies of an intrinsic a pilot has.
int pilot_rmOutfitRaw(Pilot *pilot, PilotOutfitSlot *s)
Removes an outfit from the pilot without doing any checks.
int pilot_outfitLRemove(const Pilot *pilot, PilotOutfitSlot *po)
Outfit is removed froma ship.
int pilot_addOutfitRaw(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds an outfit to the pilot, ignoring CPU or other limits.
int pilot_rmOutfitIntrinsic(Pilot *pilot, const Outfit *outfit)
Removes an outfit from an intrinsic slot.
int pilot_isSpaceworthy(const Pilot *p)
Pilot safety check - makes sure stats are safe.
int pilot_addOutfitIntrinsic(Pilot *pilot, const Outfit *outfit)
Adds an outfit as an intrinsic slot.
void pilot_outfitLInitAll(Pilot *pilot)
Runs the pilot's Lua outfits init script.
void pilot_weapSetAdd(Pilot *p, int id, const PilotOutfitSlot *o)
Adds an outfit to a weapon set.
void pilot_weapSetInrange(Pilot *p, int id, int inrange)
Changes the weapon set inrange property.
void pilot_weaponSafe(Pilot *p)
Sets the weapon set as safe.
void pilot_weapSetManual(Pilot *p, int id, int manual)
Changes the weapon set manual property.
void pilot_afterburnOver(Pilot *p)
Deactivates the afterburner.
void pilot_weapSetVolley(Pilot *p, int id, int volley)
Changes the weapon set volley property.
void ws_copy(PilotWeaponSet dest[PILOT_WEAPON_SETS], const PilotWeaponSet src[PILOT_WEAPON_SETS])
Copies a weapon set over.
void ws_free(PilotWeaponSet ws[PILOT_WEAPON_SETS])
Frees a weapon set.
int pilot_outfitOffAll(Pilot *p)
Disables all active outfits for a pilot.
void pilot_weapSetUpdateOutfitState(Pilot *p)
Updates the local state of all the pilot's outfits based on the weapon sets.
void pilot_weapSetClear(Pilot *p, int id)
Clears a weapon set.
void pilot_weaponAuto(Pilot *p)
Tries to automatically set and create the pilot's weapon set.
int pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
void pilot_weapSetType(Pilot *p, int id, WeaponSetType type)
Changes the weapon sets mode.
void player_hailStart(void)
Starts the hail sounds and aborts autoNav.
Definition player.c:2011
void player_stealth(void)
Input binding for toggling stealth for the player.
Definition player.c:4824
void player_weapSetPress(int id, double value, int repeat)
Handles keyboard events involving the player's weapon-set keys. It's valid to call this while gamepla...
Definition player.c:1491
int player_eventAlreadyDone(int id)
Checks to see if player has already completed a event.
Definition player.c:3180
int player_nships(void)
Gets the amount of ships player has in storage.
Definition player.c:2814
int player_save(xmlTextWriterPtr writer)
Save the freaking player in a freaking xmlfile.
Definition player.c:3404
int snd_jump
Definition player.c:104
int snd_nav
Definition player.c:105
void player_soundResume(void)
Resumes the ship's sounds.
Definition player.c:993
static int player_saveShipSlot(xmlTextWriterPtr writer, const PilotOutfitSlot *slot, int i)
Saves an outfit slot.
Definition player.c:3518
int player_rmOutfit(const Outfit *o, int quantity)
Remove an outfit from the player's outfit stack.
Definition player.c:3053
void player_runHooks(void)
Runs hooks for the player.
Definition player.c:3248
static int player_shipsCompare(const void *arg1, const void *arg2)
PlayerShip_t compare function for qsort().
Definition player.c:2724
void player_updateSpecific(Pilot *pplayer, const double dt)
Does a player specific update.
Definition player.c:1395
static void player_clearEscorts(void)
Clears escorts to make sure deployment is safe.
Definition player.c:3272
int player_getOutfitsFiltered(const Outfit ***outfits, int(*filter)(const Outfit *o), const char *name)
Prepares two arrays for displaying in an image array.
Definition player.c:2953
int player_hasLicense(const char *license)
Checks to see if player has license.
Definition player.c:3207
void player_autohail(void)
Automatically tries to hail a pilot that hailed the player.
Definition player.c:2521
void player_dead(void)
Player got pwned.
Definition player.c:2684
void player_swapShip(const char *shipname, int move_cargo)
Swaps player's current ship with their ship named shipname.
Definition player.c:590
static void player_renderAimHelper(double dt)
Renders the aim helper.
Definition player.c:1181
void player_cooldownBrake(void)
Starts braking or active cooldown.
Definition player.c:2623
void player_accel(double acc)
Start accelerating.
Definition player.c:2206
static double player_hailTimer
Definition player.c:115
int player_ships(char **sships, glTexture **tships)
Returns a buffer with all the player's ships names.
Definition player.c:2787
void player_targetEscort(int prev)
Targets the pilot.
Definition player.c:2349
void player_targetAsteroidSet(int field, int id)
Sets the player's target asteroid.
Definition player.c:1596
static void player_parseShipSlot(xmlNodePtr node, Pilot *ship, PilotOutfitSlot *slot)
Parses a ship outfit slot.
Definition player.c:4407
static void player_renderStealthUnderlay(double dt)
Renders the stealth overlay for the player.
Definition player.c:1113
int player_addOutfit(const Outfit *o, int quantity)
Adds an outfit to the player outfit stack.
Definition player.c:2988
void player_soundPlay(int sound, int once)
Plays a sound at the player.
Definition player.c:958
static int player_soundReserved
Definition player.c:911
void player_new(void)
Creates a new player.
Definition player.c:256
static PlayerShip_t * player_stack
Definition player.c:121
int snd_hypJump
Definition player.c:112
void player_warp(double x, double y)
Warps the player to the new position.
Definition player.c:1007
int snd_hypEng
Definition player.c:109
static int player_parseDoneMissions(xmlNodePtr parent)
Parses player's done missions.
Definition player.c:4172
static PlayerShip_t * player_newShipMake(const char *name)
Actually creates the new ship.
Definition player.c:544
static int player_parseEscorts(xmlNodePtr parent)
Parses the escorts from the escort node.
Definition player.c:4276
void player_checkLandAck(void)
Revokes landing authorization if the player's reputation is too low.
Definition player.c:1791
int player_numOutfits(void)
Gets the amount of different outfits in the player outfit stack.
Definition player.c:2976
static int player_parseShip(xmlNodePtr parent, int is_player)
Parses a player's ship.
Definition player.c:4441
void player_targetSet(unsigned int id)
Sets the player's target.
Definition player.c:2230
static int screenshot_cur
Definition player.c:2411
static void player_tryAddLicense(const char *name)
Tries to get an outfit for the player or looks for equivalents.
Definition player.c:3896
void player_render(double dt)
Renders the player.
Definition player.c:1055
void player_hailSpob(void)
Opens communication with the player's spob target.
Definition player.c:2503
credits_t player_shipPrice(const char *shipname, int count_unique)
Calculates the price of one of the player's ships.
Definition player.c:733
void player_resetSpeed(void)
Resets the player speed stuff.
Definition player.c:1521
static int player_lastEngineSound
Definition player.c:113
const PlayerShip_t * player_getShipStack(void)
Gets the array (array.h) of the player's ships.
Definition player.c:2804
static int player_outfitCompare(const void *arg1, const void *arg2)
qsort() compare function for PlayerOutfit_t sorting.
Definition player.c:2925
void player_targetHyperspaceSet(int id, int nomsg)
Sets the player's hyperspace target.
Definition player.c:1905
int snd_hail
Definition player.c:106
static char * player_message_noland
Definition player.c:83
static int player_parseMetadata(xmlNodePtr parent)
Parses the player metadata.
Definition player.c:4326
void player_cleanup(void)
Cleans up player stuff like player_stack.
Definition player.c:800
static int player_parseLicenses(xmlNodePtr parent)
Parses player's licenses.
Definition player.c:4224
void player_targetHyperspace(void)
Gets a hyperspace target.
Definition player.c:1940
static double player_timer
Definition player.c:134
static int * events_done
Definition player.c:141
int snd_hypPowDown
Definition player.c:110
PlayerShip_t * player_newShip(const Ship *ship, const char *def_name, int trade, const char *acquired, int noname)
Creates a new ship for player.
Definition player.c:470
void player_rmShip(const char *shipname)
Removes one of the player's ships.
Definition player.c:775
int player_outfitOwnedTotal(const Outfit *o)
Definition player.c:2912
void player_hyperspacePreempt(int preempt)
Enables or disables jump points preempting spobs in autoface and target clearing.
Definition player.c:1980
PlayerShip_t * player_getPlayerShip(const char *shipname)
Gets a specific ship.
Definition player.c:2863
void player_toggleMouseFly(void)
Toggles mouse flying.
Definition player.c:2601
void player_targetNearest(void)
Player targets nearest pilot.
Definition player.c:2393
static PlayerOutfit_t * player_outfits
Definition player.c:124
static credits_t player_payback
Definition player.c:81
double player_dt_default(void)
Returns the player's total default time delta based on time dilation stuff.
Definition player.c:2001
void player_update(Pilot *pplayer, const double dt)
Player update function.
Definition player.c:1379
static const Ship * player_tryGetShip(const char *name)
Tries to get an ship for the player or looks for equivalents.
Definition player.c:3869
const char ** player_getLicenses()
Gets the array (array.h) of license names in the player's inventory.
Definition player.c:3240
void player_brokeHyperspace(void)
Player actually broke hyperspace (entering new system).
Definition player.c:2102
static credits_t player_creds
Definition player.c:80
int player_getHypPreempt(void)
Returns whether the jump point target should preempt the spob target.
Definition player.c:1990
int player_missionAlreadyDone(int id)
Checks to see if player has already completed a mission.
Definition player.c:3125
void player_renderUnderlay(double dt)
Renders the player underlay.
Definition player.c:1099
static int player_hailCounter
Definition player.c:114
int player_addEscorts(void)
Adds the player's escorts.
Definition player.c:3288
static int player_parseDoneEvents(xmlNodePtr parent)
Parses player's done missions.
Definition player.c:4198
#define RADAR_RES_DEFAULT
Definition player.c:95
void player_soundStop(void)
Stops playing player sounds.
Definition player.c:966
void player_restoreControl(int reason, const char *str)
Aborts autonav and other states that take control of the ship.
Definition player.c:1535
void player_addLicense(const char *license)
Gives the player a license.
Definition player.c:3225
double player_acc
Definition player.c:132
credits_t player_modCredits(credits_t amount)
Modifies the amount of credits the player has.
Definition player.c:1047
int * player_eventsDoneList(void)
Gets a list of all the events the player has done.
Definition player.c:3196
int snd_hypPowUpJump
Definition player.c:111
int player_jump(void)
Actually attempts to jump in hyperspace.
Definition player.c:2029
static nlua_env scan_env
Definition player.c:85
void player_targetSpobSet(int id)
Sets the player's target spob.
Definition player.c:1559
static int player_filterSuitableSpob(Spob *p)
Filter function for space_getRndSpob.
Definition player.c:4161
void player_clear(void)
Clears the targets.
Definition player.c:1019
static const Outfit * player_tryGetOutfit(const char *name, int q)
Tries to get an outfit for the player or looks for equivalents.
Definition player.c:3842
static void player_addOutfitToPilot(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds outfit to pilot if it can.
Definition player.c:4378
static void player_spobOutOfRangeMsg(void)
Displays an out of range message for the player's currently selected spob.
Definition player.c:2466
static int player_runUpdaterScript(const char *type, const char *name, int q)
Runs the save updater script, leaving any result on the stack of naevL.
Definition player.c:3799
int snd_hypPowUp
Definition player.c:108
static Spob * player_parse(xmlNodePtr parent)
Parses the player node.
Definition player.c:3923
int player_hasShip(const char *shipname)
Sees if player has a ship of a name.
Definition player.c:2825
void player_accelOver(void)
Done accelerating.
Definition player.c:2220
static int player_gui_group
Definition player.c:102
void player_think(Pilot *pplayer, const double dt)
Basically uses keyboard input instead of AI input. Used in pilot.c.
Definition player.c:1252
int snd_target
Definition player.c:103
int player_land(int loud)
Try to land or target closest spob if no land target.
Definition player.c:1653
static int player_newMake(void)
Actually creates a new player.
Definition player.c:388
static int player_saveShip(xmlTextWriterPtr writer, PlayerShip_t *pship)
Saves a ship.
Definition player.c:3539
static int player_hyper_group
Definition player.c:101
void player_soundPause(void)
Pauses the ship's sounds.
Definition player.c:982
static int player_saveEscorts(xmlTextWriterPtr writer)
Saves the player's escorts.
Definition player.c:3365
Player_t player
Definition player.c:77
int player_hasCredits(credits_t amount)
Checks to see if the player has enough credits.
Definition player.c:1036
static int player_engine_group
Definition player.c:100
void player_shipsSort(void)
Sorts the players ships.
Definition player.c:2769
void player_missionFinished(int id)
Marks a mission as completed.
Definition player.c:3094
int player_outfitOwned(const Outfit *o)
Gets how many of the outfit the player owns.
Definition player.c:2882
void player_targetClearAll(void)
Clears all player targets: hyperspace, spob, asteroid, etc...
Definition player.c:2336
static int * missions_done
Definition player.c:139
static char ** player_licenses
Definition player.c:90
void player_nolandMsg(const char *str)
Sets the no land message.
Definition player.c:1822
void player_targetPrev(int mode)
Cycles to previous target.
Definition player.c:2307
const PlayerOutfit_t * player_getOutfits(void)
Gets an array (array.h) of the player's outfits.
Definition player.c:2940
Spob * player_load(xmlNodePtr parent)
Loads the player stuff.
Definition player.c:3736
static void player_newSetup()
Sets up a new player.
Definition player.c:216
static int player_ran_updater
Definition player.c:82
static const Ship * player_ship
Definition player.c:78
void player_soundPlayGUI(int sound, int once)
Plays a GUI sound (unaffected by time accel).
Definition player.c:947
static void player_renderStealthOverlay(double dt)
Renders the stealth overlay for the player.
Definition player.c:1152
void player_targetClear(void)
Clears the player's ship, spob or hyperspace target, in that order.
Definition player.c:2315
void player_targetHostile(void)
Targets the nearest hostile enemy to the player.
Definition player.c:2258
static void player_initSound(void)
Initializes the player sounds.
Definition player.c:916
static int player_parseInventory(xmlNodePtr parent)
Parses player's inventory.
Definition player.c:4249
void player_eventFinished(int id)
Marks a event as completed.
Definition player.c:3151
void player_screenshot(void)
Takes a screenshot.
Definition player.c:2415
void player_approach(void)
Logic to make the player approach a target pilot to board or spob to land on.
Definition player.c:1839
void player_destroyed(void)
Player blew up in a fireball.
Definition player.c:2698
void player_targetSpob(void)
Cycle through spob targets.
Definition player.c:1620
int player_init(void)
Initializes player stuff.
Definition player.c:199
int * player_missionsDoneList(void)
Gets a list of all the missions the player has done.
Definition player.c:3141
static void player_checkHail(void)
Checks to see if player is still being hailed and clears hail counters if he isn't.
Definition player.c:2446
Pilot * player_getShip(const char *shipname)
Gets a specific ship.
Definition player.c:2844
static int player_thinkMouseFly(double dt)
Handles mouse flying based on cursor position.
Definition player.c:2655
void player_targetNext(int mode)
Cycles to next target.
Definition player.c:2297
static int player_saveMetadata(xmlTextWriterPtr writer)
Saves the player meta-data.
Definition player.c:3691
void player_hail(void)
Opens communication with the player's target.
Definition player.c:2476
void player_autonavEnd(void)
Ends the autonav.
int player_autonavInit(void)
Initialize the autonav code.
void player_autonavResetSpeed(void)
Resets the game speed.
void player_autonavStart(void)
Starts autonav.
void player_autonavReset(double s)
Resets the game speed without disabling autonav.
void player_autonavBoard(unsigned int p)
Starts autonav with a pilot to board.
void player_autonavSpob(const char *name, int tryland)
Starts autonav with a spob destination.
void player_autonavEnter(void)
Signal to the autonav that a new system was entered.
void player_autonavAbort(const char *reason)
Aborts autonav.
void player_thinkAutonav(Pilot *pplayer, double dt)
Handles autonav thinking.
int pfleet_deploy(PlayerShip_t *ps)
Deploys a player's pilot.
void pfleet_update(void)
Updates the used fleet capacity of the player.
const char ** player_guiList(void)
Gets the list of GUIs.
Definition player_gui.c:103
int player_guiAdd(const char *name)
Adds a gui to the player.
Definition player_gui.c:39
int player_guiCheck(const char *name)
Check if player has a GUI.
Definition player_gui.c:88
void player_guiCleanup(void)
Cleans up the player's GUI list.
Definition player_gui.c:28
int player_inventoryAdd(const char *name, int amount)
Adds an item to the player inventory.
static PlayerItem * inventory
void player_inventoryClear(void)
Clears the inventory and frees memory.
const PlayerItem * player_inventory(void)
Gets the whole player inventory.
static const double c[]
Definition rng.c:256
static const double d[]
Definition rng.c:263
ShipClass ship_classFromString(const char *str)
Gets the machine ship class identifier from a human readable string.
Definition ship.c:265
const char * ship_classDisplay(const Ship *s)
Gets the ship's display class in human readable form.
Definition ship.c:203
const Ship * ship_getW(const char *name)
Gets a ship based on its name without warning.
Definition ship.c:113
int ship_gfxLoadNeeded(void)
Tries to load the graphics for all ships that need it.
Definition ship.c:603
const char * ship_classToString(ShipClass class)
Gets the ship class name in human readable form.
Definition ship.c:216
int ship_gfxLoaded(const Ship *s)
Checks to see if a ship has loaded graphics.
Definition ship.c:595
glTexture * ship_gfxStore(const Ship *s, int size, double dir, double updown, double glow)
Get the store gfx.
Definition ship.c:383
const Ship * ship_get(const char *name)
Gets a ship based on its name.
Definition ship.c:99
void shiplog_clear(void)
Clear the shiplog.
Definition shiplog.c:367
int sound_createGroup(int size)
Creates a sound group.
Definition sound.c:1338
void sound_pitchGroup(int group, double pitch)
Sets the pitch of a group.
Definition sound.c:1614
void sound_resumeGroup(int group)
Resumes all the sounds in a group.
Definition sound.c:1528
void sound_speedGroup(int group, int enable)
Sets whether or not the speed affects a group.
Definition sound.c:1560
int sound_playGroup(int group, int sound, int once)
Plays a sound in a group.
Definition sound.c:1410
void sound_stopGroup(int group)
Stops all the sounds in a group.
Definition sound.c:1486
void sound_stopAll(void)
Stops all the playing voices.
Definition sound.c:1049
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition sound.c:761
void sound_pauseGroup(int group)
Pauses all the sounds in a group.
Definition sound.c:1506
void sound_volumeGroup(int group, double volume)
Sets the volume of a group.
Definition sound.c:1584
void sound_setSpeed(double s)
Sets the speed to play the sound at.
Definition sound.c:1158
void space_init(const char *sysname, int do_simulate)
Initializes the system.
Definition space.c:1620
void space_gfxUnload(StarSystem *sys)
Unloads all the graphics for a star system.
Definition space.c:2281
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition space.c:1107
const char * space_getRndSpob(int landable, unsigned int services, int(*filter)(Spob *p))
Gets the name of a random spob.
Definition space.c:666
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition space.c:1158
char spob_getColourChar(const Spob *p)
Gets the spob colour char.
Definition space.c:1966
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition space.c:1007
int space_calcJumpInPos(const StarSystem *in, const StarSystem *out, vec2 *pos, vec2 *vel, double *dir, const Pilot *p)
Calculates the jump in pos for a pilot.
Definition space.c:556
StarSystem * cur_system
Definition space.c:110
void spob_updateLand(Spob *p)
Updates the land possibilities of a spob.
Definition space.c:2031
void space_clearKnown(void)
Clears all system knowledge.
Definition space.c:3945
const char * spob_getSystemName(const char *spobname)
Get the name of a system from a spobname.
Definition space.c:1082
const char * spob_name(const Spob *p)
Gets the translated name of a spob.
Definition space.c:1834
void space_gfxLoad(StarSystem *sys)
Loads all the graphics for a star system.
Definition space.c:2266
int space_hyperspace(Pilot *p)
Tries to get the pilot into hyperspace.
Definition space.c:529
const char * start_acquired(void)
Gets the module's starting ship was acquired.
Definition start.c:209
const char * start_event(void)
Gets the starting event of the player.
Definition start.c:274
const char * start_mission(void)
Gets the starting mission of the player.
Definition start.c:265
void start_position(double *x, double *y)
Gets the starting position of the player.
Definition start.c:255
const char * start_chapter(void)
Gets the player's starting chapter.
Definition start.c:283
const char * start_ship(void)
Gets the module player starting ship.
Definition start.c:191
ntime_t start_date(void)
Gets the starting date.
Definition start.c:236
const char * start_shipname(void)
Gets the module's starting ship's name.
Definition start.c:200
const char * start_system(void)
Gets the starting system name.
Definition start.c:245
unsigned int start_credits(void)
Gets the player's starting credits.
Definition start.c:227
Represents an asteroid field anchor.
Definition asteroid.h:111
double margin
Definition asteroid.h:126
double radius
Definition asteroid.h:118
Asteroid * asteroids
Definition asteroid.h:116
Represents a single asteroid.
Definition asteroid.h:88
Solid sol
Definition asteroid.h:98
int scanned
Definition asteroid.h:105
Represents a commodity.
Definition commodity.h:57
char * name
Definition commodity.h:58
Stores an escort.
Definition pilot.h:252
unsigned int id
Definition pilot.h:255
int persist
Definition pilot.h:257
EscortType_t type
Definition pilot.h:254
const Ship * ship
Definition pilot.h:253
The actual hook parameter.
Definition hook.h:40
const char * str
Definition hook.h:44
HookParamType type
Definition hook.h:41
LuaAsteroid_t ast
Definition hook.h:54
int ref
Definition hook.h:55
union HookParam::@065274143236224234262250043114351136253171035204 u
const Ship * ship
Definition hook.h:47
Static mission data.
Definition mission.h:64
char * name
Definition mission.h:65
char * gui
Definition outfit.h:358
char * provides
Definition outfit.h:365
OutfitSlotType type
Definition outfit.h:142
A ship outfit, depends radically on the type.
Definition outfit.h:372
char * limit
Definition outfit.h:386
union Outfit::@264277167364127137334024361374356236341374052147 u
OutfitGUIData gui
Definition outfit.h:462
OutfitSlot slot
Definition outfit.h:380
OutfitAfterburnerData afb
Definition outfit.h:458
char * name
Definition outfit.h:373
OutfitLicenseData lic
Definition outfit.h:463
Stores a pilot commodity.
Definition pilot.h:223
const Commodity * commodity
Definition pilot.h:224
unsigned int id
Definition pilot.h:226
Stores an outfit the pilot has.
Definition pilot.h:145
PilotOutfitAmmo ammo
Definition pilot.h:174
ShipOutfitSlot * sslot
Definition pilot.h:151
const Outfit * outfit
Definition pilot.h:149
A pilot Weapon Set Outfit.
Definition pilot.h:187
A weapon set represents a set of weapons that have an action.
Definition pilot.h:207
PilotWeaponSetOutfit * slots
Definition pilot.h:210
WeaponSetType type
Definition pilot.h:208
The representation of an in-game pilot.
Definition pilot.h:263
int advweap
Definition pilot.h:378
double engine_glow
Definition pilot.h:442
ShipStats stats
Definition pilot.h:348
unsigned int id
Definition pilot.h:264
lvar * shipvar
Definition pilot.h:445
PilotWeaponSet weapon_sets[PILOT_WEAPON_SETS]
Definition pilot.h:376
PilotCommodity * commodities
Definition pilot.h:383
int aimLines
Definition pilot.h:379
PilotOutfitSlot * outfit_structure
Definition pilot.h:356
credits_t credits
Definition pilot.h:382
int nav_hyperspace
Definition pilot.h:403
PilotOutfitSlot ** outfits
Definition pilot.h:354
PilotOutfitSlot * outfit_utility
Definition pilot.h:357
PilotOutfitSlot * outfit_intrinsic
Definition pilot.h:360
const Ship * ship
Definition pilot.h:274
double fuel_max
Definition pilot.h:309
int nav_anchor
Definition pilot.h:404
int autoweap
Definition pilot.h:377
Solid solid
Definition pilot.h:275
double fuel
Definition pilot.h:310
PilotOutfitSlot * afterburner
Definition pilot.h:372
char * name
Definition pilot.h:265
Escort_t * escorts
Definition pilot.h:391
unsigned int target
Definition pilot.h:400
int nav_spob
Definition pilot.h:402
PilotOutfitSlot * outfit_weapon
Definition pilot.h:358
int nav_asteroid
Definition pilot.h:405
Represents an item in the player inventory.
Wrapper for outfits.
Definition player.h:64
const Outfit * o
Definition player.h:65
Player ship.
Definition player.h:72
double time_played
Definition player.h:83
double dmg_done_armour
Definition player.h:87
unsigned int death_counter
Definition player.h:94
double dmg_taken_armour
Definition player.h:89
ntime_t acquired_date
Definition player.h:85
int deployed
Definition player.h:80
double dmg_taken_shield
Definition player.h:88
unsigned int landed_times
Definition player.h:93
Pilot * p
Definition player.h:73
int autoweap
Definition player.h:76
unsigned int ships_destroyed[SHIP_CLASS_TOTAL]
Definition player.h:90
char * acquired
Definition player.h:84
unsigned int jumped_times
Definition player.h:92
PilotWeaponSet weapon_sets[PILOT_WEAPON_SETS]
Definition player.h:75
double dmg_done_shield
Definition player.h:86
int favourite
Definition player.h:77
OutfitSlot slot
Definition ship.h:72
double ew_detect
Definition shipstats.h:277
Represents a space ship.
Definition ship.h:97
int flags
Definition ship.h:112
char * name
Definition ship.h:100
int sound
Definition ship.h:167
double engine_pitch
Definition ship.h:168
int points
Definition ship.h:110
vec2 vel
Definition physics.h:48
double dir
Definition physics.h:46
vec2 pos
Definition physics.h:49
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
int can_land
Definition space.h:125
char * land_msg
Definition space.h:128
int land_override
Definition space.h:126
int lua_land
Definition space.h:170
double radius
Definition space.h:110
nlua_env lua_env
Definition space.h:164
char * name
Definition space.h:105
vec2 pos
Definition space.h:109
int lua_can_land
Definition space.h:169
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
Contains a mission variable.
Definition lvar.h:24
Definition msgcat.c:196
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46
int toolkit_isOpen(void)
Checks to see if the toolkit is open.
Definition toolkit.c:93
void diff_clear(void)
Removes all active diffs. (Call before economy_destroy().)
Definition unidiff.c:1858