-
Notifications
You must be signed in to change notification settings - Fork 280
/
server.cpp
18851 lines (13612 loc) · 679 KB
/
server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <math.h>
#include <assert.h>
#include <float.h>
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/SettingsManager.h"
#include "minorGems/util/SimpleVector.h"
#include "minorGems/network/SocketServer.h"
#include "minorGems/network/SocketPoll.h"
#include "minorGems/network/web/WebRequest.h"
#include "minorGems/network/web/URLUtils.h"
#include "minorGems/crypto/hashes/sha1.h"
#include "minorGems/system/Thread.h"
#include "minorGems/system/Time.h"
#include "minorGems/game/doublePair.h"
#include "minorGems/util/log/AppLog.h"
#include "minorGems/util/log/FileLog.h"
#include "minorGems/formats/encodingUtils.h"
#include "minorGems/io/file/File.h"
#include "map.h"
#include "../gameSource/transitionBank.h"
#include "../gameSource/objectBank.h"
#include "../gameSource/objectMetadata.h"
#include "../gameSource/animationBank.h"
#include "../gameSource/categoryBank.h"
#include "lifeLog.h"
#include "foodLog.h"
#include "backup.h"
#include "triggers.h"
#include "playerStats.h"
#include "lineageLog.h"
#include "serverCalls.h"
#include "failureLog.h"
#include "names.h"
#include "curses.h"
#include "lineageLimit.h"
#include "objectSurvey.h"
#include "language.h"
#include "familySkipList.h"
#include "lifeTokens.h"
#include "fitnessScore.h"
#include "arcReport.h"
#include "minorGems/util/random/JenkinsRandomSource.h"
//#define IGNORE_PRINTF
#ifdef IGNORE_PRINTF
#define printf(fmt, ...) (0)
#endif
static JenkinsRandomSource randSource;
#include "../gameSource/GridPos.h"
#define HEAT_MAP_D 13
float targetHeat = 10;
double secondsPerYear = 60.0;
#define PERSON_OBJ_ID 12
int minPickupBabyAge = 10;
int babyAge = 5;
// age when bare-hand actions become available to a baby (opening doors, etc.)
int defaultActionAge = 3;
double forceDeathAge = 60;
double minSayGapInSeconds = 1.0;
int maxLineageTracked = 20;
int apocalypsePossible = 0;
char apocalypseTriggered = false;
char apocalypseRemote = false;
GridPos apocalypseLocation = { 0, 0 };
int lastApocalypseNumber = 0;
double apocalypseStartTime = 0;
char apocalypseStarted = false;
char postApocalypseStarted = false;
double remoteApocalypseCheckInterval = 30;
double lastRemoteApocalypseCheckTime = 0;
WebRequest *apocalypseRequest = NULL;
char monumentCallPending = false;
int monumentCallX = 0;
int monumentCallY = 0;
int monumentCallID = 0;
static double minFoodDecrementSeconds = 5.0;
static double maxFoodDecrementSeconds = 20;
static int babyBirthFoodDecrement = 10;
// bonus applied to all foods
// makes whole server a bit easier (or harder, if negative)
static int eatBonus = 0;
static int minActivePlayersForLanguages = 15;
// keep a running sequence number to challenge each connecting client
// to produce new login hashes, avoiding replay attacks.
static unsigned int nextSequenceNumber = 1;
static int requireClientPassword = 1;
static int requireTicketServerCheck = 1;
static char *clientPassword = NULL;
static char *ticketServerURL = NULL;
static char *reflectorURL = NULL;
// larger of dataVersionNumber.txt or serverCodeVersionNumber.txt
static int versionNumber = 1;
static double childSameRaceLikelihood = 0.9;
static int familySpan = 2;
// phrases that trigger baby and family naming
static SimpleVector<char*> nameGivingPhrases;
static SimpleVector<char*> familyNameGivingPhrases;
static SimpleVector<char*> cursingPhrases;
static SimpleVector<char*> youGivingPhrases;
static SimpleVector<char*> namedGivingPhrases;
static char *eveName = NULL;
// maps extended ascii codes to true/false for characters allowed in SAY
// messages
static char allowedSayCharMap[256];
static const char *allowedSayChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.-,'?! ";
static int killEmotionIndex = 2;
static double lastBabyPassedThresholdTime = 0;
static double eveWindowStart = 0;
// for incoming socket connections that are still in the login process
typedef struct FreshConnection {
Socket *sock;
SimpleVector<char> *sockBuffer;
unsigned int sequenceNumber;
char *sequenceNumberString;
WebRequest *ticketServerRequest;
char ticketServerAccepted;
char lifeTokenSpent;
double ticketServerRequestStartTime;
char error;
const char *errorCauseString;
double rejectedSendTime;
char shutdownMode;
// for tracking connections that have failed to LOGIN
// in a timely manner
double connectionStartTimeSeconds;
char *email;
int tutorialNumber;
CurseStatus curseStatus;
char *twinCode;
int twinCount;
} FreshConnection;
SimpleVector<FreshConnection> newConnections;
SimpleVector<FreshConnection> waitingForTwinConnections;
typedef struct LiveObject {
char *email;
int id;
// -1 if unknown
float fitnessScore;
// object ID used to visually represent this player
int displayID;
char *name;
char nameHasSuffix;
char *familyName;
char *lastSay;
CurseStatus curseStatus;
int curseTokenCount;
char curseTokenUpdate;
char isEve;
char isTutorial;
// used to track incremental tutorial map loading
TutorialLoadProgress tutorialLoad;
GridPos birthPos;
GridPos originalBirthPos;
int parentID;
// 0 for Eve
int parentChainLength;
SimpleVector<int> *lineage;
SimpleVector<char*> *ancestorEmails;
SimpleVector<char*> *ancestorRelNames;
// id of Eve that started this line
int lineageEveID;
// time that this life started (for computing age)
// not actual creation time (can be adjusted to tweak starting age,
// for example, in case of Eve who starts older).
double lifeStartTimeSeconds;
// time when this player actually died
double deathTimeSeconds;
// the wall clock time when this life started
// used for computing playtime, not age
double trueStartTimeSeconds;
double lastSayTimeSeconds;
// held by other player?
char heldByOther;
int heldByOtherID;
char everHeldByParent;
// player that's responsible for updates that happen to this
// player during current step
int responsiblePlayerID;
// start and dest for a move
// same if reached destination
int xs;
int ys;
int xd;
int yd;
// next player update should be flagged
// as a forced position change
char posForced;
char waitingForForceResponse;
int lastMoveSequenceNumber;
int pathLength;
GridPos *pathToDest;
char pathTruncated;
char firstMapSent;
int lastSentMapX;
int lastSentMapY;
double moveTotalSeconds;
double moveStartTime;
int facingOverride;
int actionAttempt;
GridPos actionTarget;
int holdingID;
// absolute time in seconds that what we're holding should decay
// or 0 if it never decays
timeSec_t holdingEtaDecay;
// where on map held object was picked up from
char heldOriginValid;
int heldOriginX;
int heldOriginY;
// track origin of held separate to use when placing a grave
int heldGraveOriginX;
int heldGraveOriginY;
int heldGravePlayerID;
// if held object was created by a transition on a target, what is the
// object ID of the target from the transition?
int heldTransitionSourceID;
int numContained;
int *containedIDs;
timeSec_t *containedEtaDecays;
// vector of sub-contained for each contained item
SimpleVector<int> *subContainedIDs;
SimpleVector<timeSec_t> *subContainedEtaDecays;
// if they've been killed and part of a weapon (bullet?) has hit them
// this will be included in their grave
int embeddedWeaponID;
timeSec_t embeddedWeaponEtaDecay;
// and what original weapon killed them?
int murderSourceID;
char holdingWound;
// who killed them?
int murderPerpID;
char *murderPerpEmail;
// or if they were killed by a non-person, what was it?
int deathSourceID;
// true if this character landed a mortal wound on another player
char everKilledAnyone;
// true in case of sudden infant death
char suicide;
Socket *sock;
SimpleVector<char> *sockBuffer;
// indicates that some messages were sent to this player this
// frame, and they need a FRAME terminator message
char gotPartOfThisFrame;
char isNew;
char firstMessageSent;
char inFlight;
char dying;
// wall clock time when they will be dead
double dyingETA;
// in cases where their held wound produces a forced emot
char emotFrozen;
double emotUnfreezeETA;
char connected;
char error;
const char *errorCauseString;
int customGraveID;
char *deathReason;
char deleteSent;
// wall clock time when we consider the delete good and sent
// and can close their connection
double deleteSentDoneETA;
char deathLogged;
char newMove;
// heat map that player carries around with them
// every time they stop moving, it is updated to compute
// their local temp
float heatMap[ HEAT_MAP_D * HEAT_MAP_D ];
// net heat of environment around player
// map is tracked in heat units (each object produces an
// integer amount of heat)
// this is in base heat units, range 0 to infinity
float envHeat;
// amount of heat currently in player's body, also in
// base heat units
float bodyHeat;
// used track current biome heat for biome-change shock effects
float biomeHeat;
float lastBiomeHeat;
// body heat normalized to [0,1], with targetHeat at 0.5
float heat;
// flags this player as needing to recieve a heat update
char heatUpdate;
// wall clock time of last time this player was sent
// a heat update
double lastHeatUpdate;
// true if heat map features player surrounded by walls
char isIndoors;
int foodStore;
double foodCapModifier;
double fever;
// wall clock time when we should decrement the food store
double foodDecrementETASeconds;
// should we send player a food status message
char foodUpdate;
// info about the last thing we ate, for FX food messages sent
// just to player
int lastAteID;
int lastAteFillMax;
// this is for PU messages sent to everyone
char justAte;
int justAteID;
// chain of non-repeating foods eaten
SimpleVector<int> yummyFoodChain;
// how many bonus from yummy food is stored
// these are used first before food is decremented
int yummyBonusStore;
ClothingSet clothing;
timeSec_t clothingEtaDecay[NUM_CLOTHING_PIECES];
SimpleVector<int> clothingContained[NUM_CLOTHING_PIECES];
SimpleVector<timeSec_t>
clothingContainedEtaDecays[NUM_CLOTHING_PIECES];
char needsUpdate;
char updateSent;
char updateGlobal;
// babies born to this player
SimpleVector<timeSec_t> *babyBirthTimes;
SimpleVector<int> *babyIDs;
// wall clock time after which they can have another baby
// starts at 0 (start of time epoch) for non-mothers, as
// they can have their first baby right away.
timeSec_t birthCoolDown;
timeSec_t lastRegionLookTime;
double playerCrossingCheckTime;
char monumentPosSet;
GridPos lastMonumentPos;
int lastMonumentID;
char monumentPosSent;
char holdingFlightObject;
char vogMode;
GridPos preVogPos;
GridPos preVogBirthPos;
int vogJumpIndex;
char postVogMode;
// list of positions owned by this player
SimpleVector<GridPos> ownedPositions;
// list of owned positions that this player has heard about
SimpleVector<GridPos> knownOwnedPositions;
} LiveObject;
SimpleVector<LiveObject> players;
SimpleVector<LiveObject> tutorialLoadingPlayers;
char doesEveLineExist( int inEveID ) {
for( int i=0; i<players.size(); i++ ) {
LiveObject *o = players.getElement( i );
if( ( ! o->error ) && o->lineageEveID == inEveID ) {
return true;
}
}
return false;
}
typedef struct DeadObject {
int id;
int displayID;
char *name;
SimpleVector<int> *lineage;
// id of Eve that started this line
int lineageEveID;
// time that this life started (for computing age)
// not actual creation time (can be adjusted to tweak starting age,
// for example, in case of Eve who starts older).
double lifeStartTimeSeconds;
// time this person died
double deathTimeSeconds;
} DeadObject;
static double lastPastPlayerFlushTime = 0;
SimpleVector<DeadObject> pastPlayers;
static void addPastPlayer( LiveObject *inPlayer ) {
DeadObject o;
o.id = inPlayer->id;
o.displayID = inPlayer->displayID;
o.name = NULL;
if( inPlayer->name != NULL ) {
o.name = stringDuplicate( inPlayer->name );
}
o.lineageEveID = inPlayer->lineageEveID;
o.lifeStartTimeSeconds = inPlayer->lifeStartTimeSeconds;
o.deathTimeSeconds = inPlayer->deathTimeSeconds;
o.lineage = new SimpleVector<int>();
for( int i=0; i< inPlayer->lineage->size(); i++ ) {
o.lineage->push_back( inPlayer->lineage->getElementDirect( i ) );
}
pastPlayers.push_back( o );
}
char isOwned( LiveObject *inPlayer, int inX, int inY ) {
for( int i=0; i<inPlayer->ownedPositions.size(); i++ ) {
GridPos *p = inPlayer->ownedPositions.getElement( i );
if( p->x == inX && p->y == inY ) {
return true;
}
}
return false;
}
char isOwned( LiveObject *inPlayer, GridPos inPos ) {
return isOwned( inPlayer, inPos.x, inPos.y );
}
char isKnownOwned( LiveObject *inPlayer, int inX, int inY ) {
for( int i=0; i<inPlayer->knownOwnedPositions.size(); i++ ) {
GridPos *p = inPlayer->knownOwnedPositions.getElement( i );
if( p->x == inX && p->y == inY ) {
return true;
}
}
return false;
}
char isKnownOwned( LiveObject *inPlayer, GridPos inPos ) {
return isKnownOwned( inPlayer, inPos.x, inPos.y );
}
SimpleVector<GridPos> recentlyRemovedOwnerPos;
void removeAllOwnership( LiveObject *inPlayer ) {
double startTime = Time::getCurrentTime();
int num = inPlayer->ownedPositions.size();
for( int i=0; i<inPlayer->ownedPositions.size(); i++ ) {
GridPos *p = inPlayer->ownedPositions.getElement( i );
recentlyRemovedOwnerPos.push_back( *p );
int oID = getMapObject( p->x, p->y );
if( oID <= 0 ) {
continue;
}
char noOtherOwners = true;
for( int j=0; j<players.size(); j++ ) {
LiveObject *otherPlayer = players.getElement( j );
if( otherPlayer != inPlayer ) {
if( isOwned( otherPlayer, *p ) ) {
noOtherOwners = false;
break;
}
}
}
if( noOtherOwners ) {
// last owner of p just died
// force end transition
SimpleVector<int> *deathMarkers = getAllPossibleDeathIDs();
for( int j=0; j<deathMarkers->size(); j++ ) {
int deathID = deathMarkers->getElementDirect( j );
TransRecord *t = getTrans( deathID, oID );
if( t != NULL ) {
setMapObject( p->x, p->y, t->newTarget );
break;
}
}
}
}
inPlayer->ownedPositions.deleteAll();
AppLog::infoF( "Removing all ownership (%d owned) for "
"player %d (%s) took %lf sec",
num, inPlayer->id, inPlayer->email,
Time::getCurrentTime() - startTime );
}
char *getOwnershipString( int inX, int inY ) {
SimpleVector<char> messageWorking;
for( int j=0; j<players.size(); j++ ) {
LiveObject *otherPlayer = players.getElement( j );
if( ! otherPlayer->error &&
isOwned( otherPlayer, inX, inY ) ) {
char *playerIDString =
autoSprintf( " %d", otherPlayer->id );
messageWorking.appendElementString(
playerIDString );
delete [] playerIDString;
}
}
char *message = messageWorking.getElementString();
return message;
}
char *getOwnershipString( GridPos inPos ) {
return getOwnershipString( inPos.x, inPos.y );
}
static char checkReadOnly() {
const char *testFileName = "testReadOnly.txt";
FILE *testFile = fopen( testFileName, "w" );
if( testFile != NULL ) {
fclose( testFile );
remove( testFileName );
return false;
}
return true;
}
// returns a person to their natural state
static void backToBasics( LiveObject *inPlayer ) {
LiveObject *p = inPlayer;
// do not heal dying people
if( ! p->holdingWound && p->holdingID > 0 ) {
p->holdingID = 0;
p->holdingEtaDecay = 0;
p->heldOriginValid = false;
p->heldTransitionSourceID = -1;
p->numContained = 0;
if( p->containedIDs != NULL ) {
delete [] p->containedIDs;
delete [] p->containedEtaDecays;
p->containedIDs = NULL;
p->containedEtaDecays = NULL;
}
if( p->subContainedIDs != NULL ) {
delete [] p->subContainedIDs;
delete [] p->subContainedEtaDecays;
p->subContainedIDs = NULL;
p->subContainedEtaDecays = NULL;
}
}
p->clothing = getEmptyClothingSet();
for( int c=0; c<NUM_CLOTHING_PIECES; c++ ) {
p->clothingEtaDecay[c] = 0;
p->clothingContained[c].deleteAll();
p->clothingContainedEtaDecays[c].deleteAll();
}
}
typedef struct GraveInfo {
GridPos pos;
int playerID;
// eve that started the line of this dead person
// used for tracking whether grave is part of player's family or not
int lineageEveID;
} GraveInfo;
typedef struct GraveMoveInfo {
GridPos posStart;
GridPos posEnd;
int swapDest;
} GraveMoveInfo;
// tracking spots on map that inflicted a mortal wound
// put them on timeout afterward so that they don't attack
// again immediately
typedef struct DeadlyMapSpot {
GridPos pos;
double timeOfAttack;
} DeadlyMapSpot;
static double deadlyMapSpotTimeoutSec = 10;
static SimpleVector<DeadlyMapSpot> deadlyMapSpots;
static char wasRecentlyDeadly( GridPos inPos ) {
double curTime = Time::getCurrentTime();
for( int i=0; i<deadlyMapSpots.size(); i++ ) {
DeadlyMapSpot *s = deadlyMapSpots.getElement( i );
if( curTime - s->timeOfAttack >= deadlyMapSpotTimeoutSec ) {
deadlyMapSpots.deleteElement( i );
i--;
}
else if( s->pos.x == inPos.x && s->pos.y == inPos.y ) {
// note that this is a lazy method that only walks through
// the whole list and checks for timeouts when
// inPos isn't found
return true;
}
}
return false;
}
static void addDeadlyMapSpot( GridPos inPos ) {
// don't check for duplicates
// we're only called to add a new deadly spot when the spot isn't
// currently on deadly cooldown anyway
DeadlyMapSpot s = { inPos, Time::getCurrentTime() };
deadlyMapSpots.push_back( s );
}
static LiveObject *getLiveObject( int inID ) {
for( int i=0; i<players.size(); i++ ) {
LiveObject *o = players.getElement( i );
if( o->id == inID ) {
return o;
}
}
return NULL;
}
char *getPlayerName( int inID ) {
LiveObject *o = getLiveObject( inID );
if( o != NULL ) {
return o->name;
}
return NULL;
}
static double pickBirthCooldownSeconds() {
// Kumaraswamy distribution
// PDF:
// k(x,a,b) = a * b * x**( a - 1 ) * (1-x**a)**(b-1)
// CDF:
// kCDF(x,a,b) = 1 - (1-x**a)**b
// Invers CDF:
// kCDFInv(y,a,b) = ( 1 - (1-y)**(1.0/b) )**(1.0/a)
// For b=1, PDF curve starts at 0 and curves upward, for all a > 2
// good values seem to be a=1.5, b=1
// actually, make it more bell-curve like, with a=2, b=3
double a = 2;
double b = 3;
// mean is around 2 minutes
// uniform
double u = randSource.getRandomDouble();
// feed into inverted CDF to sample a value from the distribution
double v = pow( 1 - pow( 1-u, (1/b) ), 1/a );
// v is in [0..1], the value range of Kumaraswamy
// put max at 5 minutes
return v * 5 * 60;
}
typedef struct FullMapContained{
int numContained;
int *containedIDs;
timeSec_t *containedEtaDecays;
SimpleVector<int> *subContainedIDs;
SimpleVector<timeSec_t> *subContainedEtaDecays;
} FullMapContained;
// including contained and sub contained in one call
FullMapContained getFullMapContained( int inX, int inY ) {
FullMapContained r;
r.containedIDs = getContained( inX, inY, &( r.numContained ) );
r.containedEtaDecays =
getContainedEtaDecay( inX, inY, &( r.numContained ) );
if( r.numContained == 0 ) {
r.subContainedIDs = NULL;
r.subContainedEtaDecays = NULL;
}
else {
r.subContainedIDs = new SimpleVector<int>[ r.numContained ];
r.subContainedEtaDecays = new SimpleVector<timeSec_t>[ r.numContained ];
}
for( int c=0; c< r.numContained; c++ ) {
if( r.containedIDs[c] < 0 ) {
int numSub;
int *subContainedIDs = getContained( inX, inY, &numSub,
c + 1 );
if( subContainedIDs != NULL ) {
r.subContainedIDs[c].appendArray( subContainedIDs, numSub );
delete [] subContainedIDs;
}
timeSec_t *subContainedEtaDecays =
getContainedEtaDecay( inX, inY, &numSub,
c + 1 );
if( subContainedEtaDecays != NULL ) {
r.subContainedEtaDecays[c].appendArray( subContainedEtaDecays,
numSub );
delete [] subContainedEtaDecays;
}
}
}
return r;
}
void freePlayerContainedArrays( LiveObject *inPlayer ) {