Oolite 1.91.0.7745-260117-205bce7
Loading...
Searching...
No Matches
Universe.m
Go to the documentation of this file.
1/*
2
3Universe.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25
26#import "Universe.h"
27#import "MyOpenGLView.h"
28#import "GameController.h"
29#import "ResourceManager.h"
30#import "AI.h"
31#import "GuiDisplayGen.h"
32#import "HeadUpDisplay.h"
33#import "OOSound.h"
34#import "OOColor.h"
35#import "OOCacheManager.h"
36#import "OOStringExpander.h"
37#import "OOStringParsing.h"
39#import "OOConstToString.h"
40#import "OOConstToJSString.h"
43#import "OOCPUInfo.h"
44#import "OOMaterial.h"
45#import "OOTexture.h"
46#import "OORoleSet.h"
47#import "OOShipGroup.h"
48#import "OODebugSupport.h"
49
50#import "Octree.h"
51#import "CollisionRegion.h"
53#import "OODebugSupport.h"
55
56#import "OOCharacter.h"
57#import "OOShipRegistry.h"
58#import "OOProbabilitySet.h"
59#import "OOEquipmentType.h"
61
62#import "PlayerEntity.h"
66#import "StationEntity.h"
67#import "DockEntity.h"
68#import "SkyEntity.h"
69#import "DustEntity.h"
70#import "OOPlanetEntity.h"
72#import "OOWaypointEntity.h"
73#import "OOSunEntity.h"
74#import "WormholeEntity.h"
76#import "ShipEntityAI.h"
77#import "ProxyPlayerEntity.h"
83#import "OOMusicController.h"
85#import "OODebugFlags.h"
86#import "OODebugStandards.h"
87#import "OOLoggingExtended.h"
89#import "OOJoystickManager.h"
90#import "OOScriptTimer.h"
91#import "OOJSScript.h"
94#import "OOOpenGL.h"
95#import "OOShaderProgram.h"
96
97
98#if OO_LOCALIZATION_TOOLS
100#endif
101
102enum
103{
107};
108
109#define DEMO2_VANISHING_DISTANCE 650.0
110#define DEMO2_FLY_IN_STAGE_TIME 0.4
111
112
113#define MAX_NUMBER_OF_ENTITIES 200
114#define STANDARD_STATION_ROLL 0.4
115// currently twice scanner radius
116#define LANE_WIDTH 51200.0
117
118static NSString * const kOOLogUniversePopulateError = @"universe.populate.error";
119static NSString * const kOOLogUniversePopulateWitchspace = @"universe.populate.witchspace";
120static NSString * const kOOLogEntityVerificationError = @"entity.linkedList.verify.error";
121static NSString * const kOOLogEntityVerificationRebuild = @"entity.linkedList.verify.rebuild";
122
123
124
125const GLfloat framebufferQuadVertices[] = {
126 // positions // texture coords
127 1.0f, 1.0f, 1.0f, 1.0f, // top right
128 1.0f, -1.0f, 1.0f, 0.0f, // bottom right
129 -1.0f, -1.0f, 0.0f, 0.0f, // bottom left
130 -1.0f, 1.0f, 0.0f, 1.0f // top left
131};
132const GLuint framebufferQuadIndices[] = {
133 0, 1, 3, // first triangle
134 1, 2, 3 // second triangle
135};
136
137
139
142
143
145OOINLINE BOOL EntityInRange(HPVector p1, Entity *e2, float range);
146
147static OOComparisonResult compareName(id dict1, id dict2, void * context);
148static OOComparisonResult comparePrice(id dict1, id dict2, void * context);
149
150/* TODO: route calculation is really slow - find a way to safely enable this */
151#undef CACHE_ROUTE_FROM_SYSTEM_RESULTS
152
153@interface RouteElement: NSObject
154{
155@private
159}
160
161+ (instancetype) elementWithLocation:(OOSystemID) location parent:(OOSystemID)parent cost:(double) cost distance:(double) distance time:(double) time jumps:(int) jumps;
162- (OOSystemID) parent;
163- (OOSystemID) location;
164- (double) cost;
165- (double) distance;
166- (double) time;
167- (int) jumps;
168
169@end
170
171@implementation RouteElement
172
173+ (instancetype) elementWithLocation:(OOSystemID) location parent:(OOSystemID) parent cost:(double) cost distance:(double) distance time:(double) time jumps:(int) jumps
174{
175 RouteElement *r = [[RouteElement alloc] init];
176
177 r->_location = location;
178 r->_parent = parent;
179 r->_cost = cost;
180 r->_distance = distance;
181 r->_time = time;
182 r->_jumps = jumps;
183
184 return [r autorelease];
185}
186
187- (OOSystemID) parent { return _parent; }
189- (double) cost { return _cost; }
190- (double) distance { return _distance; }
191- (double) time { return _time; }
192- (int) jumps { return _jumps; }
193
194@end
195
196
197@interface Universe (OOPrivate)
198
199- (void) initTargetFramebufferWithViewSize:(NSSize)viewSize;
200- (void) deleteOpenGLObjects;
201- (void) resizeTargetFramebufferWithViewSize:(NSSize)viewSize;
204
205- (BOOL) doRemoveEntity:(Entity *)entity;
206- (void) setUpCargoPods;
207- (void) setUpInitialUniverse;
208- (HPVector) fractionalPositionFrom:(HPVector)point0 to:(HPVector)point1 withFraction:(double)routeFraction;
209
211
212- (NSString *)chooseStringForKey:(NSString *)key inDictionary:(NSDictionary *)dictionary;
213
214#if OO_LOCALIZATION_TOOLS
215#if DEBUG_GRAPHVIZ
216- (void) dumpDebugGraphViz;
217- (void) dumpSystemDescriptionGraphViz;
218#endif
219- (void) addNumericRefsInString:(NSString *)string toGraphViz:(NSMutableString *)graphViz fromNode:(NSString *)fromNode nodeCount:(NSUInteger)nodeCount;
220
225- (void) runLocalizationTools;
226#endif
227
228#if NEW_PLANETS
229- (void) prunePreloadingPlanetMaterials;
230#endif
231
232// Set shader effects level without logging or triggering a reset -- should only be used directly during startup.
233- (void) setShaderEffectsLevelDirectly:(OOShaderSetting)value;
234
235- (void) setFirstBeacon:(Entity <OOBeaconEntity> *)beacon;
236- (void) setLastBeacon:(Entity <OOBeaconEntity> *)beacon;
237
238- (void) verifyDescriptions;
239- (void) loadDescriptions;
240- (void) loadScenarios;
241
244- (Vector) randomPlaceWithinScannerFrom:(Vector)pos alongRoute:(Vector)route withOffset:(double)offset;
245
246- (void) setDetailLevelDirectly:(OOGraphicsDetail)value;
247
248- (NSDictionary *)demoShipData;
250
251@end
252
253
254@implementation Universe
255
256// Flags needed when JS reset fails.
257static int JSResetFlags = 0;
258
259
260// track the position and status of the lights
261static BOOL object_light_on = NO;
262static BOOL demo_light_on = NO;
263static GLfloat sun_off[4] = {0.0, 0.0, 0.0, 1.0};
264static GLfloat demo_light_position[4] = { DEMO_LIGHT_POSITION, 1.0 };
265
266#define DOCKED_AMBIENT_LEVEL 0.2f // Was 0.05, 'temporarily' set to 0.2.
267#define DOCKED_ILLUM_LEVEL 0.7f
270static GLfloat docked_light_specular[4] = { DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL * 0.75f, (GLfloat) 1.0f }; // yellow-white
271
272// Weight of sun in ambient light calculation. 1.0 means only sun's diffuse is used for ambient, 0.0 means only sky colour is used.
273// TODO: considering the size of the sun and the number of background stars might be worthwhile. -- Ahruman 20080322
274#define SUN_AMBIENT_INFLUENCE 0.75
275// How dark the default ambient level of 1.0 will be
276#define SKY_AMBIENT_ADJUSTMENT 0.0625
277
278- (BOOL) bloom
279{
280 return _bloom && [self detailLevel] >= DETAIL_LEVEL_EXTRAS;
281}
282
283- (void) setBloom: (BOOL)newBloom
284{
285 _bloom = !!newBloom;
286}
287
289{
290 return _currentPostFX;
291}
292
293- (void) setCurrentPostFX: (int) newCurrentPostFX
294{
295 if (newCurrentPostFX < 1 || newCurrentPostFX > OO_POSTFX_ENDOFLIST - 1)
296 {
297 newCurrentPostFX = OO_POSTFX_NONE;
298 }
299
300 if (OO_POSTFX_NONE <= newCurrentPostFX && newCurrentPostFX <= OO_POSTFX_COLORBLINDNESS_TRITAN)
301 {
302 _colorblindMode = newCurrentPostFX;
303 }
304
305 _currentPostFX = newCurrentPostFX;
306}
307
308
309- (void) terminatePostFX:(int)postFX
310{
311 if ([self currentPostFX] == postFX)
312 {
313 [self setCurrentPostFX:[self colorblindMode]];
314 }
315}
316
317- (int) nextColorblindMode:(int) index
318{
320 index = OO_POSTFX_NONE;
321
322 return index;
323}
324
325- (int) prevColorblindMode:(int) index
326{
327 if (--index < OO_POSTFX_NONE)
329
330 return index;
331}
332
334{
335 return _colorblindMode;
336}
337
338- (void) initTargetFramebufferWithViewSize:(NSSize)viewSize
339{
340 // liberate us from the 0.0 to 1.0 rgb range!
341 OOGL(glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE));
342 OOGL(glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE));
343 OOGL(glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE));
344
345 // have to do this because on my machine the default framebuffer is not zero
346 OOGL(glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &defaultDrawFBO));
347
348 GLint previousProgramID;
349 OOGL(glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgramID));
350 GLint previousTextureID;
351 OOGL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previousTextureID));
352 GLint previousVAO;
353 OOGL(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &previousVAO));
354 GLint previousArrayBuffer;
355 OOGL(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &previousArrayBuffer));
356 GLint previousElementBuffer;
357 OOGL(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &previousElementBuffer));
358
359 // create MSAA framebuffer and attach MSAA texture and depth buffer to framebuffer
360 OOGL(glGenFramebuffers(1, &msaaFramebufferID));
361 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebufferID));
362
363 // creating MSAA texture that should be rendered into
364 OOGL(glGenTextures(1, &msaaTextureID));
365 OOGL(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTextureID));
366 OOGL(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, GL_TRUE));
367 OOGL(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0));
368 OOGL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, msaaTextureID, 0));
369
370 // create necessary MSAA depth render buffer
371 OOGL(glGenRenderbuffers(1, &msaaDepthBufferID));
372 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, msaaDepthBufferID));
373 OOGL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT32F, (GLsizei)viewSize.width, (GLsizei)viewSize.height));
374 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
375 OOGL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaaDepthBufferID));
376
377 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
378 {
379 OOLogERR(@"initTargetFramebufferWithViewSize.result", @"%@", @"***** Error: Multisample framebuffer not complete");
380 }
381
382 // create framebuffer and attach texture and depth buffer to framebuffer
383 OOGL(glGenFramebuffers(1, &targetFramebufferID));
384 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, targetFramebufferID));
385
386 // creating texture that should be rendered into
387 OOGL(glGenTextures(1, &targetTextureID));
388 OOGL(glBindTexture(GL_TEXTURE_2D, targetTextureID));
389 OOGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, 0, GL_RGBA, GL_FLOAT, NULL));
390 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
391 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
392 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
393 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
394 OOGL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTextureID, 0));
395
396 // create necessary depth render buffer
397 OOGL(glGenRenderbuffers(1, &targetDepthBufferID));
398 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, targetDepthBufferID));
399 OOGL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, (GLsizei)viewSize.width, (GLsizei)viewSize.height));
400 OOGL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, targetDepthBufferID));
401
402 GLenum attachment[1] = { GL_COLOR_ATTACHMENT0 };
403 OOGL(glDrawBuffers(1, attachment));
404
405 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
406 {
407 OOLogERR(@"initTargetFramebufferWithViewSize.result", @"%@", @"***** Error: Framebuffer not complete");
408 }
409
410 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
411
412 targetFramebufferSize = viewSize;
413
414 // passthrough buffer
415 // This is a framebuffer whose sole purpose is to pass on the texture rendered from the game to the blur and the final bloom
416 // shaders. We need it in order to be able to use the OpenGL 3.3 layout (location = x) out vec4 outVector; construct, which allows
417 // us to perform multiple render target operations needed for bloom. The alternative would be to not use this and change all our
418 // shaders to be OpenGL 3.3 compatible, but given how Oolite synthesizes them and the work needed to port them over, well yeah no,
419 // not doing it at this time - Nikos 20220814.
420 OOGL(glGenFramebuffers(1, &passthroughFramebufferID));
421 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, passthroughFramebufferID));
422
423 // creating textures that should be rendered into
424 OOGL(glGenTextures(2, passthroughTextureID));
425 for (unsigned int i = 0; i < 2; i++)
426 {
427 OOGL(glBindTexture(GL_TEXTURE_2D, passthroughTextureID[i]));
428 OOGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, 0, GL_RGBA, GL_FLOAT, NULL));
429 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
430 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
431 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
432 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
433 OOGL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, passthroughTextureID[i], 0));
434 }
435
436 GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
437 OOGL(glDrawBuffers(2, attachments));
438
439 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
440 {
441 OOLogERR(@"initTargetFramebufferWithViewSize.result", @"%@", @"***** Error: Passthrough framebuffer not complete");
442 }
443 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
444
445 // ping-pong-framebuffer for blurring
446 OOGL(glGenFramebuffers(2, pingpongFBO));
447 OOGL(glGenTextures(2, pingpongColorbuffers));
448 for (unsigned int i = 0; i < 2; i++)
449 {
450 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]));
451 OOGL(glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[i]));
452 OOGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, 0, GL_RGBA, GL_FLOAT, NULL));
453 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
454 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
455 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); // we clamp to the edge as the blur filter would otherwise sample repeated texture values!
456 OOGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
457 OOGL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongColorbuffers[i], 0));
458 // check if framebuffers are complete (no need for depth buffer)
459 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
460 {
461 OOLogERR(@"initTargetFramebufferWithViewSize.result", @"%@", @"***** Error: Pingpong framebuffers not complete");
462 }
463 }
464 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
465
466 _bloom = [self detailLevel] >= DETAIL_LEVEL_EXTRAS;
467 _currentPostFX = _colorblindMode = OO_POSTFX_NONE;
468
469 /* TODO: in OOEnvironmentCubeMap.m call these bind functions not with 0 but with "previousXxxID"s:
470 - OOGL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0));
471 - OOGL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
472 - OOGL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
473 */
474
475 // shader for drawing a textured quad on the passthrough framebuffer and preparing it for bloom using MRT
476 if (![[OOOpenGLExtensionManager sharedManager] shadersForceDisabled])
477 {
478 textureProgram = [[OOShaderProgram shaderProgramWithVertexShaderName:@"oolite-texture.vertex"
479 fragmentShaderName:@"oolite-texture.fragment"
480 prefix:@"#version 330\n"
481 attributeBindings:[NSDictionary dictionary]] retain];
482 // shader for blurring the over-threshold brightness image generated from the previous step using Gaussian filter
483 blurProgram = [[OOShaderProgram shaderProgramWithVertexShaderName:@"oolite-blur.vertex"
484 fragmentShaderName:@"oolite-blur.fragment"
485 prefix:@"#version 330\n"
486 attributeBindings:[NSDictionary dictionary]] retain];
487 // shader for applying bloom and any necessary post-proc fx, tonemapping and gamma correction
488 finalProgram = [[OOShaderProgram shaderProgramWithVertexShaderName:@"oolite-final.vertex"
489#if OOLITE_WINDOWS
490 fragmentShaderName:[[UNIVERSE gameView] hdrOutput] ? @"oolite-final-hdr.fragment" : @"oolite-final.fragment"
491#else
492 fragmentShaderName:@"oolite-final.fragment"
493#endif
494 prefix:@"#version 330\n"
495 attributeBindings:[NSDictionary dictionary]] retain];
496 }
497
498 OOGL(glGenVertexArrays(1, &quadTextureVAO));
499 OOGL(glGenBuffers(1, &quadTextureVBO));
500 OOGL(glGenBuffers(1, &quadTextureEBO));
501
502 OOGL(glBindVertexArray(quadTextureVAO));
503
504 OOGL(glBindBuffer(GL_ARRAY_BUFFER, quadTextureVBO));
505 OOGL(glBufferData(GL_ARRAY_BUFFER, sizeof(framebufferQuadVertices), framebufferQuadVertices, GL_STATIC_DRAW));
506
507 OOGL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadTextureEBO));
508 OOGL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(framebufferQuadIndices), framebufferQuadIndices, GL_STATIC_DRAW));
509
510 OOGL(glEnableVertexAttribArray(0));
511 // position attribute
512 OOGL(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0));
513 OOGL(glEnableVertexAttribArray(1));
514 // texture coord attribute
515 OOGL(glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))));
516
517
518 // restoring previous bindings
519 OOGL(glUseProgram(previousProgramID));
520 OOGL(glBindTexture(GL_TEXTURE_2D, previousTextureID));
521 OOGL(glBindVertexArray(previousVAO));
522 OOGL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, previousElementBuffer));
523 OOGL(glBindBuffer(GL_ARRAY_BUFFER, previousArrayBuffer));
524
525}
526
527
529{
530 OOGL(glDeleteTextures(1, &msaaTextureID));
531 OOGL(glDeleteTextures(1, &targetTextureID));
532 OOGL(glDeleteTextures(2, passthroughTextureID));
533 OOGL(glDeleteTextures(2, pingpongColorbuffers));
534 OOGL(glDeleteRenderbuffers(1, &msaaDepthBufferID));
535 OOGL(glDeleteRenderbuffers(1, &targetDepthBufferID));
536 OOGL(glDeleteFramebuffers(1, &msaaFramebufferID));
537 OOGL(glDeleteFramebuffers(1, &targetFramebufferID));
538 OOGL(glDeleteFramebuffers(2, pingpongFBO));
539 OOGL(glDeleteFramebuffers(1, &passthroughFramebufferID));
540 OOGL(glDeleteVertexArrays(1, &quadTextureVAO));
541 OOGL(glDeleteBuffers(1, &quadTextureVBO));
542 OOGL(glDeleteBuffers(1, &quadTextureEBO));
543 [textureProgram release];
544 [blurProgram release];
545 [finalProgram release];
546}
547
548
549- (void) resizeTargetFramebufferWithViewSize:(NSSize)viewSize
550{
551 int i;
552 // resize MSAA color attachment
553 OOGL(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTextureID));
554 OOGL(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, GL_TRUE));
555 OOGL(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0));
556
557 // resize MSAA depth attachment
558 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, msaaDepthBufferID));
559 OOGL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT32F, (GLsizei)viewSize.width, (GLsizei)viewSize.height));
560 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
561
562 // resize color attachments
563 OOGL(glBindTexture(GL_TEXTURE_2D, targetTextureID));
564 OOGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, 0, GL_RGBA, GL_FLOAT, NULL));
565 OOGL(glBindTexture(GL_TEXTURE_2D, 0));
566
567 for (i = 0; i < 2; i++)
568 {
569 OOGL(glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[i]));
570 OOGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, 0, GL_RGBA, GL_FLOAT, NULL));
571 OOGL(glBindTexture(GL_TEXTURE_2D, 0));
572 }
573
574 for (i = 0; i < 2; i++)
575 {
576 OOGL(glBindTexture(GL_TEXTURE_2D, passthroughTextureID[i]));
577 OOGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, (GLsizei)viewSize.width, (GLsizei)viewSize.height, 0, GL_RGBA, GL_FLOAT, NULL));
578 OOGL(glBindTexture(GL_TEXTURE_2D, 0));
579 }
580
581 // resize depth attachment
582 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, targetDepthBufferID));
583 OOGL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, (GLsizei)viewSize.width, (GLsizei)viewSize.height));
584 OOGL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
585
586 targetFramebufferSize.width = viewSize.width;
587 targetFramebufferSize.height = viewSize.height;
588}
589
590
592{
593 // save previous bindings state
594 GLint previousFBO;
595 OOGL(glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousFBO));
596 GLint previousProgramID;
597 OOGL(glGetIntegerv(GL_CURRENT_PROGRAM, &previousProgramID));
598 GLint previousTextureID;
599 OOGL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previousTextureID));
600 GLint previousVAO;
601 OOGL(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &previousVAO));
602 GLint previousActiveTexture;
603 OOGL(glGetIntegerv(GL_ACTIVE_TEXTURE, &previousActiveTexture));
604
605 OOGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
606 // fixes transparency issue for some reason
607 OOGL(glDisable(GL_BLEND));
608
609 GLhandleARB program = [textureProgram program];
610 GLhandleARB blur = [blurProgram program];
611 GLhandleARB final = [finalProgram program];
612 NSSize viewSize = [gameView viewSize];
613 float fboResolution[2] = {viewSize.width, viewSize.height};
614
615 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, passthroughFramebufferID));
616 OOGL(glClear(GL_COLOR_BUFFER_BIT));
617
618 OOGL(glUseProgram(program));
619 OOGL(glBindTexture(GL_TEXTURE_2D, targetTextureID));
620 OOGL(glUniform1i(glGetUniformLocation(program, "image"), 0));
621
622
623 OOGL(glBindVertexArray(quadTextureVAO));
624 OOGL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0));
625 OOGL(glBindVertexArray(0));
626
627 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
628
629
630 BOOL horizontal = YES, firstIteration = YES;
631 unsigned int amount = [self bloom] ? 10 : 0; // if not blooming, why bother with the heavy calculations?
632 OOGL(glUseProgram(blur));
633 for (unsigned int i = 0; i < amount; i++)
634 {
635 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]));
636 OOGL(glUniform1i(glGetUniformLocation(blur, "horizontal"), horizontal));
637 OOGL(glActiveTexture(GL_TEXTURE0));
638 // bind texture of other framebuffer (or scene if first iteration)
639 OOGL(glBindTexture(GL_TEXTURE_2D, firstIteration ? passthroughTextureID[1] : pingpongColorbuffers[!horizontal]));
640 OOGL(glUniform1i(glGetUniformLocation([blurProgram program], "imageIn"), 0));
641 OOGL(glBindVertexArray(quadTextureVAO));
642 OOGL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0));
643 OOGL(glBindVertexArray(0));
644 horizontal = !horizontal;
645 firstIteration = NO;
646 }
647 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
648
649
650 OOGL(glUseProgram(final));
651
652 OOGL(glActiveTexture(GL_TEXTURE0));
653 OOGL(glBindTexture(GL_TEXTURE_2D, passthroughTextureID[0]));
654 OOGL(glUniform1i(glGetUniformLocation(final, "scene"), 0));
655 OOGL(glUniform1i(glGetUniformLocation(final, "bloom"), [self bloom]));
656 OOGL(glUniform1f(glGetUniformLocation(final, "uTime"), [self getTime]));
657 OOGL(glUniform2fv(glGetUniformLocation(final, "uResolution"), 1, fboResolution));
658 OOGL(glUniform1i(glGetUniformLocation(final, "uPostFX"), [self currentPostFX]));
659#if OOLITE_WINDOWS
660 if([gameView hdrOutput])
661 {
662 OOGL(glUniform1f(glGetUniformLocation(final, "uMaxBrightness"), [gameView hdrMaxBrightness]));
663 OOGL(glUniform1f(glGetUniformLocation(final, "uPaperWhiteBrightness"), [gameView hdrPaperWhiteBrightness]));
664 OOGL(glUniform1i(glGetUniformLocation(final, "uHDRToneMapper"), [gameView hdrToneMapper]));
665 }
666#endif
667 OOGL(glUniform1i(glGetUniformLocation(final, "uSDRToneMapper"), [gameView sdrToneMapper]));
668
669 OOGL(glActiveTexture(GL_TEXTURE1));
670 OOGL(glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[!horizontal]));
671 OOGL(glUniform1i(glGetUniformLocation(final, "bloomBlur"), 1));
672 OOGL(glUniform1f(glGetUniformLocation(final, "uSaturation"), [gameView colorSaturation]));
673
674 OOGL(glBindVertexArray(quadTextureVAO));
675 OOGL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0));
676
677 // restore GL_TEXTURE1 to 0, just in case we are returning from a
678 // DETAIL_LEVEL_NORMAL to DETAIL_LEVEL_SHADERS
679 OOGL(glBindTexture(GL_TEXTURE_2D, 0));
680
681 // restore previous bindings
682 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, previousFBO));
683 OOGL(glActiveTexture(previousActiveTexture));
684 OOGL(glBindTexture(GL_TEXTURE_2D, previousTextureID));
685 OOGL(glUseProgram(previousProgramID));
686 OOGL(glBindVertexArray(previousVAO));
687 OOGL(glEnable(GL_BLEND));
688}
689
690- (id) initWithGameView:(MyOpenGLView *)inGameView
691{
692 if (gSharedUniverse != nil)
693 {
694 [self release];
695 [NSException raise:NSInternalInconsistencyException format:@"%s: expected only one Universe to exist at a time.", __PRETTY_FUNCTION__];
696 }
697
698 OO_DEBUG_PROGRESS(@"%@", @"Universe initWithGameView:");
699
700 self = [super init];
701 if (self == nil) return nil;
702
703 _doingStartUp = YES;
704
705 OOInitReallyRandom([NSDate timeIntervalSinceReferenceDate] * 1e9);
706
707 NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
708
709 // prefs value no longer used - per save game but startup needs to
710 // be non-strict
711 useAddOns = [[NSString alloc] initWithString:SCENARIO_OXP_DEFINITION_ALL];
712
713 [self setGameView:inGameView];
714 gSharedUniverse = self;
715
716 allPlanets = [[NSMutableArray alloc] init];
717 allStations = [[NSMutableSet alloc] init];
718
721
722 // init OpenGL extension manager (must be done before any other threads might use it)
724 [self setDetailLevelDirectly:[prefs oo_intForKey:@"detailLevel"
726
728
730
731 // Preload cache
733
734#if OOLITE_SPEECH_SYNTH
735 OOLog(@"speech.synthesis", @"Spoken messages are %@.", ([prefs oo_boolForKey:@"speech_on" defaultValue:NO] ? @"on" :@"off"));
736#endif
737
738 // init the Resource Manager
739 [ResourceManager setUseAddOns:useAddOns]; // also logs the paths if changed
740
741 // Set up the internal game strings
742 [self loadDescriptions];
743 // DESC expansion is now possible!
744
745 // load starting saves
746 [self loadScenarios];
747
748 autoSave = [prefs oo_boolForKey:@"autosave" defaultValue:NO];
749 wireframeGraphics = [prefs oo_boolForKey:@"wireframe-graphics" defaultValue:NO];
750 doProcedurallyTexturedPlanets = [prefs oo_boolForKey:@"procedurally-textured-planets" defaultValue:YES];
751 [inGameView setGammaValue:[prefs oo_floatForKey:@"gamma-value" defaultValue:1.0f]];
752 [inGameView setMsaa:[prefs oo_boolForKey:@"anti-aliasing" defaultValue:NO]];
753 OOLog(@"MSAA.setup", @"Multisample anti-aliasing %@requested.", [inGameView msaa] ? @"" : @"not ");
754 [inGameView setFov:OOClamp_0_max_f([prefs oo_floatForKey:@"fov-value" defaultValue:57.2f], MAX_FOV_DEG) fromFraction:NO];
755 if ([inGameView fov:NO] < MIN_FOV_DEG) [inGameView setFov:MIN_FOV_DEG fromFraction:NO];
756
757 [self setECMVisualFXEnabled:[prefs oo_boolForKey:@"ecm-visual-fx" defaultValue:YES]];
758
759 // Set up speech synthesizer.
760#if OOLITE_SPEECH_SYNTH
761#if OOLITE_MAC_OS_X
762 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
763 ^{
764 /*
765 NSSpeechSynthesizer can take over a second on an SSD and several
766 seconds on an HDD for a cold start, and a third of a second upward
767 for a warm start. There are no particular thread safety consider-
768 ations documented for NSSpeechSynthesizer, so I'm assuming the
769 default one-thread-at-a-time access rule applies.
770 -- Ahruman 2012-09-13
771 */
772 OOLog(@"speech.setup.begin", @"Starting to set up speech synthesizer.");
773 NSSpeechSynthesizer *synth = [[NSSpeechSynthesizer alloc] init];
774 OOLog(@"speech.setup.end", @"Finished setting up speech synthesizer.");
775 speechSynthesizer = synth;
776 });
777#elif OOLITE_ESPEAK
778 int volume = [OOSound masterVolume] * 100;
779 if (!SDL_getenv("ESPEAK_DATA_PATH"))
780 {
781 espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 100, [[ResourceManager builtInPath] UTF8String], 0);
782 }
783 else
784 {
785 espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 100, NULL, 0);
786 }
787 espeak_SetParameter(espeakPUNCTUATION, espeakPUNCT_NONE, 0);
788 espeak_SetParameter(espeakVOLUME, volume, 0);
789 espeak_voices = espeak_ListVoices(NULL);
790 for (espeak_voice_count = 0;
791 espeak_voices[espeak_voice_count];
792 ++espeak_voice_count)
793 /**/;
794#endif
795#endif
796
797 [[GameController sharedController] logProgress:DESC(@"loading-ships")];
798 // Load ship data
799
801
802 entities = [[NSMutableArray arrayWithCapacity:MAX_NUMBER_OF_ENTITIES] retain];
803
804 [[GameController sharedController] logProgress:OOExpandKeyRandomized(@"loading-miscellany")];
805
806 // this MUST have the default no. of rows else the GUI_ROW macros in PlayerEntity.h need modification
807 gui = [[GuiDisplayGen alloc] init]; // alloc retains
808 comm_log_gui = [[GuiDisplayGen alloc] init]; // alloc retains
809
810 missiontext = [[ResourceManager dictionaryFromFilesNamed:@"missiontext.plist" inFolder:@"Config" andMerge:YES] retain];
811
812 waypoints = [[NSMutableDictionary alloc] init];
813
814 [self setUpSettings];
815
816 // can't do this here as it might lock an OXZ open
817 // [self preloadSounds]; // Must be after setUpSettings.
818
819 // Preload particle effect textures:
822
823
824 // set up cargopod templates
825 [self setUpCargoPods];
826
829 [self addEntity:player];
830
831 [player setStatus:STATUS_START_GAME];
833
835
836 universeRegion = [[CollisionRegion alloc] initAsUniverse];
837 entitiesDeadThisUpdate = [[NSMutableSet alloc] init];
839
840 [[GameController sharedController] logProgress:DESC(@"initializing-debug-support")];
842
843 [[GameController sharedController] logProgress:DESC(@"running-scripts")];
845
846 [[GameController sharedController] logProgress:DESC(@"populating-space")];
847 [self populateNormalSpace];
848
849 [[GameController sharedController] logProgress:OOExpandKeyRandomized(@"loading-miscellany")];
850
851#if OO_LOCALIZATION_TOOLS
853#if DEBUG_GRAPHVIZ
854 [self dumpDebugGraphViz];
855#endif
856#endif
857
859 _doingStartUp = NO;
860
861 return self;
862}
863
864
865- (void) dealloc
866{
868
869 [currentMessage release];
870
871 [gui release];
872 [message_gui release];
873 [comm_log_gui release];
874
875 [entities release];
876
877 [commodities release];
878
879 [_descriptions release];
880 [characters release];
881 [customSounds release];
882 [globalSettings release];
883 [systemManager release];
884 [missiontext release];
885 [equipmentData release];
886 [equipmentDataOutfitting release];
887 [demo_ships release];
888 [autoAIMap release];
889 [screenBackgrounds release];
890 [gameView release];
891 [populatorSettings release];
892 [system_repopulator release];
893 [allPlanets release];
894 [allStations release];
895 [explosionSettings release];
896
897 [activeWormholes release];
898 [characterPool release];
899 [universeRegion release];
900 [cargoPods release];
901
905
906 unsigned i;
907 for (i = 0; i < 256; i++) [system_names[i] release];
908
909 [entitiesDeadThisUpdate release];
910
912
913#if OOLITE_SPEECH_SYNTH
914 [speechArray release];
915#if OOLITE_MAC_OS_X
916 [speechSynthesizer release];
917#elif OOLITE_ESPEAK
918 espeak_Cancel();
919#endif
920#endif
921 [conditionScripts release];
922
923 [self deleteOpenGLObjects];
924
925 [super dealloc];
926}
927
928
929- (NSUInteger) sessionID
930{
931 return _sessionID;
932}
933
934
936{
937 return _doingStartUp;
938}
939
940
945
946
947- (void) setDoProcedurallyTexturedPlanets:(BOOL) value
948{
949 doProcedurallyTexturedPlanets = !!value; // ensure yes or no
950 [[NSUserDefaults standardUserDefaults] setBool:doProcedurallyTexturedPlanets forKey:@"procedurally-textured-planets"];
951}
952
953
954- (NSString *) useAddOns
955{
956 return useAddOns;
957}
958
959
960- (BOOL) setUseAddOns:(NSString *) newUse fromSaveGame:(BOOL) saveGame
961{
962 return [self setUseAddOns:newUse fromSaveGame:saveGame forceReinit:NO];
963}
964
965
966- (BOOL) setUseAddOns:(NSString *) newUse fromSaveGame:(BOOL) saveGame forceReinit:(BOOL)force
967{
968 if (!force && [newUse isEqualToString:useAddOns])
969 {
970 return YES;
971 }
973 useAddOns = [newUse retain];
974
975 return [self reinitAndShowDemo:!saveGame];
976}
977
978
979
980- (NSUInteger) entityCount
981{
982 return [entities count];
983}
984
985
986#ifndef NDEBUG
988{
989 int i;
990 int show_count = n_entities;
991
992 if (!OOLogWillDisplayMessagesInClass(@"universe.objectDump")) return;
993
994 OOLog(@"universe.objectDump", @"DEBUG: Entity Dump - [entities count] = %llu,\tn_entities = %u", [entities count], n_entities);
995
996 OOLogIndent();
997 for (i = 0; i < show_count; i++)
998 {
999 OOLog(@"universe.objectDump", @"Ent:%4u %@", i, [sortedEntities[i] descriptionForObjDump]);
1000 }
1001 OOLogOutdent();
1002
1003 if ([entities count] != n_entities)
1004 {
1005 OOLog(@"universe.objectDump", @"entities = %@", [entities description]);
1006 }
1007}
1008
1009
1010- (NSArray *) entityList
1011{
1012 return [NSArray arrayWithArray:entities];
1013}
1014#endif
1015
1016
1018{
1019 // deal with the machine going to sleep, or player pressing 'p'.
1020 PlayerEntity *player = PLAYER;
1021
1022 [self setPauseMessageVisible:NO];
1023 NSString *pauseKey = [PLAYER keyBindingDescription2:@"key_pausebutton"];
1024
1025 if ([player status] == STATUS_DOCKED)
1026 {
1027 if ([gui setForegroundTextureKey:@"paused_docked_overlay"])
1028 {
1029 [gui drawGUI:1.0 drawCursor:NO];
1030 }
1031 else
1032 {
1033 [self setPauseMessageVisible:YES];
1034 [self addMessage:OOExpandKey(@"game-paused-docked", pauseKey) forCount:1.0];
1035 }
1036 }
1037 else
1038 {
1039 if ([player guiScreen] != GUI_SCREEN_MAIN && [gui setForegroundTextureKey:@"paused_overlay"])
1040 {
1041 [gui drawGUI:1.0 drawCursor:NO];
1042 }
1043 else
1044 {
1045 [self setPauseMessageVisible:YES];
1046 [self addMessage:OOExpandKey(@"game-paused", pauseKey) forCount:1.0];
1047 }
1048 }
1049
1050 [[self gameController] setGamePaused:YES];
1051}
1052
1053
1054- (void) carryPlayerOn:(StationEntity*)carrier inWormhole:(WormholeEntity*)wormhole
1055{
1056 PlayerEntity *player = PLAYER;
1058
1059 [player setWormhole:wormhole];
1060 [player addScannedWormhole:wormhole];
1061 JSContext *context = OOJSAcquireContext();
1062 [player setJumpCause:@"carried"];
1064 ShipScriptEvent(context, player, "shipWillEnterWitchspace", STRING_TO_JSVAL(JS_InternString(context, [[player jumpCause] UTF8String])), INT_TO_JSVAL(dest));
1065 OOJSRelinquishContext(context);
1066
1067 [self allShipsDoScriptEvent:OOJSID("playerWillEnterWitchspace") andReactToAIMessage:@"PLAYER WITCHSPACE"];
1068
1069 [player setRandom_factor:(ranrot_rand() & 255)]; // random factor for market values is reset
1070
1071// misjump on wormhole sets correct travel time if needed
1073// clear old entities
1075
1076// should we add wear-and-tear to the player ship if they're not doing
1077// the jump themselves? Left out for now. - CIM
1078
1079 if (![wormhole withMisjump])
1080 {
1081 [player setSystemID:dest];
1082 [self setSystemTo: dest];
1083
1084 [self setUpSpace];
1085 [self populateNormalSpace];
1086 [player setBounty:([player legalStatus]/2) withReason:kOOLegalStatusReasonNewSystem];
1087 if ([player random_factor] < 8) [player erodeReputation]; // every 32 systems or so, dro
1088 }
1089 else
1090 {
1092
1094
1095 if (randf() < 0.1) [player erodeReputation]; // once every 10 misjumps - should be much rarer than successful jumps!
1096 }
1097 // which will kick the ship out of the wormhole with the
1098 // player still aboard
1100
1101 //reset atmospherics in case carrier was in atmosphere
1102 [UNIVERSE setSkyColorRed:0.0f // back to black
1103 green:0.0f
1104 blue:0.0f
1105 alpha:0.0f];
1106
1107 [self setWitchspaceBreakPattern:YES];
1108 [player doScriptEvent:OOJSID("shipWillExitWitchspace") withArgument:[player jumpCause]];
1109 [player doScriptEvent:OOJSID("shipExitedWitchspace") withArgument:[player jumpCause]];
1110 [player setWormhole:nil];
1111
1112}
1113
1114
1116{
1117 if (![self sun])
1118 {
1119 // we're in witchspace...
1120
1121 PlayerEntity *player = PLAYER;
1122 StationEntity *dockedStation = [player dockedStation];
1123 NSPoint coords = [player galaxy_coordinates];
1124 // check the nearest system
1126 BOOL interstel =[dockedStation interstellarUndockingAllowed];// && (s_seed.d != coords.x || s_seed.b != coords.y); - Nikos 20110623: Do we really need the commented out check?
1128
1129 // remove everything except the player and the docked station
1130 if (dockedStation && !interstel)
1131 { // jump to the nearest system
1132 [player setSystemID:sys];
1133 closeSystems = nil;
1134 [self setSystemTo: sys];
1135 int index = 0;
1136 while ([entities count] > 2)
1137 {
1138 Entity *ent = [entities objectAtIndex:index];
1139 if ((ent != player)&&(ent != dockedStation))
1140 {
1141 if (ent->isStation) // clear out queues
1142 [(StationEntity *)ent clear];
1143 [self removeEntity:ent];
1144 }
1145 else
1146 {
1147 index++; // leave that one alone
1148 }
1149 }
1150 }
1151 else
1152 {
1153 if (dockedStation == nil) [self removeAllEntitiesExceptPlayer]; // get rid of witchspace sky etc. if still extant
1154 }
1155
1156 if (!dockedStation || !interstel)
1157 {
1158 [self setUpSpace]; // launching from station that jumped from interstellar space to normal space.
1159 [self populateNormalSpace];
1160 if (dockedStation)
1161 {
1162 if ([dockedStation maxFlightSpeed] > 0) // we are a carrier: exit near the WitchspaceExitPosition
1163 {
1164 float d1 = [self randomDistanceWithinScanner];
1165 HPVector pos = [UNIVERSE getWitchspaceExitPosition]; // no need to reset the PRNG
1166 Quaternion q1;
1167
1169 if (abs((int)d1) < 2750)
1170 {
1171 d1 += ((d1 > 0.0)? 2750.0f: -2750.0f); // no closer than 2750m. Carriers are bigger than player ships.
1172 }
1173 Vector v1 = vector_forward_from_quaternion(q1);
1174 pos.x += v1.x * d1; // randomise exit position
1175 pos.y += v1.y * d1;
1176 pos.z += v1.z * d1;
1177
1179 }
1180 [self setWitchspaceBreakPattern:YES];
1181 [player setJumpCause:@"carried"];
1182 [player doScriptEvent:OOJSID("shipWillExitWitchspace") withArgument:[player jumpCause]];
1183 [player doScriptEvent:OOJSID("shipExitedWitchspace") withArgument:[player jumpCause]];
1184 }
1185 }
1186 }
1187
1188 if(!autoSaveNow) [self setViewDirection:VIEW_FORWARD];
1189 displayGUI = NO;
1190
1191 //reset atmospherics in case we ejected while we were in the atmophere
1192 [UNIVERSE setSkyColorRed:0.0f // back to black
1193 green:0.0f
1194 blue:0.0f
1195 alpha:0.0f];
1196}
1197
1198
1200{
1201 PlayerEntity *player;
1202
1203 //
1204 // check the player is still around!
1205 //
1206 if ([entities count] == 0)
1207 {
1208 /*- the player ship -*/
1209 player = [[PlayerEntity alloc] init]; // alloc retains!
1210
1211 [self addEntity:player];
1212
1213 /*--*/
1214 }
1215 else
1216 {
1217 player = [PLAYER retain]; // retained here
1218 }
1219
1220 [self setUpSpace];
1221 [self populateNormalSpace];
1222
1224 [player release]; // released here
1225
1226 [self setViewDirection:VIEW_FORWARD];
1227
1228 [comm_log_gui printLongText:[NSString stringWithFormat:@"%@ %@", [self getSystemName:systemID], [player dial_clock_adjusted]]
1229 align:GUI_ALIGN_CENTER color:[OOColor whiteColor] fadeTime:0 key:nil addToArray:[player commLog]];
1230
1231 displayGUI = NO;
1232}
1233
1234
1236{
1237 PlayerEntity *player;
1238
1239 //
1240 // check the player is still around!
1241 //
1242 if ([entities count] == 0)
1243 {
1244 /*- the player ship -*/
1245 player = [[PlayerEntity alloc] init]; // alloc retains!
1246
1247 [self addEntity:player];
1248
1249 /*--*/
1250 }
1251 else
1252 {
1253 player = [PLAYER retain]; // retained here
1254 }
1255
1256 [self setUpWitchspace];
1257 // ensure that if we got here from a jump within a planet's atmosphere,
1258 // we don't get any residual air friction
1259 [self setAirResistanceFactor:0.0f];
1260
1262 [player release]; // released here
1263
1264 [self setViewDirection:VIEW_FORWARD];
1265
1266 displayGUI = NO;
1267}
1268
1269
1271{
1272 [self setUpWitchspaceBetweenSystem:[PLAYER systemID] andSystem:[PLAYER nextHopTargetSystemID]];
1273}
1274
1275
1276- (void) setUpWitchspaceBetweenSystem:(OOSystemID)s1 andSystem:(OOSystemID)s2
1277{
1278 // new system is hyper-centric : witchspace exit point is origin
1279
1280 Entity *thing;
1281 PlayerEntity* player = PLAYER;
1282 Quaternion randomQ;
1283
1284 NSString* override_key = [self keyForInterstellarOverridesForSystems:s1 :s2 inGalaxy:galaxyID];
1285
1286 NSDictionary *systeminfo = [systemManager getPropertiesForSystemKey:override_key];
1287
1289
1290 // fixed entities (part of the graphics system really) come first...
1291
1292 /*- the sky backdrop -*/
1293 OOColor *col1 = [OOColor colorWithRed:0.0 green:1.0 blue:0.5 alpha:1.0];
1294 OOColor *col2 = [OOColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0];
1295 thing = [[SkyEntity alloc] initWithColors:col1:col2 andSystemInfo: systeminfo]; // alloc retains!
1296 [thing setScanClass: CLASS_NO_DRAW];
1297 quaternion_set_random(&randomQ);
1298 [thing setOrientation:randomQ];
1299 [self addEntity:thing];
1300 [thing release];
1301
1302 /*- the dust particle system -*/
1303 thing = [[DustEntity alloc] init];
1304 [thing setScanClass: CLASS_NO_DRAW];
1305 [self addEntity:thing];
1306 [thing release];
1307
1308 ambientLightLevel = [systeminfo oo_floatForKey:@"ambient_level" defaultValue:1.0];
1309 [self setLighting]; // also sets initial lights positions.
1310
1311 OOLog(kOOLogUniversePopulateWitchspace, @"%@", @"Populating witchspace ...");
1313
1314 [self clearSystemPopulator];
1315 NSString *populator = [systeminfo oo_stringForKey:@"populator" defaultValue:@"interstellarSpaceWillPopulate"];
1316 [system_repopulator release];
1317 system_repopulator = [[systeminfo oo_stringForKey:@"repopulator" defaultValue:@"interstellarSpaceWillRepopulate"] retain];
1318 JSContext *context = OOJSAcquireContext();
1319 [PLAYER doWorldScriptEvent:OOJSIDFromString(populator) inContext:context withArguments:NULL count:0 timeLimit:kOOJSLongTimeLimit];
1320 OOJSRelinquishContext(context);
1322
1323 // systeminfo might have a 'script_actions' resource we want to activate now...
1324 NSArray *script_actions = [systeminfo oo_arrayForKey:@"script_actions"];
1325 if (script_actions != nil)
1326 {
1327 OOStandardsDeprecated([NSString stringWithFormat:@"The script_actions system info key is deprecated for %@.",override_key]);
1328 if (!OOEnforceStandards())
1329 {
1330 [player runUnsanitizedScriptActions:script_actions
1332 withContextName:@"<witchspace script_actions>"
1333 forTarget:nil];
1334 }
1335 }
1336
1338
1340}
1341
1342
1343- (OOPlanetEntity *) setUpPlanet
1344{
1345 // set the system seed for random number generation
1347 seed_for_planet_description(systemSeed);
1348
1349 NSMutableDictionary *planetDict = [NSMutableDictionary dictionaryWithDictionary:[systemManager getPropertiesForCurrentSystem]];
1350 [planetDict oo_setBool:YES forKey:@"mainForLocalSystem"];
1351 OOPlanetEntity *a_planet = [[OOPlanetEntity alloc] initFromDictionary:planetDict withAtmosphere:[planetDict oo_boolForKey:@"has_atmosphere" defaultValue:YES] andSeed:systemSeed forSystem:systemID];
1352
1353 double planet_zpos = [planetDict oo_floatForKey:@"planet_distance" defaultValue:500000];
1354 planet_zpos *= [planetDict oo_floatForKey:@"planet_distance_multiplier" defaultValue:1.0];
1355
1356#ifdef OO_DUMP_PLANETINFO
1357 OOLog(@"planetinfo.record",@"planet zpos = %f",planet_zpos);
1358#endif
1359 [a_planet setPosition:(HPVector){ 0, 0, planet_zpos }];
1360 [a_planet setEnergy:1000000.0];
1361
1362 if ([allPlanets count]>0) // F7 sets [UNIVERSE planet], which can lead to some trouble! TODO: track down where exactly that happens!
1363 {
1364 OOPlanetEntity *tmp=[allPlanets objectAtIndex:0];
1365 [self addEntity:a_planet];
1366 [allPlanets removeObject:a_planet];
1367 cachedPlanet=a_planet;
1368 [allPlanets replaceObjectAtIndex:0 withObject:a_planet];
1369 [self removeEntity:(Entity *)tmp];
1370 }
1371 else
1372 {
1373 [self addEntity:a_planet];
1374 }
1375 return [a_planet autorelease];
1376}
1377
1378/* At any time other than game start, any call to this must be followed
1379 * by [self populateNormalSpace]. However, at game start, they need to be
1380 * separated to allow Javascript startUp routines to be run in-between */
1382{
1383 Entity *thing;
1384// ShipEntity *nav_buoy;
1385 StationEntity *a_station;
1386 OOSunEntity *a_sun;
1387 OOPlanetEntity *a_planet;
1388
1389 HPVector stationPos;
1390
1391 Vector vf;
1392 id dict_object;
1393
1394 NSDictionary *systeminfo = [systemManager getPropertiesForCurrentSystem];
1395 unsigned techlevel = [systeminfo oo_unsignedIntForKey:KEY_TECHLEVEL];
1396 NSString *stationDesc = nil, *defaultStationDesc = nil;
1397 OOColor *bgcolor;
1398 OOColor *pale_bgcolor;
1399 BOOL sunGoneNova;
1400
1402
1403 [[GameController sharedController] logProgress:DESC(@"populating-space")];
1404
1405 sunGoneNova = [systeminfo oo_boolForKey:@"sun_gone_nova" defaultValue:NO];
1406
1407 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - clearSubRegions, sky, dust");
1409
1410 // fixed entities (part of the graphics system really) come first...
1411 [self setSkyColorRed:0.0f
1412 green:0.0f
1413 blue:0.0f
1414 alpha:0.0f];
1415
1416
1417#ifdef OO_DUMP_PLANETINFO
1418 OOLog(@"planetinfo.record",@"seed = %d %d %d %d",system_seed.c,system_seed.d,system_seed.e,system_seed.f);
1419 OOLog(@"planetinfo.record",@"coordinates = %d %d",system_seed.d,system_seed.b);
1420
1421#define SPROP(PROP) OOLog(@"planetinfo.record",@#PROP " = \"%@\";",[systeminfo oo_stringForKey:@"" #PROP]);
1422#define IPROP(PROP) OOLog(@"planetinfo.record",@#PROP " = %d;",[systeminfo oo_intForKey:@#PROP]);
1423#define FPROP(PROP) OOLog(@"planetinfo.record",@#PROP " = %f;",[systeminfo oo_floatForKey:@"" #PROP]);
1424 IPROP(government);
1425 IPROP(economy);
1426 IPROP(techlevel);
1427 IPROP(population);
1428 IPROP(productivity);
1429 SPROP(name);
1430 SPROP(inhabitant);
1431 SPROP(inhabitants);
1432 SPROP(description);
1433#endif
1434
1435 // set the system seed for random number generation
1436 seed_for_planet_description(systemSeed);
1437
1438 /*- the sky backdrop -*/
1439 // colors...
1440 float h1 = randf();
1441 float h2 = h1 + 1.0 / (1.0 + (Ranrot() % 5));
1442 while (h2 > 1.0)
1443 h2 -= 1.0;
1444 OOColor *col1 = [OOColor colorWithHue:h1 saturation:randf() brightness:0.5 + randf()/2.0 alpha:1.0];
1445 OOColor *col2 = [OOColor colorWithHue:h2 saturation:0.5 + randf()/2.0 brightness:0.5 + randf()/2.0 alpha:1.0];
1446
1447 thing = [[SkyEntity alloc] initWithColors:col1:col2 andSystemInfo: systeminfo]; // alloc retains!
1448 [thing setScanClass: CLASS_NO_DRAW];
1449 [self addEntity:thing];
1450// bgcolor = [(SkyEntity *)thing skyColor];
1451//
1452 h1 = randf()/3.0;
1453 if (h1 > 0.17)
1454 {
1455 h1 += 0.33;
1456 }
1457
1458 ambientLightLevel = [systeminfo oo_floatForKey:@"ambient_level" defaultValue:1.0];
1459
1460 // pick a main sequence colour
1461
1462 dict_object=[systeminfo objectForKey:@"sun_color"];
1463 if (dict_object!=nil)
1464 {
1465 bgcolor = [OOColor colorWithDescription:dict_object];
1466 }
1467 else
1468 {
1469 bgcolor = [OOColor colorWithHue:h1 saturation:0.75*randf() brightness:0.65+randf()/5.0 alpha:1.0];
1470 }
1471
1473 [thing release];
1474 /*--*/
1475
1476 /*- the dust particle system -*/
1477 thing = [[DustEntity alloc] init]; // alloc retains!
1478 [thing setScanClass: CLASS_NO_DRAW];
1479 [self addEntity:thing];
1480 [(DustEntity *)thing setDustColor:pale_bgcolor];
1481 [thing release];
1482 /*--*/
1483
1484 float defaultSunFlare = randf()*0.1;
1485 float defaultSunHues = 0.5+randf()*0.5;
1487
1488 // actual entities next...
1489
1490 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - planet");
1491 a_planet=[self setUpPlanet]; // resets RNG when called
1492 double planet_radius = [a_planet radius];
1494
1495 // set the system seed for random number generation
1496 seed_for_planet_description(systemSeed);
1497
1498 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - sun");
1499 /*- space sun -*/
1500 double sun_radius;
1501 double sun_distance;
1502 double sunDistanceModifier;
1503 double safeDistance;
1504 HPVector sunPos;
1505
1506 sunDistanceModifier = [systeminfo oo_nonNegativeDoubleForKey:@"sun_distance_modifier" defaultValue:0.0];
1507 if (sunDistanceModifier < 6.0) // <6 isn't valid
1508 {
1509 sun_distance = [systeminfo oo_nonNegativeDoubleForKey:@"sun_distance" defaultValue:(planet_radius*20)];
1510 // note, old property was _modifier, new property is _multiplier
1511 sun_distance *= [systeminfo oo_nonNegativeDoubleForKey:@"sun_distance_multiplier" defaultValue:1];
1512 }
1513 else
1514 {
1515 sun_distance = planet_radius * sunDistanceModifier;
1516 }
1517
1518 sun_radius = [systeminfo oo_nonNegativeDoubleForKey:@"sun_radius" defaultValue:2.5 * planet_radius];
1519 // clamp the sun radius
1520 if ((sun_radius < 1000.0) || (sun_radius > sun_distance / 2 && !sunGoneNova))
1521 {
1522 OOLogWARN(@"universe.setup.badSun",@"Sun radius of %f is not valid for this system",sun_radius);
1523 sun_radius = sun_radius < 1000.0 ? 1000.0 : (sun_distance / 2);
1524 }
1525#ifdef OO_DUMP_PLANETINFO
1526 OOLog(@"planetinfo.record",@"sun_radius = %f",sun_radius);
1527#endif
1528 safeDistance=36 * sun_radius * sun_radius; // 6 times the sun radius
1529
1530 // here we need to check if the sun collides with (or is too close to) the witchpoint
1531 // otherwise at (for example) Maregais in Galaxy 1 we go BANG!
1532 HPVector sun_dir = [systeminfo oo_hpvectorForKey:@"sun_vector"];
1533 sun_distance /= 2.0;
1534 do
1535 {
1536 sun_distance *= 2.0;
1537 sunPos = HPvector_subtract([a_planet position],
1538 HPvector_multiply_scalar(sun_dir,sun_distance));
1539
1540 // if not in the safe distance, multiply by two and try again
1541 }
1542 while (HPmagnitude2(sunPos) < safeDistance);
1543
1544 // set planetary axial tilt to 0 degrees
1545 // TODO: allow this to vary
1546 [a_planet setOrientation:quaternion_rotation_betweenHP(sun_dir,make_HPvector(1.0,0.0,0.0))];
1547
1548#ifdef OO_DUMP_PLANETINFO
1549 OOLog(@"planetinfo.record",@"sun_vector = %.3f %.3f %.3f",vf.x,vf.y,vf.z);
1550 OOLog(@"planetinfo.record",@"sun_distance = %.0f",sun_distance);
1551#endif
1552
1553
1554
1555 NSMutableDictionary *sun_dict = [NSMutableDictionary dictionaryWithCapacity:5];
1556 [sun_dict setObject:[NSNumber numberWithDouble:sun_radius] forKey:@"sun_radius"];
1557 dict_object=[systeminfo objectForKey: @"corona_shimmer"];
1558 if (dict_object!=nil) [sun_dict setObject:dict_object forKey:@"corona_shimmer"];
1559 dict_object=[systeminfo objectForKey: @"corona_hues"];
1560 if (dict_object!=nil)
1561 {
1562 [sun_dict setObject:dict_object forKey:@"corona_hues"];
1563 }
1564 else
1565 {
1566 [sun_dict setObject:[NSNumber numberWithFloat:defaultSunHues] forKey:@"corona_hues"];
1567 }
1568 dict_object=[systeminfo objectForKey: @"corona_flare"];
1569 if (dict_object!=nil)
1570 {
1571 [sun_dict setObject:dict_object forKey:@"corona_flare"];
1572 }
1573 else
1574 {
1575 [sun_dict setObject:[NSNumber numberWithFloat:defaultSunFlare] forKey:@"corona_flare"];
1576 }
1577 dict_object=[systeminfo objectForKey:KEY_SUNNAME];
1578 if (dict_object!=nil)
1579 {
1580 [sun_dict setObject:dict_object forKey:KEY_SUNNAME];
1581 }
1582#ifdef OO_DUMP_PLANETINFO
1583 OOLog(@"planetinfo.record",@"corona_flare = %f",[sun_dict oo_floatForKey:@"corona_flare"]);
1584 OOLog(@"planetinfo.record",@"corona_hues = %f",[sun_dict oo_floatForKey:@"corona_hues"]);
1585 OOLog(@"planetinfo.record",@"sun_color = %@",[bgcolor descriptionComponents]);
1586#endif
1587 a_sun = [[OOSunEntity alloc] initSunWithColor:bgcolor andDictionary:sun_dict]; // alloc retains!
1588
1589 [a_sun setStatus:STATUS_ACTIVE];
1590 [a_sun setPosition:sunPos]; // sets also light origin
1591 [a_sun setEnergy:1000000.0];
1592 [self addEntity:a_sun];
1593
1594 if (sunGoneNova)
1595 {
1596 [a_sun setRadius: sun_radius andCorona:0.3];
1597 [a_sun setThrowSparks:YES];
1598 [a_sun setVelocity: kZeroVector];
1599 }
1600
1601 // set the lighting only after we know which sun we have.
1602 [self setLighting];
1604
1605 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - main station");
1606 /*- space station -*/
1607 stationPos = [a_planet position];
1608
1609 vf = [systeminfo oo_vectorForKey:@"station_vector"];
1610#ifdef OO_DUMP_PLANETINFO
1611 OOLog(@"planetinfo.record",@"station_vector = %.3f %.3f %.3f",vf.x,vf.y,vf.z);
1612#endif
1613 stationPos = HPvector_subtract(stationPos, vectorToHPVector(vector_multiply_scalar(vf, 2.0 * planet_radius)));
1614
1615
1617 stationDesc = [systeminfo oo_stringForKey:@"station" defaultValue:@"coriolis"];
1618#ifdef OO_DUMP_PLANETINFO
1619 OOLog(@"planetinfo.record",@"station = %@",stationDesc);
1620#endif
1621
1622 a_station = (StationEntity *)[self newShipWithRole:stationDesc]; // retain count = 1
1623
1624 /* Sanity check: ensure that only stations are generated here. This is an
1625 attempt to fix exceptions of the form:
1626 NSInvalidArgumentException : *** -[ShipEntity setPlanet:]: selector
1627 not recognized [self = 0x19b7e000] *****
1628 which I presume to be originating here since all other uses of
1629 setPlanet: are guarded by isStation checks. This error could happen if
1630 a ship that is not a station has a station role, or equivalently if an
1631 OXP sets a system's station role to a role used by non-stations.
1632 -- Ahruman 20080303
1633 */
1634 if (![a_station isStation] || ![a_station validForAddToUniverse])
1635 {
1636 if (a_station == nil)
1637 {
1638 // Should have had a more specific error already, just specify context
1639 OOLog(@"universe.setup.badStation", @"Failed to set up a ship for role \"%@\" as system station, trying again with \"%@\".", stationDesc, defaultStationDesc);
1640 }
1641 else
1642 {
1643 OOLog(@"universe.setup.badStation", @"***** ERROR: Attempt to use non-station ship of type \"%@\" for role \"%@\" as system station, trying again with \"%@\".", [a_station name], stationDesc, defaultStationDesc);
1644 }
1645 [a_station release];
1646 stationDesc = defaultStationDesc;
1647 a_station = (StationEntity *)[self newShipWithRole:stationDesc]; // retain count = 1
1648
1649 if (![a_station isStation] || ![a_station validForAddToUniverse])
1650 {
1651 if (a_station == nil)
1652 {
1653 OOLog(@"universe.setup.badStation", @"On retry, failed to set up a ship for role \"%@\" as system station. Trying to fall back to built-in Coriolis station.", stationDesc);
1654 }
1655 else
1656 {
1657 OOLog(@"universe.setup.badStation", @"***** ERROR: On retry, rolled non-station ship of type \"%@\" for role \"%@\". Non-station ships should not have this role! Trying to fall back to built-in Coriolis station.", [a_station name], stationDesc);
1658 }
1659 [a_station release];
1660
1661 a_station = (StationEntity *)[self newShipWithName:@"coriolis-station"];
1662 if (![a_station isStation] || ![a_station validForAddToUniverse])
1663 {
1664 OOLog(@"universe.setup.badStation", @"%@", @"Could not create built-in Coriolis station! Generating a stationless system.");
1665 DESTROY(a_station);
1666 }
1667 }
1668 }
1669
1670 if (a_station != nil)
1671 {
1672 [a_station setOrientation:quaternion_rotation_between(vf,make_vector(0.0,0.0,1.0))];
1673 [a_station setPosition: stationPos];
1674 [a_station setPitch: 0.0];
1675 [a_station setScanClass: CLASS_STATION];
1676 //[a_station setPlanet:[self planet]]; // done inside addEntity.
1678 [self addEntity:a_station]; // STATUS_IN_FLIGHT, AI state GLOBAL
1679 [a_station setStatus:STATUS_ACTIVE]; // For backward compatibility. Might not be needed.
1680 [a_station setAllowsFastDocking:true]; // Main stations always allow fast docking.
1681 [a_station setAllegiance:@"galcop"]; // Main station is galcop controlled
1682 }
1684
1685 cachedSun = a_sun;
1686 cachedPlanet = a_planet;
1687 cachedStation = a_station;
1688 closeSystems = nil;
1690
1691
1692 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - populate from wormholes");
1695
1696 [a_sun release];
1697 [a_station release];
1698}
1699
1700
1702{
1703 NSDictionary *systeminfo = [systemManager getPropertiesForCurrentSystem];
1704
1705 BOOL sunGoneNova = [systeminfo oo_boolForKey:@"sun_gone_nova"];
1706 // check for nova
1707 if (sunGoneNova)
1708 {
1709 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - post-nova");
1710
1711 HPVector v0 = make_HPvector(0,0,34567.89);
1712 double min_safe_dist2 = 6000000.0 * 6000000.0;
1713 HPVector sunPos = [cachedSun position];
1714 while (HPmagnitude2(cachedSun->position) < min_safe_dist2) // back off the planetary bodies
1715 {
1716 v0.z *= 2.0;
1717
1718 sunPos = HPvector_add(sunPos, v0);
1719 [cachedSun setPosition:sunPos]; // also sets light origin
1720
1721 }
1722
1723 [self removeEntity:cachedPlanet]; // and Poof! it's gone
1724 cachedPlanet = nil;
1725 [self removeEntity:cachedStation]; // also remove main station
1726 cachedStation = nil;
1727 }
1728
1729 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - populate from hyperpoint");
1730// [self populateSpaceFromHyperPoint:witchPos toPlanetPosition: a_planet->position andSunPosition: a_sun->position];
1731 [self clearSystemPopulator];
1732
1733 if ([PLAYER status] != STATUS_START_GAME)
1734 {
1735 NSString *populator = [systeminfo oo_stringForKey:@"populator" defaultValue:(sunGoneNova)?@"novaSystemWillPopulate":@"systemWillPopulate"];
1736 [system_repopulator release];
1737 system_repopulator = [[systeminfo oo_stringForKey:@"repopulator" defaultValue:(sunGoneNova)?@"novaSystemWillRepopulate":@"systemWillRepopulate"] retain];
1738
1739 JSContext *context = OOJSAcquireContext();
1740 [PLAYER doWorldScriptEvent:OOJSIDFromString(populator) inContext:context withArguments:NULL count:0 timeLimit:kOOJSLongTimeLimit];
1741 OOJSRelinquishContext(context);
1742 [self populateSystemFromDictionariesWithSun:cachedSun andPlanet:cachedPlanet];
1743 }
1744
1746
1747 // systeminfo might have a 'script_actions' resource we want to activate now...
1748 NSArray *script_actions = [systeminfo oo_arrayForKey:@"script_actions"];
1749 if (script_actions != nil)
1750 {
1751 OOStandardsDeprecated([NSString stringWithFormat:@"The script_actions system info key is deprecated for %@.",[self getSystemName:systemID]]);
1752 if (!OOEnforceStandards())
1753 {
1754 OO_DEBUG_PUSH_PROGRESS(@"%@", @"setUpSpace - legacy script_actions");
1755 [PLAYER runUnsanitizedScriptActions:script_actions
1756 allowingAIMethods:NO
1757 withContextName:@"<system script_actions>"
1758 forTarget:nil];
1760 }
1761 }
1762
1764}
1765
1766
1768{
1769 [populatorSettings release];
1770 populatorSettings = [[NSMutableDictionary alloc] initWithCapacity:128];
1771}
1772
1773
1774- (NSDictionary *) getPopulatorSettings
1775{
1776 return populatorSettings;
1777}
1778
1779
1780- (void) setPopulatorSetting:(NSString *)key to:(NSDictionary *)setting
1781{
1782 if (setting == nil)
1783 {
1784 [populatorSettings removeObjectForKey:key];
1785 }
1786 else
1787 {
1788 [populatorSettings setObject:setting forKey:key];
1789 }
1790}
1791
1792
1794{
1796}
1797
1798
1799- (void) populateSystemFromDictionariesWithSun:(OOSunEntity *)sun andPlanet:(OOPlanetEntity *)planet
1800{
1802 NSArray *blocks = [populatorSettings allValues];
1803 NSEnumerator *enumerator = [[blocks sortedArrayUsingFunction:populatorPrioritySort context:nil] objectEnumerator];
1804 NSDictionary *populator = nil;
1805 HPVector location = kZeroHPVector;
1806 uint32_t i, locationSeed, groupCount, rndvalue;
1807 RANROTSeed rndcache = RANROTGetFullSeed();
1808 RANROTSeed rndlocal = RANROTGetFullSeed();
1809 NSString *locationCode = nil;
1811 while ((populator = [enumerator nextObject]))
1812 {
1813 deterministic_population = [populator oo_boolForKey:@"deterministic" defaultValue:NO];
1814 if (EXPECT_NOT(sun == nil || planet == nil))
1815 {
1816 // needs to be a non-nova system, and not interstellar space
1818 }
1819
1820 locationSeed = [populator oo_unsignedIntForKey:@"locationSeed" defaultValue:0];
1821 groupCount = [populator oo_unsignedIntForKey:@"groupCount" defaultValue:1];
1822
1823 for (i = 0; i < groupCount; i++)
1824 {
1825 locationCode = [populator oo_stringForKey:@"location" defaultValue:@"COORDINATES"];
1826 if ([locationCode isEqualToString:@"COORDINATES"])
1827 {
1828 location = [populator oo_hpvectorForKey:@"coordinates" defaultValue:kZeroHPVector];
1829 }
1830 else
1831 {
1832 if (locationSeed != 0)
1833 {
1834 rndcache = RANROTGetFullSeed();
1835 // different place for each system
1836 rndlocal = RanrotSeedFromRandomSeed(systemSeed);
1837 rndvalue = RanrotWithSeed(&rndlocal);
1838 // ...for location seed
1839 rndlocal = MakeRanrotSeed(rndvalue+locationSeed);
1840 rndvalue = RanrotWithSeed(&rndlocal);
1841 // ...for iteration (63647 is nothing special, just a largish prime)
1842 RANROTSetFullSeed(MakeRanrotSeed(rndvalue+(i*63647)));
1843 }
1844 else
1845 {
1846 // not fixed coordinates and not seeded RNG; can't
1847 // be deterministic
1849 }
1850 if (sun == nil || planet == nil)
1851 {
1852 // all interstellar space and nova locations equal to WITCHPOINT
1853 location = [self locationByCode:@"WITCHPOINT" withSun:nil andPlanet:nil];
1854 }
1855 else
1856 {
1857 location = [self locationByCode:locationCode withSun:sun andPlanet:planet];
1858 }
1859 if(locationSeed != 0)
1860 {
1861 // go back to the main random sequence
1862 RANROTSetFullSeed(rndcache);
1863 }
1864 }
1865 // location now contains a Vector coordinate, one way or another
1866 pdef = [populator objectForKey:@"callbackObj"];
1867 [pdef runCallback:location];
1868 }
1869 }
1870 // nothing is deterministic once the populator is done
1872}
1873
1874
1875/* Generates a position within one of the named regions:
1876 *
1877 * WITCHPOINT: within scanner of witchpoint
1878 * LANE_*: within two scanner of lane, not too near each end
1879 * STATION_AEGIS: within two scanner of main station, not in planet
1880 * *_ORBIT_*: around the object, in a shell relative to object radius
1881 * TRIANGLE: somewhere in the triangle defined by W, P, S
1882 * INNER_SYSTEM: closer to the sun than the planet is
1883 * OUTER_SYSTEM: further from the sun than the planet is
1884 * *_OFFPLANE: like the above, but not on the orbital plane
1885 *
1886 * Can be called with nil sun or planet, but if so the calling function
1887 * must make sure the location code is WITCHPOINT.
1888 */
1889- (HPVector) locationByCode:(NSString *)code withSun:(OOSunEntity *)sun andPlanet:(OOPlanetEntity *)planet
1890{
1891 HPVector result = kZeroHPVector;
1892 if ([code isEqualToString:@"WITCHPOINT"] || sun == nil || planet == nil || [sun goneNova])
1893 {
1895 }
1896 // past this point, can assume non-nil sun, planet
1897 else
1898 {
1899 if ([code isEqualToString:@"LANE_WPS"])
1900 {
1901 // pick position on one of the lanes, weighted by lane length
1902 double l1 = HPmagnitude([planet position]);
1903 double l2 = HPmagnitude(HPvector_subtract([sun position],[planet position]));
1904 double l3 = HPmagnitude([sun position]);
1905 double total = l1+l2+l3;
1906 float choice = randf();
1907 if (choice < l1/total)
1908 {
1909 return [self locationByCode:@"LANE_WP" withSun:sun andPlanet:planet];
1910 }
1911 else if (choice < (l1+l2)/total)
1912 {
1913 return [self locationByCode:@"LANE_PS" withSun:sun andPlanet:planet];
1914 }
1915 else
1916 {
1917 return [self locationByCode:@"LANE_WS" withSun:sun andPlanet:planet];
1918 }
1919 }
1920 else if ([code isEqualToString:@"LANE_WP"])
1921 {
1923 }
1924 else if ([code isEqualToString:@"LANE_WS"])
1925 {
1927 }
1928 else if ([code isEqualToString:@"LANE_PS"])
1929 {
1930 result = OORandomPositionInCylinder([planet position],[planet radius]*3,[sun position],[sun radius]*3,LANE_WIDTH);
1931 }
1932 else if ([code isEqualToString:@"STATION_AEGIS"])
1933 {
1934 do
1935 {
1936 result = OORandomPositionInShell([[self station] position],[[self station] collisionRadius]*1.2,SCANNER_MAX_RANGE*2.0);
1937 } while(HPdistance2(result,[planet position])<[planet radius]*[planet radius]*1.5);
1938 // loop to make sure not generated too close to the planet's surface
1939 }
1940 else if ([code isEqualToString:@"PLANET_ORBIT_LOW"])
1941 {
1942 result = OORandomPositionInShell([planet position],[planet radius]*1.1,[planet radius]*2.0);
1943 }
1944 else if ([code isEqualToString:@"PLANET_ORBIT"])
1945 {
1946 result = OORandomPositionInShell([planet position],[planet radius]*2.0,[planet radius]*4.0);
1947 }
1948 else if ([code isEqualToString:@"PLANET_ORBIT_HIGH"])
1949 {
1950 result = OORandomPositionInShell([planet position],[planet radius]*4.0,[planet radius]*8.0);
1951 }
1952 else if ([code isEqualToString:@"STAR_ORBIT_LOW"])
1953 {
1954 result = OORandomPositionInShell([sun position],[sun radius]*1.1,[sun radius]*2.0);
1955 }
1956 else if ([code isEqualToString:@"STAR_ORBIT"])
1957 {
1958 result = OORandomPositionInShell([sun position],[sun radius]*2.0,[sun radius]*4.0);
1959 }
1960 else if ([code isEqualToString:@"STAR_ORBIT_HIGH"])
1961 {
1962 result = OORandomPositionInShell([sun position],[sun radius]*4.0,[sun radius]*8.0);
1963 }
1964 else if ([code isEqualToString:@"TRIANGLE"])
1965 {
1966 do {
1967 // pick random point in triangle by algorithm at
1968 // http://adamswaab.wordpress.com/2009/12/11/random-point-in-a-triangle-barycentric-coordinates/
1969 // simplified by using the origin as A
1970 OOScalar r = randf();
1971 OOScalar s = randf();
1972 if (r+s >= 1)
1973 {
1974 r = 1-r;
1975 s = 1-s;
1976 }
1977 result = HPvector_add(HPvector_multiply_scalar([planet position],r),HPvector_multiply_scalar([sun position],s));
1978 }
1979 // make sure at least 3 radii from vertices
1980 while(HPdistance2(result,[sun position]) < [sun radius]*[sun radius]*9.0 || HPdistance2(result,[planet position]) < [planet radius]*[planet radius]*9.0 || HPmagnitude2(result) < SCANNER_MAX_RANGE2 * 9.0);
1981 }
1982 else if ([code isEqualToString:@"INNER_SYSTEM"])
1983 {
1984 do {
1985 result = OORandomPositionInShell([sun position],[sun radius]*3.0,HPdistance([sun position],[planet position]));
1986 result = OOProjectHPVectorToPlane(result,kZeroHPVector,HPcross_product([sun position],[planet position]));
1987 result = HPvector_add(result,OOHPVectorRandomSpatial([planet radius]));
1988 // projection to plane could bring back too close to sun
1989 } while (HPdistance2(result,[sun position]) < [sun radius]*[sun radius]*9.0);
1990 }
1991 else if ([code isEqualToString:@"INNER_SYSTEM_OFFPLANE"])
1992 {
1993 result = OORandomPositionInShell([sun position],[sun radius]*3.0,HPdistance([sun position],[planet position]));
1994 }
1995 else if ([code isEqualToString:@"OUTER_SYSTEM"])
1996 {
1997 result = OORandomPositionInShell([sun position],HPdistance([sun position],[planet position]),HPdistance([sun position],[planet position])*10.0); // no more than 10 AU out
1998 result = OOProjectHPVectorToPlane(result,kZeroHPVector,HPcross_product([sun position],[planet position]));
1999 result = HPvector_add(result,OOHPVectorRandomSpatial(0.01*HPdistance(result,[sun position]))); // within 1% of plane
2000 }
2001 else if ([code isEqualToString:@"OUTER_SYSTEM_OFFPLANE"])
2002 {
2003 result = OORandomPositionInShell([sun position],HPdistance([sun position],[planet position]),HPdistance([sun position],[planet position])*10.0); // no more than 10 AU out
2004 }
2005 else
2006 {
2007 OOLog(kOOLogUniversePopulateError,@"Named populator region %@ is not implemented, falling back to WITCHPOINT",code);
2009 }
2010 }
2011 return result;
2012}
2013
2014
2015- (void) setAmbientLightLevel:(float)newValue
2016{
2017 NSAssert(UNIVERSE != nil, @"Attempt to set ambient light level with a non yet existent universe.");
2018
2019 ambientLightLevel = OOClamp_0_max_f(newValue, 10.0f);
2020 return;
2021}
2022
2023
2025{
2026 return ambientLightLevel;
2027}
2028
2029
2031{
2032 /*
2033
2034 GL_LIGHT1 is the sun and is active while a sun exists in space
2035 where there is no sun (witch/interstellar space) this is placed at the origin
2036
2037 Shaders: this light is also used inside the station and needs to have its position reset
2038 relative to the player whenever demo ships or background scenes are to be shown -- 20100111
2039
2040
2041 GL_LIGHT0 is the light for inside the station and needs to have its position reset
2042 relative to the player whenever demo ships or background scenes are to be shown
2043
2044 Shaders: this light is not used. -- 20100111
2045
2046 */
2047
2048 OOSunEntity *the_sun = [self sun];
2049 SkyEntity *the_sky = nil;
2050 GLfloat sun_pos[] = {0.0, 0.0, 0.0, 1.0}; // equivalent to kZeroVector - for interstellar space.
2051 GLfloat sun_ambient[] = {0.0, 0.0, 0.0, 1.0}; // overridden later in code
2052 int i;
2053
2054 for (i = n_entities - 1; i > 0; i--)
2055 if ((sortedEntities[i]) && ([sortedEntities[i] isKindOfClass:[SkyEntity class]]))
2056 the_sky = (SkyEntity*)sortedEntities[i];
2057
2058 if (the_sun)
2059 {
2060 [the_sun getDiffuseComponents:sun_diffuse];
2061 [the_sun getSpecularComponents:sun_specular];
2062 OOGL(glLightfv(GL_LIGHT1, GL_AMBIENT, sun_ambient));
2063 OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_diffuse));
2064 OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, sun_specular));
2065 sun_pos[0] = the_sun->position.x;
2066 sun_pos[1] = the_sun->position.y;
2067 sun_pos[2] = the_sun->position.z;
2068 }
2069 else
2070 {
2071 // witchspace
2072 stars_ambient[0] = 0.05; stars_ambient[1] = 0.20; stars_ambient[2] = 0.05; stars_ambient[3] = 1.0;
2073 sun_diffuse[0] = 0.85; sun_diffuse[1] = 1.0; sun_diffuse[2] = 0.85; sun_diffuse[3] = 1.0;
2074 sun_specular[0] = 0.95; sun_specular[1] = 1.0; sun_specular[2] = 0.95; sun_specular[3] = 1.0;
2075 OOGL(glLightfv(GL_LIGHT1, GL_AMBIENT, sun_ambient));
2076 OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_diffuse));
2077 OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, sun_specular));
2078 }
2079
2080 OOGL(glLightfv(GL_LIGHT1, GL_POSITION, sun_pos));
2081
2082 if (the_sky)
2083 {
2084 // ambient lighting!
2085 GLfloat r,g,b,a;
2086 [[the_sky skyColor] getRed:&r green:&g blue:&b alpha:&a];
2090 GLfloat ambient_level = [self ambientLightLevel];
2091 stars_ambient[0] = ambient_level * SKY_AMBIENT_ADJUSTMENT * (1.0 + r) * (1.0 + r);
2092 stars_ambient[1] = ambient_level * SKY_AMBIENT_ADJUSTMENT * (1.0 + g) * (1.0 + g);
2093 stars_ambient[2] = ambient_level * SKY_AMBIENT_ADJUSTMENT * (1.0 + b) * (1.0 + b);
2094 stars_ambient[3] = 1.0;
2095 }
2096
2097 // light for demo ships display..
2098 OOGL(glLightfv(GL_LIGHT0, GL_AMBIENT, docked_light_ambient));
2099 OOGL(glLightfv(GL_LIGHT0, GL_DIFFUSE, docked_light_diffuse));
2100 OOGL(glLightfv(GL_LIGHT0, GL_SPECULAR, docked_light_specular));
2101 OOGL(glLightfv(GL_LIGHT0, GL_POSITION, demo_light_position));
2102 OOGL(glLightModelfv(GL_LIGHT_MODEL_AMBIENT, stars_ambient));
2103}
2104
2105
2106// Call this method to avoid lighting glich after windowed/fullscreen transition on macs.
2108{
2110}
2111
2112
2113- (void) setMainLightPosition: (Vector) sunPos
2114{
2115 main_light_position[0] = sunPos.x;
2116 main_light_position[1] = sunPos.y;
2117 main_light_position[2] = sunPos.z;
2118 main_light_position[3] = 1.0;
2119}
2120
2121
2122- (ShipEntity *) addShipWithRole:(NSString *)desc launchPos:(HPVector)launchPos rfactor:(GLfloat)rfactor
2123{
2124 if (rfactor != 0.0)
2125 {
2126 // Calculate the position as soon as possible, to minimise 'lollipop flash'
2127 launchPos.x += 2 * rfactor * (randf() - 0.5);
2128 launchPos.y += 2 * rfactor * (randf() - 0.5);
2129 launchPos.z += 2 * rfactor * (randf() - 0.5);
2130 }
2131
2132 ShipEntity *ship = [self newShipWithRole:desc]; // retain count = 1
2133
2134 if (ship)
2135 {
2136 [ship setPosition:launchPos]; // minimise 'lollipop flash'
2137
2138 // Deal with scripted cargopods and ensure they are filled with something.
2139 if ([ship hasRole:@"cargopod"]) [self fillCargopodWithRandomCargo:ship];
2140
2141 // Ensure piloted ships have pilots.
2142 if (![ship crew] && ![ship isUnpiloted])
2143 [ship setCrew:[NSArray arrayWithObject:
2145 andOriginalSystem:Ranrot() & 255]]];
2146
2147 if ([ship scanClass] == CLASS_NOT_SET)
2148 {
2149 [ship setScanClass: CLASS_NEUTRAL];
2150 }
2151 [self addEntity:ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
2152 [ship release];
2153 return ship;
2154 }
2155 return nil;
2156}
2157
2158
2159- (void) addShipWithRole:(NSString *) desc nearRouteOneAt:(double) route_fraction
2160{
2161 // adds a ship within scanner range of a point on route 1
2162
2163 Entity *theStation = [self station];
2164 if (!theStation)
2165 {
2166 return;
2167 }
2168
2169 HPVector launchPos = OOHPVectorInterpolate([self getWitchspaceExitPosition], [theStation position], route_fraction);
2170
2171 [self addShipWithRole:desc launchPos:launchPos rfactor:SCANNER_MAX_RANGE];
2172}
2173
2174
2175- (HPVector) coordinatesForPosition:(HPVector) pos withCoordinateSystem:(NSString *) system returningScalar:(GLfloat*) my_scalar
2176{
2177 /* the point is described using a system selected by a string
2178 consisting of a three letter code.
2179
2180 The first letter indicates the feature that is the origin of the coordinate system.
2181 w => witchpoint
2182 s => sun
2183 p => planet
2184
2185 The next letter indicates the feature on the 'z' axis of the coordinate system.
2186 w => witchpoint
2187 s => sun
2188 p => planet
2189
2190 Then the 'y' axis of the system is normal to the plane formed by the planet, sun and witchpoint.
2191 And the 'x' axis of the system is normal to the y and z axes.
2192 So:
2193 ps: z axis = (planet -> sun) y axis = normal to (planet - sun - witchpoint) x axis = normal to y and z axes
2194 pw: z axis = (planet -> witchpoint) y axis = normal to (planet - witchpoint - sun) x axis = normal to y and z axes
2195 sp: z axis = (sun -> planet) y axis = normal to (sun - planet - witchpoint) x axis = normal to y and z axes
2196 sw: z axis = (sun -> witchpoint) y axis = normal to (sun - witchpoint - planet) x axis = normal to y and z axes
2197 wp: z axis = (witchpoint -> planet) y axis = normal to (witchpoint - planet - sun) x axis = normal to y and z axes
2198 ws: z axis = (witchpoint -> sun) y axis = normal to (witchpoint - sun - planet) x axis = normal to y and z axes
2199
2200 The third letter denotes the units used:
2201 m: meters
2202 p: planetary radii
2203 s: solar radii
2204 u: distance between first two features indicated (eg. spu means that u = distance from sun to the planet)
2205
2206 in interstellar space (== no sun) coordinates are absolute irrespective of the system used.
2207
2208 [1.71] The position code "abs" can also be used for absolute coordinates.
2209
2210 */
2211
2212 NSString* l_sys = [system lowercaseString];
2213 if ([l_sys length] != 3)
2214 return kZeroHPVector;
2215 OOPlanetEntity* the_planet = [self planet];
2216 OOSunEntity* the_sun = [self sun];
2217 if (the_planet == nil || the_sun == nil || [l_sys isEqualToString:@"abs"])
2218 {
2219 if (my_scalar) *my_scalar = 1.0;
2220 return pos;
2221 }
2222 HPVector w_pos = [self getWitchspaceExitPosition]; // don't reset PRNG
2223 HPVector p_pos = the_planet->position;
2224 HPVector s_pos = the_sun->position;
2225
2226 const char* c_sys = [l_sys UTF8String];
2227 HPVector p0, p1, p2;
2228
2229 switch (c_sys[0])
2230 {
2231 case 'w':
2232 p0 = w_pos;
2233 switch (c_sys[1])
2234 {
2235 case 'p':
2236 p1 = p_pos; p2 = s_pos; break;
2237 case 's':
2238 p1 = s_pos; p2 = p_pos; break;
2239 default:
2240 return kZeroHPVector;
2241 }
2242 break;
2243 case 'p':
2244 p0 = p_pos;
2245 switch (c_sys[1])
2246 {
2247 case 'w':
2248 p1 = w_pos; p2 = s_pos; break;
2249 case 's':
2250 p1 = s_pos; p2 = w_pos; break;
2251 default:
2252 return kZeroHPVector;
2253 }
2254 break;
2255 case 's':
2256 p0 = s_pos;
2257 switch (c_sys[1])
2258 {
2259 case 'w':
2260 p1 = w_pos; p2 = p_pos; break;
2261 case 'p':
2262 p1 = p_pos; p2 = w_pos; break;
2263 default:
2264 return kZeroHPVector;
2265 }
2266 break;
2267 default:
2268 return kZeroHPVector;
2269 }
2270 HPVector k = HPvector_normal_or_zbasis(HPvector_subtract(p1, p0)); // 'forward'
2271 HPVector v = HPvector_normal_or_xbasis(HPvector_subtract(p2, p0)); // temporary vector in plane of 'forward' and 'right'
2272
2273 HPVector j = HPcross_product(k, v); // 'up'
2274 HPVector i = HPcross_product(j, k); // 'right'
2275
2276 GLfloat scale = 1.0;
2277 switch (c_sys[2])
2278 {
2279 case 'p':
2280 scale = [the_planet radius];
2281 break;
2282
2283 case 's':
2284 scale = [the_sun radius];
2285 break;
2286
2287 case 'u':
2288 scale = HPmagnitude(HPvector_subtract(p1, p0));
2289 break;
2290
2291 case 'm':
2292 scale = 1.0f;
2293 break;
2294
2295 default:
2296 return kZeroHPVector;
2297 }
2298 if (my_scalar)
2299 *my_scalar = scale;
2300
2301 // result = p0 + ijk
2302 HPVector result = p0; // origin
2303 result.x += scale * (pos.x * i.x + pos.y * j.x + pos.z * k.x);
2304 result.y += scale * (pos.x * i.y + pos.y * j.y + pos.z * k.y);
2305 result.z += scale * (pos.x * i.z + pos.y * j.z + pos.z * k.z);
2306
2307 return result;
2308}
2309
2310
2311- (NSString *) expressPosition:(HPVector) pos inCoordinateSystem:(NSString *) system
2312{
2313 HPVector result = [self legacyPositionFrom:pos asCoordinateSystem:system];
2314 return [NSString stringWithFormat:@"%@ %.2f %.2f %.2f", system, result.x, result.y, result.z];
2315}
2316
2317
2318- (HPVector) legacyPositionFrom:(HPVector) pos asCoordinateSystem:(NSString *) system
2319{
2320 NSString* l_sys = [system lowercaseString];
2321 if ([l_sys length] != 3)
2322 return kZeroHPVector;
2323 OOPlanetEntity* the_planet = [self planet];
2324 OOSunEntity* the_sun = [self sun];
2325 if (the_planet == nil || the_sun == nil || [l_sys isEqualToString:@"abs"])
2326 {
2327 return pos;
2328 }
2329 HPVector w_pos = [self getWitchspaceExitPosition]; // don't reset PRNG
2330 HPVector p_pos = the_planet->position;
2331 HPVector s_pos = the_sun->position;
2332
2333 const char* c_sys = [l_sys UTF8String];
2334 HPVector p0, p1, p2;
2335
2336 switch (c_sys[0])
2337 {
2338 case 'w':
2339 p0 = w_pos;
2340 switch (c_sys[1])
2341 {
2342 case 'p':
2343 p1 = p_pos; p2 = s_pos; break;
2344 case 's':
2345 p1 = s_pos; p2 = p_pos; break;
2346 default:
2347 return kZeroHPVector;
2348 }
2349 break;
2350 case 'p':
2351 p0 = p_pos;
2352 switch (c_sys[1])
2353 {
2354 case 'w':
2355 p1 = w_pos; p2 = s_pos; break;
2356 case 's':
2357 p1 = s_pos; p2 = w_pos; break;
2358 default:
2359 return kZeroHPVector;
2360 }
2361 break;
2362 case 's':
2363 p0 = s_pos;
2364 switch (c_sys[1])
2365 {
2366 case 'w':
2367 p1 = w_pos; p2 = p_pos; break;
2368 case 'p':
2369 p1 = p_pos; p2 = w_pos; break;
2370 default:
2371 return kZeroHPVector;
2372 }
2373 break;
2374 default:
2375 return kZeroHPVector;
2376 }
2377 HPVector k = HPvector_normal_or_zbasis(HPvector_subtract(p1, p0)); // 'z' axis in m
2378 HPVector v = HPvector_normal_or_xbasis(HPvector_subtract(p2, p0)); // temporary vector in plane of 'forward' and 'right'
2379
2380 HPVector j = HPcross_product(k, v); // 'y' axis in m
2381 HPVector i = HPcross_product(j, k); // 'x' axis in m
2382
2383 GLfloat scale = 1.0;
2384 switch (c_sys[2])
2385 {
2386 case 'p':
2387 {
2388 scale = 1.0f / [the_planet radius];
2389 break;
2390 }
2391 case 's':
2392 {
2393 scale = 1.0f / [the_sun radius];
2394 break;
2395 }
2396
2397 case 'u':
2398 scale = 1.0f / HPdistance(p1, p0);
2399 break;
2400
2401 case 'm':
2402 scale = 1.0f;
2403 break;
2404
2405 default:
2406 return kZeroHPVector;
2407 }
2408
2409 // result = p0 + ijk
2410 HPVector r_pos = HPvector_subtract(pos, p0);
2411 HPVector result = make_HPvector(scale * (r_pos.x * i.x + r_pos.y * i.y + r_pos.z * i.z),
2412 scale * (r_pos.x * j.x + r_pos.y * j.y + r_pos.z * j.z),
2413 scale * (r_pos.x * k.x + r_pos.y * k.y + r_pos.z * k.z) ); // scale * dot_products
2414
2415 return result;
2416}
2417
2418
2419- (HPVector) coordinatesFromCoordinateSystemString:(NSString *) system_x_y_z
2420{
2421 NSArray* tokens = ScanTokensFromString(system_x_y_z);
2422 if ([tokens count] != 4)
2423 {
2424 // Not necessarily an error.
2425 return make_HPvector(0,0,0);
2426 }
2427 GLfloat dummy;
2428 return [self coordinatesForPosition:make_HPvector([tokens oo_floatAtIndex:1], [tokens oo_floatAtIndex:2], [tokens oo_floatAtIndex:3]) withCoordinateSystem:[tokens oo_stringAtIndex:0] returningScalar:&dummy];
2429}
2430
2431
2432- (BOOL) addShipWithRole:(NSString *) desc nearPosition:(HPVector) pos withCoordinateSystem:(NSString *) system
2433{
2434 // initial position
2435 GLfloat scalar = 1.0;
2436 HPVector launchPos = [self coordinatesForPosition:pos withCoordinateSystem:system returningScalar:&scalar];
2437 // randomise
2438 GLfloat rfactor = scalar;
2439 if (rfactor > SCANNER_MAX_RANGE)
2440 rfactor = SCANNER_MAX_RANGE;
2441 if (rfactor < 1000)
2442 rfactor = 1000;
2443
2444 return ([self addShipWithRole:desc launchPos:launchPos rfactor:rfactor] != nil);
2445}
2446
2447
2448- (BOOL) addShips:(int) howMany withRole:(NSString *) desc atPosition:(HPVector) pos withCoordinateSystem:(NSString *) system
2449{
2450 // initial bounding box
2451 GLfloat scalar = 1.0;
2452 HPVector launchPos = [self coordinatesForPosition:pos withCoordinateSystem:system returningScalar:&scalar];
2453 GLfloat distance_from_center = 0.0;
2454 HPVector v_from_center, ship_pos;
2455 HPVector ship_positions[howMany];
2456 int i = 0;
2457 int scale_up_after = 0;
2458 int current_shell = 0;
2459 GLfloat walk_factor = 2.0;
2460 while (i < howMany)
2461 {
2462 ShipEntity *ship = [self addShipWithRole:desc launchPos:launchPos rfactor:0.0];
2463 if (ship == nil) return NO;
2464 OOScanClass scanClass = [ship scanClass];
2465 [ship setScanClass:CLASS_NO_DRAW]; // avoid lollipop flash
2466
2467 GLfloat safe_distance2 = ship->collision_radius * ship->collision_radius * SAFE_ADDITION_FACTOR2;
2468 BOOL safe;
2469 int limit_count = 8;
2470
2471 v_from_center = kZeroHPVector;
2472 do
2473 {
2474 do
2475 {
2476 v_from_center.x += walk_factor * (randf() - 0.5);
2477 v_from_center.y += walk_factor * (randf() - 0.5);
2478 v_from_center.z += walk_factor * (randf() - 0.5); // drunkards walk
2479 } while ((v_from_center.x == 0.0)&&(v_from_center.y == 0.0)&&(v_from_center.z == 0.0));
2480 v_from_center = HPvector_normal(v_from_center); // guaranteed non-zero
2481
2482 ship_pos = make_HPvector( launchPos.x + distance_from_center * v_from_center.x,
2483 launchPos.y + distance_from_center * v_from_center.y,
2484 launchPos.z + distance_from_center * v_from_center.z);
2485
2486 // check this position against previous ship positions in this shell
2487 safe = YES;
2488 int j = i - 1;
2489 while (safe && (j >= current_shell))
2490 {
2491 safe = (safe && (HPdistance2(ship_pos, ship_positions[j]) > safe_distance2));
2492 j--;
2493 }
2494 if (!safe)
2495 {
2496 limit_count--;
2497 if (!limit_count) // give up and expand the shell
2498 {
2499 limit_count = 8;
2500 distance_from_center += sqrt(safe_distance2); // expand to the next distance
2501 }
2502 }
2503
2504 } while (!safe);
2505
2506 [ship setPosition:ship_pos];
2507 [ship setScanClass:scanClass == CLASS_NOT_SET ? CLASS_NEUTRAL : scanClass];
2508
2509 Quaternion qr;
2511 [ship setOrientation:qr];
2512
2513 // [self addEntity:ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
2514
2515 ship_positions[i] = ship_pos;
2516 i++;
2517 if (i > scale_up_after)
2518 {
2519 current_shell = i;
2520 scale_up_after += 1 + 2 * i;
2521 distance_from_center += sqrt(safe_distance2); // fill the next shell
2522 }
2523 }
2524 return YES;
2525}
2526
2527
2528- (BOOL) addShips:(int) howMany withRole:(NSString *) desc nearPosition:(HPVector) pos withCoordinateSystem:(NSString *) system
2529{
2530 // initial bounding box
2531 GLfloat scalar = 1.0;
2532 HPVector launchPos = [self coordinatesForPosition:pos withCoordinateSystem:system returningScalar:&scalar];
2533 GLfloat rfactor = scalar;
2534 if (rfactor > SCANNER_MAX_RANGE)
2535 rfactor = SCANNER_MAX_RANGE;
2536 if (rfactor < 1000)
2537 rfactor = 1000;
2538 BoundingBox launch_bbox;
2539 bounding_box_reset_to_vector(&launch_bbox, make_vector(launchPos.x - rfactor, launchPos.y - rfactor, launchPos.z - rfactor));
2540 bounding_box_add_xyz(&launch_bbox, launchPos.x + rfactor, launchPos.y + rfactor, launchPos.z + rfactor);
2541
2542 return [self addShips: howMany withRole: desc intoBoundingBox: launch_bbox];
2543}
2544
2545
2546- (BOOL) addShips:(int) howMany withRole:(NSString *) desc nearPosition:(HPVector) pos withCoordinateSystem:(NSString *) system withinRadius:(GLfloat) radius
2547{
2548 // initial bounding box
2549 GLfloat scalar = 1.0;
2550 HPVector launchPos = [self coordinatesForPosition:pos withCoordinateSystem:system returningScalar:&scalar];
2551 GLfloat rfactor = radius;
2552 if (rfactor < 1000)
2553 rfactor = 1000;
2554 BoundingBox launch_bbox;
2555 bounding_box_reset_to_vector(&launch_bbox, make_vector(launchPos.x - rfactor, launchPos.y - rfactor, launchPos.z - rfactor));
2556 bounding_box_add_xyz(&launch_bbox, launchPos.x + rfactor, launchPos.y + rfactor, launchPos.z + rfactor);
2557
2558 return [self addShips: howMany withRole: desc intoBoundingBox: launch_bbox];
2559}
2560
2561
2562- (BOOL) addShips:(int) howMany withRole:(NSString *) desc intoBoundingBox:(BoundingBox) bbox
2563{
2564 if (howMany < 1)
2565 return YES;
2566 if (howMany > 1)
2567 {
2568 // divide the number of ships in two
2569 int h0 = howMany / 2;
2570 int h1 = howMany - h0;
2571 // split the bounding box into two along its longest dimension
2572 GLfloat lx = bbox.max.x - bbox.min.x;
2573 GLfloat ly = bbox.max.y - bbox.min.y;
2574 GLfloat lz = bbox.max.z - bbox.min.z;
2575 BoundingBox bbox0 = bbox;
2576 BoundingBox bbox1 = bbox;
2577 if ((lx > lz)&&(lx > ly)) // longest dimension is x
2578 {
2579 bbox0.min.x += 0.5 * lx;
2580 bbox1.max.x -= 0.5 * lx;
2581 }
2582 else
2583 {
2584 if (ly > lz) // longest dimension is y
2585 {
2586 bbox0.min.y += 0.5 * ly;
2587 bbox1.max.y -= 0.5 * ly;
2588 }
2589 else // longest dimension is z
2590 {
2591 bbox0.min.z += 0.5 * lz;
2592 bbox1.max.z -= 0.5 * lz;
2593 }
2594 }
2595 // place half the ships into each bounding box
2596 return ([self addShips: h0 withRole: desc intoBoundingBox: bbox0] && [self addShips: h1 withRole: desc intoBoundingBox: bbox1]);
2597 }
2598
2599 // randomise within the bounding box (biased towards the center of the box)
2600 HPVector pos = make_HPvector(bbox.min.x, bbox.min.y, bbox.min.z);
2601 pos.x += 0.5 * (randf() + randf()) * (bbox.max.x - bbox.min.x);
2602 pos.y += 0.5 * (randf() + randf()) * (bbox.max.y - bbox.min.y);
2603 pos.z += 0.5 * (randf() + randf()) * (bbox.max.z - bbox.min.z);
2604
2605 return ([self addShipWithRole:desc launchPos:pos rfactor:0.0] != nil);
2606}
2607
2608
2609- (BOOL) spawnShip:(NSString *) shipdesc
2610{
2611 // no need to do any more than log - enforcing modes wouldn't even have
2612 // loaded the legacy script
2613 OOStandardsDeprecated([NSString stringWithFormat:@"'spawn' via legacy script is deprecated as a way of adding ships for %@",shipdesc]);
2614
2615 ShipEntity *ship;
2616 NSDictionary *shipdict = nil;
2617
2618 shipdict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipdesc];
2619 if (shipdict == nil) return NO;
2620
2621 ship = [self newShipWithName:shipdesc]; // retain count is 1
2622
2623 if (ship == nil) return NO;
2624
2625 // set any spawning characteristics
2626 NSDictionary *spawndict = [shipdict oo_dictionaryForKey:@"spawn"];
2627 HPVector pos, rpos, spos;
2628 NSString *positionString = nil;
2629
2630 // position
2631 positionString = [spawndict oo_stringForKey:@"position"];
2632 if (positionString != nil)
2633 {
2634 if([positionString hasPrefix:@"abs "] && ([self planet] != nil || [self sun] !=nil))
2635 {
2636 OOLogWARN(@"script.deprecated", @"setting %@ for %@ '%@' in 'abs' inside .plists can cause compatibility issues across Oolite versions. Use coordinates relative to main system objects instead.",@"position",@"entity",shipdesc);
2637 }
2638
2639 pos = [self coordinatesFromCoordinateSystemString:positionString];
2640 }
2641 else
2642 {
2643 // without position defined, the ship will be added on top of the witchpoint buoy.
2645 OOLogERR(@"universe.spawnShip.error", @"***** ERROR: failed to find a spawn position for ship %@.", shipdesc);
2646 }
2647 [ship setPosition:pos];
2648
2649 // facing_position
2650 positionString = [spawndict oo_stringForKey:@"facing_position"];
2651 if (positionString != nil)
2652 {
2653 if([positionString hasPrefix:@"abs "] && ([self planet] != nil || [self sun] !=nil))
2654 {
2655 OOLogWARN(@"script.deprecated", @"setting %@ for %@ '%@' in 'abs' inside .plists can cause compatibility issues across Oolite versions. Use coordinates relative to main system objects instead.",@"facing_position",@"entity",shipdesc);
2656 }
2657
2658 spos = [ship position];
2659 Quaternion q1;
2660 rpos = [self coordinatesFromCoordinateSystemString:positionString];
2661 rpos = HPvector_subtract(rpos, spos); // position relative to ship
2662
2663 if (!HPvector_equal(rpos, kZeroHPVector))
2664 {
2665 rpos = HPvector_normal(rpos);
2666
2667 if (!HPvector_equal(rpos, HPvector_flip(kBasisZHPVector)))
2668 {
2669 q1 = quaternion_rotation_between(HPVectorToVector(rpos), kBasisZVector);
2670 }
2671 else
2672 {
2673 // for the inverse of the kBasisZVector the rotation is undefined, so we select one.
2674 q1 = make_quaternion(0,1,0,0);
2675 }
2676
2677
2678 [ship setOrientation:q1];
2679 }
2680 }
2681
2682 [self addEntity:ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
2683 [ship release];
2684
2685 return YES;
2686}
2687
2688
2689- (void) witchspaceShipWithPrimaryRole:(NSString *)role
2690{
2691 // adds a ship exiting witchspace (corollary of when ships leave the system)
2692 ShipEntity *ship = nil;
2693 NSDictionary *systeminfo = nil;
2694 OOGovernmentID government;
2695
2696 systeminfo = [self currentSystemData];
2697 government = [systeminfo oo_unsignedCharForKey:KEY_GOVERNMENT];
2698
2699 ship = [self newShipWithRole:role]; // retain count = 1
2700
2701 // Deal with scripted cargopods and ensure they are filled with something.
2702 if (ship && [ship hasRole:@"cargopod"])
2703 {
2704 [self fillCargopodWithRandomCargo:ship];
2705 }
2706
2707 if (ship)
2708 {
2709 if (([ship scanClass] == CLASS_NO_DRAW)||([ship scanClass] == CLASS_NOT_SET))
2710 [ship setScanClass: CLASS_NEUTRAL];
2711 if ([role isEqual:@"trader"])
2712 {
2713 [ship setCargoFlag: CARGO_FLAG_FULL_SCARCE];
2714 if ([ship hasRole:@"sunskim-trader"] && randf() < 0.25) // select 1/4 of the traders suitable for sunskimming.
2715 {
2716 [ship setCargoFlag: CARGO_FLAG_FULL_PLENTIFUL];
2717 [self makeSunSkimmer:ship andSetAI:YES];
2718 }
2719 else
2720 {
2721 [ship switchAITo:@"oolite-traderAI.js"];
2722 }
2723
2724 if (([ship pendingEscortCount] > 0)&&((Ranrot() % 7) < government)) // remove escorts if we feel safe
2725 {
2726 int nx = [ship pendingEscortCount] - 2 * (1 + (Ranrot() & 3)); // remove 2,4,6, or 8 escorts
2727 [ship setPendingEscortCount:(nx > 0) ? nx : 0];
2728 }
2729 }
2730 if ([role isEqual:@"pirate"])
2731 {
2732 [ship setCargoFlag: CARGO_FLAG_PIRATE];
2733 [ship setBounty: (Ranrot() & 7) + (Ranrot() & 7) + ((randf() < 0.05)? 63 : 23) withReason:kOOLegalStatusReasonSetup]; // they already have a price on their heads
2734 }
2735 if ([ship crew] == nil && ![ship isUnpiloted])
2736 [ship setCrew:[NSArray arrayWithObject:
2738 andOriginalSystem: Ranrot() & 255]]];
2739 // The following is set inside leaveWitchspace: AI state GLOBAL, STATUS_EXITING_WITCHSPACE, ai message: EXITED_WITCHSPACE, then STATUS_IN_FLIGHT
2741 [ship release];
2742 }
2743}
2744
2745
2746// adds a ship within the collision radius of the other entity
2747- (ShipEntity *) spawnShipWithRole:(NSString *) desc near:(Entity *) entity
2748{
2749 if (entity == nil) return nil;
2750
2751 ShipEntity *ship = nil;
2752 HPVector spawn_pos;
2753 Quaternion spawn_q;
2754 GLfloat offset = (randf() + randf()) * entity->collision_radius;
2755
2756 quaternion_set_random(&spawn_q);
2757 spawn_pos = HPvector_add([entity position], vectorToHPVector(vector_multiply_scalar(vector_forward_from_quaternion(spawn_q), offset)));
2758
2759 ship = [self addShipWithRole:desc launchPos:spawn_pos rfactor:0.0];
2760 [ship setOrientation:spawn_q];
2761
2762 return ship;
2763}
2764
2765
2766- (OOVisualEffectEntity *) addVisualEffectAt:(HPVector)pos withKey:(NSString *)key
2767{
2769
2770 // minimise the time between creating ship & assigning position.
2771
2772 OOVisualEffectEntity *vis = [self newVisualEffectWithName:key]; // is retained
2773 BOOL success = NO;
2774 if (vis != nil)
2775 {
2776 [vis setPosition:pos];
2777 [vis setOrientation:OORandomQuaternion()];
2778
2779 success = [self addEntity:vis]; // retained globally now
2780
2781 [vis release];
2782 }
2783 return success ? vis : (OOVisualEffectEntity *)nil;
2784
2786}
2787
2788
2789- (ShipEntity *) addShipAt:(HPVector)pos withRole:(NSString *)role withinRadius:(GLfloat)radius
2790{
2792
2793 // minimise the time between creating ship & assigning position.
2794 if (radius == NSNotFound)
2795 {
2796 GLfloat scalar = 1.0;
2798 // randomise
2799 GLfloat rfactor = scalar;
2800 if (rfactor > SCANNER_MAX_RANGE)
2801 rfactor = SCANNER_MAX_RANGE;
2802 if (rfactor < 1000)
2803 rfactor = 1000;
2804 pos.x += rfactor*(randf() - randf());
2805 pos.y += rfactor*(randf() - randf());
2806 pos.z += rfactor*(randf() - randf());
2807 }
2808 else
2809 {
2810 pos = HPvector_add(pos, OOHPVectorRandomSpatial(radius));
2811 }
2812
2813 ShipEntity *ship = [self newShipWithRole:role]; // is retained
2814 BOOL success = NO;
2815
2816 if (ship != nil)
2817 {
2818 [ship setPosition:pos];
2819 if ([ship hasRole:@"cargopod"]) [self fillCargopodWithRandomCargo:ship];
2820 OOScanClass scanClass = [ship scanClass];
2821 if (scanClass == CLASS_NOT_SET)
2822 {
2823 scanClass = CLASS_NEUTRAL;
2824 [ship setScanClass:scanClass];
2825 }
2826
2827 if ([ship crew] == nil && ![ship isUnpiloted])
2828 {
2829 [ship setCrew:[NSArray arrayWithObject:
2831 andOriginalSystem:Ranrot() & 255]]];
2832 }
2833
2834 [ship setOrientation:OORandomQuaternion()];
2835
2836 BOOL trader = [role isEqualToString:@"trader"];
2837 if (trader)
2838 {
2839 // half of traders created anywhere will now have cargo.
2840 if (randf() > 0.5f)
2841 {
2842 [ship setCargoFlag:(randf() < 0.66f ? CARGO_FLAG_FULL_PLENTIFUL : CARGO_FLAG_FULL_SCARCE)]; // most of them will carry the cargo produced in-system.
2843 }
2844
2845 uint8_t pendingEscortCount = [ship pendingEscortCount];
2846 if (pendingEscortCount > 0)
2847 {
2848 OOGovernmentID government = [[self currentSystemData] oo_unsignedCharForKey:KEY_GOVERNMENT];
2849 if ((Ranrot() % 7) < government) // remove escorts if we feel safe
2850 {
2851 int nx = pendingEscortCount - 2 * (1 + (Ranrot() & 3)); // remove 2,4,6, or 8 escorts
2852 [ship setPendingEscortCount:(nx > 0) ? nx : 0];
2853 }
2854 }
2855 }
2856
2857 if (HPdistance([self getWitchspaceExitPosition], pos) > SCANNER_MAX_RANGE)
2858 {
2859 // nothing extra to do
2860 success = [self addEntity:ship]; // STATUS_IN_FLIGHT, AI state GLOBAL - ship is retained globally
2861 }
2862 else // witchspace incoming traders & pirates need extra settings.
2863 {
2864 if (trader)
2865 {
2866 [ship setCargoFlag:CARGO_FLAG_FULL_SCARCE];
2867 if ([ship hasRole:@"sunskim-trader"] && randf() < 0.25)
2868 {
2869 [ship setCargoFlag:CARGO_FLAG_FULL_PLENTIFUL];
2870 [self makeSunSkimmer:ship andSetAI:YES];
2871 }
2872 else
2873 {
2874 [ship switchAITo:@"oolite-traderAI.js"];
2875 }
2876 }
2877 else if ([role isEqual:@"pirate"])
2878 {
2879 [ship setBounty:(Ranrot() & 7) + (Ranrot() & 7) + ((randf() < 0.05)? 63 : 23) withReason:kOOLegalStatusReasonSetup]; // they already have a price on their heads
2880 }
2881
2882 // Status changes inside the following call: AI state GLOBAL, then STATUS_EXITING_WITCHSPACE,
2883 // with the EXITED_WITCHSPACE message sent to the AI. At last we set STATUS_IN_FLIGHT.
2884 // Includes addEntity, so ship is retained globally.
2885 success = [ship witchspaceLeavingEffects];
2886 }
2887
2888 [ship release];
2889 }
2890 return success ? ship : (ShipEntity *)nil;
2891
2893}
2894
2895
2896- (NSArray *) addShipsAt:(HPVector)pos withRole:(NSString *)role quantity:(unsigned)count withinRadius:(GLfloat)radius asGroup:(BOOL)isGroup
2897{
2899
2900 NSMutableArray *ships = [NSMutableArray arrayWithCapacity:count];
2901 ShipEntity *ship = nil;
2902 OOShipGroup *group = nil;
2903
2904 if (isGroup)
2905 {
2906 group = [OOShipGroup groupWithName:[NSString stringWithFormat:@"%@ group", role]];
2907 }
2908
2909 while (count--)
2910 {
2911 ship = [self addShipAt:pos withRole:role withinRadius:radius];
2912 if (ship != nil)
2913 {
2914 // TODO: avoid collisions!!!
2915 if (isGroup) [ship setGroup:group];
2916 [ships addObject:ship];
2917 }
2918 }
2919
2920 if ([ships count] == 0) return nil;
2921
2922 return [[ships copy] autorelease];
2923
2925}
2926
2927
2928- (NSArray *) addShipsToRoute:(NSString *)route withRole:(NSString *)role quantity:(unsigned)count routeFraction:(double)routeFraction asGroup:(BOOL)isGroup
2929{
2930 NSMutableArray *ships = [NSMutableArray arrayWithCapacity:count];
2931 ShipEntity *ship = nil;
2932 Entity<OOStellarBody> *entity = nil;
2933 HPVector pos = kZeroHPVector, direction = kZeroHPVector, point0 = kZeroHPVector, point1 = kZeroHPVector;
2934 double radius = 0;
2935
2936 if ([route isEqualToString:@"pw"] || [route isEqualToString:@"sw"] || [route isEqualToString:@"ps"])
2937 {
2938 routeFraction = 1.0f - routeFraction;
2939 }
2940
2941 // which route is it?
2942 if ([route isEqualTo:@"wp"] || [route isEqualTo:@"pw"])
2943 {
2944 point0 = [self getWitchspaceExitPosition];
2945 entity = [self planet];
2946 if (entity == nil) return nil;
2947 point1 = [entity position];
2948 radius = [entity radius];
2949 }
2950 else if ([route isEqualTo:@"ws"] || [route isEqualTo:@"sw"])
2951 {
2952 point0 = [self getWitchspaceExitPosition];
2953 entity = [self sun];
2954 if (entity == nil) return nil;
2955 point1 = [entity position];
2956 radius = [entity radius];
2957 }
2958 else if ([route isEqualTo:@"sp"] || [route isEqualTo:@"ps"])
2959 {
2960 entity = [self sun];
2961 if (entity == nil) return nil;
2962 point0 = [entity position];
2963 double radius0 = [entity radius];
2964
2965 entity = [self planet];
2966 if (entity == nil) return nil;
2967 point1 = [entity position];
2968 radius = [entity radius];
2969
2970 // shorten the route by scanner range & sun radius, otherwise ships could be created inside it.
2971 direction = HPvector_normal(HPvector_subtract(point0, point1));
2972 point0 = HPvector_subtract(point0, HPvector_multiply_scalar(direction, radius0 + SCANNER_MAX_RANGE * 1.1f));
2973 }
2974 else if ([route isEqualTo:@"st"])
2975 {
2976 point0 = [self getWitchspaceExitPosition];
2977 if ([self station] == nil) return nil;
2978 point1 = [[self station] position];
2979 radius = [[self station] collisionRadius];
2980 }
2981 else return nil; // no route specifier? We shouldn't be here!
2982
2983 // shorten the route by scanner range & radius, otherwise ships could be created inside the route destination.
2984 direction = HPvector_normal(HPvector_subtract(point1, point0));
2985 point1 = HPvector_subtract(point1, HPvector_multiply_scalar(direction, radius + SCANNER_MAX_RANGE * 1.1f));
2986
2987 pos = [self fractionalPositionFrom:point0 to:point1 withFraction:routeFraction];
2988 if(isGroup)
2989 {
2990 return [self addShipsAt:pos withRole:role quantity:count withinRadius:(SCANNER_MAX_RANGE / 10.0f) asGroup:YES];
2991 }
2992 else
2993 {
2994 while (count--)
2995 {
2996 ship = [self addShipAt:pos withRole:role withinRadius:0]; // no radius because pos is already randomised with SCANNER_MAX_RANGE.
2997 if (ship != nil) [ships addObject:ship];
2998 if (count > 0) pos = [self fractionalPositionFrom:point0 to:point1 withFraction:routeFraction];
2999 }
3000
3001 if ([ships count] == 0) return nil;
3002 }
3003
3004 return [[ships copy] autorelease];
3005}
3006
3007
3008- (BOOL) roleIsPirateVictim:(NSString *)role
3009{
3010 return [self role:role isInCategory:@"oolite-pirate-victim"];
3011}
3012
3013
3014- (BOOL) role:(NSString *)role isInCategory:(NSString *)category
3015{
3016 NSSet *categoryInfo = [roleCategories objectForKey:category];
3017 if (categoryInfo == nil)
3018 {
3019 return NO;
3020 }
3021 return [categoryInfo containsObject:role];
3022}
3023
3024
3025// used to avoid having lost escorts when player advances clock while docked
3027{
3028 unsigned i;
3029 for (i = 0; i < n_entities; i++)
3030 {
3031 if (sortedEntities[i]->isShip)
3032 {
3033 ShipEntity *my_ship = (ShipEntity*)sortedEntities[i];
3034 Entity* my_target = [my_ship primaryTarget];
3035 if ([my_target isWormhole])
3036 {
3038 }
3039 else if ([[[my_ship getAI] state] isEqualToString:@"ENTER_WORMHOLE"])
3040 {
3042 }
3043 }
3044 }
3045}
3046
3047
3048- (void) addWitchspaceJumpEffectForShip:(ShipEntity *)ship
3049{
3050 // don't add rings when system is being populated
3051 if ([PLAYER status] != STATUS_ENTERING_WITCHSPACE && [PLAYER status] != STATUS_EXITING_WITCHSPACE)
3052 {
3055 }
3056}
3057
3058
3060{
3061 for (unsigned i = 0; i < n_entities; i++)
3062 {
3063 Entity *e2 = sortedEntities[i];
3064 if ([e2 isShip] && [(ShipEntity*)e2 hasPrimaryRole:@"buoy-witchpoint"])
3065 {
3067 }
3068 }
3069 return MIN_DISTANCE_TO_BUOY;
3070}
3071
3072
3073- (void) setUpBreakPattern:(HPVector) pos orientation:(Quaternion) q forDocking:(BOOL) forDocking
3074{
3075 int i;
3076 OOBreakPatternEntity *ring = nil;
3077 id colorDesc = nil;
3078 OOColor *color = nil;
3079
3080 [self setViewDirection:VIEW_FORWARD];
3081
3082 q.w = -q.w; // reverse the quaternion because this is from the player's viewpoint
3083
3084 Vector v = vector_forward_from_quaternion(q);
3085 Vector vel = vector_multiply_scalar(v, -BREAK_PATTERN_RING_SPEED);
3086
3087 // hyperspace colours
3088
3089 OOColor *col1 = [OOColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.5]; //standard tunnel colour
3090 OOColor *col2 = [OOColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:0.25]; //standard tunnel colour
3091
3092 colorDesc = [[self globalSettings] objectForKey:@"hyperspace_tunnel_color_1"];
3093 if (colorDesc != nil)
3094 {
3095 color = [OOColor colorWithDescription:colorDesc];
3096 if (color != nil) col1 = color;
3097 else OOLogWARN(@"hyperspaceTunnel.fromDict", @"could not interpret \"%@\" as a colour.", colorDesc);
3098 }
3099
3100 colorDesc = [[self globalSettings] objectForKey:@"hyperspace_tunnel_color_2"];
3101 if (colorDesc != nil)
3102 {
3103 color = [OOColor colorWithDescription:colorDesc];
3104 if (color != nil) col2 = color;
3105 else OOLogWARN(@"hyperspaceTunnel.fromDict", @"could not interpret \"%@\" as a colour.", colorDesc);
3106 }
3107
3108 unsigned sides = kOOBreakPatternMaxSides;
3109 GLfloat startAngle = 0;
3110 GLfloat aspectRatio = 1;
3111
3112 if (forDocking)
3113 {
3114 NSDictionary *info = [[PLAYER dockedStation] shipInfoDictionary];
3115 sides = [info oo_unsignedIntForKey:@"tunnel_corners" defaultValue:4];
3116 startAngle = [info oo_floatForKey:@"tunnel_start_angle" defaultValue:45.0f];
3117 aspectRatio = [info oo_floatForKey:@"tunnel_aspect_ratio" defaultValue:2.67f];
3118 }
3119
3120 for (i = 1; i < 11; i++)
3121 {
3122 ring = [OOBreakPatternEntity breakPatternWithPolygonSides:sides startAngle:startAngle aspectRatio:aspectRatio];
3123 if (!forDocking)
3124 {
3125 [ring setInnerColor:col1 outerColor:col2];
3126 }
3127
3128 Vector offset = vector_multiply_scalar(v, i * BREAK_PATTERN_RING_SPACING);
3129 [ring setPosition:HPvector_add(pos, vectorToHPVector(offset))]; // ahead of the player
3130 [ring setOrientation:q];
3131 [ring setVelocity:vel];
3132 [ring setLifetime:i * BREAK_PATTERN_RING_SPACING];
3133
3134 // FIXME: better would be to have break pattern timing not depend on
3135 // these ring objects existing in the first place. - CIM
3136 if (forDocking && ![[PLAYER dockedStation] hasBreakPattern])
3137 {
3138 ring->isImmuneToBreakPatternHide = NO;
3139 }
3140 else if (!forDocking && ![self witchspaceBreakPattern])
3141 {
3142 ring->isImmuneToBreakPatternHide = NO;
3143 }
3144 [self addEntity:ring];
3146 }
3147}
3148
3149
3151{
3153}
3154
3155
3156- (void) setWitchspaceBreakPattern:(BOOL)newValue
3157{
3158 _witchspaceBreakPattern = !!newValue;
3159}
3160
3161
3166
3167
3168- (void) setDockingClearanceProtocolActive:(BOOL)newValue
3169{
3171 NSEnumerator *statEnum = [allStations objectEnumerator];
3173
3174 /* CIM: picking a random ship type which can take the same primary
3175 * role as the station to determine whether it has no set docking
3176 * clearance requirements seems unlikely to work entirely
3177 * correctly. To be fixed. */
3178
3179 while ((station = [statEnum nextObject]))
3180 {
3181 NSString *stationKey = [registry randomShipKeyForRole:[station primaryRole]];
3182 if (![[[registry shipInfoForKey:stationKey] allKeys] containsObject:@"requires_docking_clearance"])
3183 {
3185 }
3186 }
3187
3189}
3190
3191
3193{
3194 if ([[self gameController] playerFileToLoad])
3195 {
3197 }
3198 else
3199 {
3200 [self setUseAddOns:SCENARIO_OXP_DEFINITION_ALL fromSaveGame:NO forceReinit:YES]; // calls reinitAndShowDemo
3201 }
3202}
3203
3204
3205- (void) setupIntroFirstGo:(BOOL)justCobra
3206{
3207 PlayerEntity *player = PLAYER;
3208 ShipEntity *ship = nil;
3209 Quaternion q2 = { 0.0f, 0.0f, 1.0f, 0.0f }; // w,x,y,z
3210
3211 // in status demo draw ships and display text
3212 if (!justCobra)
3213 {
3216 // always, even if it's the cobra, because it's repositioned
3217 [self removeDemoShips];
3218 }
3219 if (justCobra)
3220 {
3221 [player setStatus: STATUS_START_GAME];
3222 }
3223 [player setShowDemoShips: YES];
3224 displayGUI = YES;
3225
3226 if (justCobra)
3227 {
3228 /*- cobra - intro1 -*/
3229 ship = [self newShipWithName:PLAYER_SHIP_DESC usePlayerProxy:YES];
3230 }
3231 else
3232 {
3233 /*- demo ships - intro2 -*/
3234
3235 demo_ship_index = 0;
3237
3238 /* Try to set the initial list position to Cobra III if
3239 * available, and at least the Ships category. */
3240 NSArray *subList = nil;
3241 foreach (subList, demo_ships)
3242 {
3243 if ([[[subList oo_dictionaryAtIndex:0] oo_stringForKey:kOODemoShipClass] isEqualToString:@"ship"])
3244 {
3245 demo_ship_index = [demo_ships indexOfObject:subList];
3246 NSDictionary *shipEntry = nil;
3247 foreach (shipEntry, subList)
3248 {
3249 if ([[shipEntry oo_stringForKey:kOODemoShipKey] isEqualToString:@"cobra3-trader"])
3250 {
3251 demo_ship_subindex = [subList indexOfObject:shipEntry];
3252 break;
3253 }
3254 }
3255 break;
3256 }
3257 }
3258
3259
3260 if (!demo_ship) ship = [self newShipWithName:[[[demo_ships oo_arrayAtIndex:demo_ship_index] oo_dictionaryAtIndex:demo_ship_subindex] oo_stringForKey:kOODemoShipKey] usePlayerProxy:NO];
3261 // stop consistency problems on the ship library screen
3262 [ship removeEquipmentItem:@"EQ_SHIELD_BOOSTER"];
3263 [ship removeEquipmentItem:@"EQ_SHIELD_ENHANCER"];
3264 }
3265
3266 if (ship)
3267 {
3268 [ship setOrientation:q2];
3269 if (!justCobra)
3270 {
3271 [ship setPositionX:0.0f y:0.0f z:DEMO2_VANISHING_DISTANCE * ship->collision_radius * 0.01];
3272 [ship setDestination: ship->position]; // ideal position
3273 }
3274 else
3275 {
3276 // main screen Cobra is closer
3277 [ship setPositionX:0.0f y:0.0f z:3.6 * ship->collision_radius];
3278 }
3279 [ship setDemoShip: 1.0f];
3280 [ship setDemoStartTime: universal_time];
3281 [ship setScanClass: CLASS_NO_DRAW];
3282 [ship switchAITo:@"nullAI.plist"];
3283 if([ship pendingEscortCount] > 0) [ship setPendingEscortCount:0];
3284 [self addEntity:ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
3285 // now override status
3286 [ship setStatus:STATUS_COCKPIT_DISPLAY];
3287 demo_ship = ship;
3288
3289 [ship release];
3290 }
3291
3292 if (!justCobra)
3293 {
3294// [gui setText:[demo_ship displayName] forRow:19 align:GUI_ALIGN_CENTER];
3296 }
3297
3299 if (!justCobra)
3300 {
3303 }
3304}
3305
3306
3307- (NSDictionary *)demoShipData
3308{
3309 return [[demo_ships oo_arrayAtIndex:demo_ship_index] oo_dictionaryAtIndex:demo_ship_subindex];
3310}
3311
3312
3314{
3315 OOGUITabSettings tab_stops;
3316 tab_stops[0] = 0;
3317 tab_stops[1] = 170;
3318 tab_stops[2] = 340;
3319 [gui setTabStops:tab_stops];
3320
3321/* [gui setText:[demo_ship displayName] forRow:19 align:GUI_ALIGN_CENTER];
3322 [gui setColor:[OOColor whiteColor] forRow:19]; */
3323
3324 NSDictionary *librarySettings = [self demoShipData];
3325
3326 OOGUIRow descRow = 7;
3327
3328 NSString *field1 = nil;
3329 NSString *field2 = nil;
3330 NSString *field3 = nil;
3331 NSString *override = nil;
3332
3333 // clear rows
3334 for (NSUInteger i=1;i<=26;i++)
3335 {
3336 [gui setText:@"" forRow:i];
3337 }
3338
3339 /* Row 1: ScanClass, Name, Summary */
3340 override = [librarySettings oo_stringForKey:kOODemoShipClass defaultValue:@"ship"];
3341 field1 = OOShipLibraryCategorySingular(override);
3342
3343
3344 field2 = [demo_ship shipClassName];
3345
3346
3347 override = [librarySettings oo_stringForKey:kOODemoShipSummary defaultValue:nil];
3348 if (override != nil)
3349 {
3350 field3 = OOExpand(override);
3351 }
3352 else
3353 {
3354 field3 = @"";
3355 }
3356 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:1];
3357 [gui setColor:[OOColor greenColor] forRow:1];
3358
3359 // ship_data defaults to true for "ship" class, false for everything else
3360 if (![librarySettings oo_boolForKey:kOODemoShipShipData defaultValue:[[librarySettings oo_stringForKey:kOODemoShipClass defaultValue:@"ship"] isEqualToString:@"ship"]])
3361 {
3362 descRow = 3;
3363 }
3364 else
3365 {
3366 /* Row 2: Speed, Turn Rate, Cargo */
3367
3368 override = [librarySettings oo_stringForKey:kOODemoShipSpeed defaultValue:nil];
3369 if (override != nil)
3370 {
3371 if ([override length] == 0)
3372 {
3373 field1 = @"";
3374 }
3375 else
3376 {
3377 field1 = [NSString stringWithFormat:DESC(@"oolite-ship-library-speed-custom"),OOExpand(override)];
3378 }
3379 }
3380 else
3381 {
3382 field1 = OOShipLibrarySpeed(demo_ship);
3383 }
3384
3385
3386 override = [librarySettings oo_stringForKey:kOODemoShipTurnRate defaultValue:nil];
3387 if (override != nil)
3388 {
3389 if ([override length] == 0)
3390 {
3391 field2 = @"";
3392 }
3393 else
3394 {
3395 field2 = [NSString stringWithFormat:DESC(@"oolite-ship-library-turn-custom"),OOExpand(override)];
3396 }
3397 }
3398 else
3399 {
3400 field2 = OOShipLibraryTurnRate(demo_ship);
3401 }
3402
3403
3404 override = [librarySettings oo_stringForKey:kOODemoShipCargo defaultValue:nil];
3405 if (override != nil)
3406 {
3407 if ([override length] == 0)
3408 {
3409 field3 = @"";
3410 }
3411 else
3412 {
3413 field3 = [NSString stringWithFormat:DESC(@"oolite-ship-library-cargo-custom"),OOExpand(override)];
3414 }
3415 }
3416 else
3417 {
3418 field3 = OOShipLibraryCargo(demo_ship);
3419 }
3420
3421
3422 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:3];
3423
3424 /* Row 3: recharge rate, energy banks, witchspace */
3425 override = [librarySettings oo_stringForKey:kOODemoShipGenerator defaultValue:nil];
3426 if (override != nil)
3427 {
3428 if ([override length] == 0)
3429 {
3430 field1 = @"";
3431 }
3432 else
3433 {
3434 field1 = [NSString stringWithFormat:DESC(@"oolite-ship-library-generator-custom"),OOExpand(override)];
3435 }
3436 }
3437 else
3438 {
3439 field1 = OOShipLibraryGenerator(demo_ship);
3440 }
3441
3442
3443 override = [librarySettings oo_stringForKey:kOODemoShipShields defaultValue:nil];
3444 if (override != nil)
3445 {
3446 if ([override length] == 0)
3447 {
3448 field2 = @"";
3449 }
3450 else
3451 {
3452 field2 = [NSString stringWithFormat:DESC(@"oolite-ship-library-shields-custom"),OOExpand(override)];
3453 }
3454 }
3455 else
3456 {
3457 field2 = OOShipLibraryShields(demo_ship);
3458 }
3459
3460
3461 override = [librarySettings oo_stringForKey:kOODemoShipWitchspace defaultValue:nil];
3462 if (override != nil)
3463 {
3464 if ([override length] == 0)
3465 {
3466 field3 = @"";
3467 }
3468 else
3469 {
3470 field3 = [NSString stringWithFormat:DESC(@"oolite-ship-library-witchspace-custom"),OOExpand(override)];
3471 }
3472 }
3473 else
3474 {
3475 field3 = OOShipLibraryWitchspace(demo_ship);
3476 }
3477
3478
3479 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:4];
3480
3481
3482 /* Row 4: weapons, turrets, size */
3483 override = [librarySettings oo_stringForKey:kOODemoShipWeapons defaultValue:nil];
3484 if (override != nil)
3485 {
3486 if ([override length] == 0)
3487 {
3488 field1 = @"";
3489 }
3490 else
3491 {
3492 field1 = [NSString stringWithFormat:DESC(@"oolite-ship-library-weapons-custom"),OOExpand(override)];
3493 }
3494 }
3495 else
3496 {
3497 field1 = OOShipLibraryWeapons(demo_ship);
3498 }
3499
3500 override = [librarySettings oo_stringForKey:kOODemoShipTurrets defaultValue:nil];
3501 if (override != nil)
3502 {
3503 if ([override length] == 0)
3504 {
3505 field2 = @"";
3506 }
3507 else
3508 {
3509 field2 = [NSString stringWithFormat:DESC(@"oolite-ship-library-turrets-custom"),OOExpand(override)];
3510 }
3511 }
3512 else
3513 {
3514 field2 = OOShipLibraryTurrets(demo_ship);
3515 }
3516
3517 override = [librarySettings oo_stringForKey:kOODemoShipSize defaultValue:nil];
3518 if (override != nil)
3519 {
3520 if ([override length] == 0)
3521 {
3522 field3 = @"";
3523 }
3524 else
3525 {
3526 field3 = [NSString stringWithFormat:DESC(@"oolite-ship-library-size-custom"),OOExpand(override)];
3527 }
3528 }
3529 else
3530 {
3531 field3 = OOShipLibrarySize(demo_ship);
3532 }
3533
3534 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:5];
3535 }
3536
3537 override = [librarySettings oo_stringForKey:kOODemoShipDescription defaultValue:nil];
3538 if (override != nil)
3539 {
3540 [gui addLongText:OOExpand(override) startingAtRow:descRow align:GUI_ALIGN_LEFT];
3541 }
3542
3543
3544 // line 19: ship categories
3545 field1 = [NSString stringWithFormat:@"<-- %@",OOShipLibraryCategoryPlural([[[demo_ships objectAtIndex:((demo_ship_index+[demo_ships count]-1)%[demo_ships count])] objectAtIndex:0] oo_stringForKey:kOODemoShipClass])];
3546 field2 = OOShipLibraryCategoryPlural([[[demo_ships objectAtIndex:demo_ship_index] objectAtIndex:0] oo_stringForKey:kOODemoShipClass]);
3547 field3 = [NSString stringWithFormat:@"%@ -->",OOShipLibraryCategoryPlural([[[demo_ships objectAtIndex:((demo_ship_index+1)%[demo_ships count])] objectAtIndex:0] oo_stringForKey:kOODemoShipClass])];
3548
3549 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:19];
3550 [gui setColor:[OOColor greenColor] forRow:19];
3551
3552 // lines 21-25: ship names
3553 NSArray *subList = [demo_ships objectAtIndex:demo_ship_index];
3554 NSUInteger i,start = demo_ship_subindex - (demo_ship_subindex%5);
3555 NSUInteger end = start + 4;
3556 if (end >= [subList count])
3557 {
3558 end = [subList count] - 1;
3559 }
3560 OOGUIRow row = 21;
3561 field1 = @"";
3562 field3 = @"";
3563 for (i = start ; i <= end ; i++)
3564 {
3565 field2 = [[subList objectAtIndex:i] oo_stringForKey:kOODemoShipName];
3566 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:row];
3567 if (i == demo_ship_subindex)
3568 {
3569 [gui setColor:[OOColor yellowColor] forRow:row];
3570 }
3571 else
3572 {
3573 [gui setColor:[OOColor whiteColor] forRow:row];
3574 }
3575 row++;
3576 }
3577
3578 field2 = @"...";
3579 if (start > 0)
3580 {
3581 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:20];
3582 [gui setColor:[OOColor whiteColor] forRow:20];
3583 }
3584 if (end < [subList count]-1)
3585 {
3586 [gui setArray:[NSArray arrayWithObjects:field1,field2,field3,nil] forRow:26];
3587 [gui setColor:[OOColor whiteColor] forRow:26];
3588 }
3589
3590}
3591
3592
3594{
3596 NSUInteger subcount = [[demo_ships objectAtIndex:demo_ship_index] count];
3597 demo_ship_subindex = (demo_ship_subindex + subcount - 2) % subcount;
3598 demo_stage_time = universal_time - 1.0; // force change
3599}
3600
3601
3603{
3605 demo_ship_index = (demo_ship_index + [demo_ships count] - 1) % [demo_ships count];
3606 demo_ship_subindex = [[demo_ships objectAtIndex:demo_ship_index] count] - 1;
3607 demo_stage_time = universal_time - 1.0; // force change
3608}
3609
3610
3612{
3615 demo_ship_subindex = [[demo_ships objectAtIndex:demo_ship_index] count] - 1;
3616 demo_stage_time = universal_time - 1.0; // force change
3617}
3618
3619
3621{
3623 demo_stage_time = universal_time - 1.0; // force change
3624}
3625
3626
3627static BOOL IsCandidateMainStationPredicate(Entity *entity, void *parameter)
3628{
3629 return [entity isStation] && !entity->isExplicitlyNotMainStation;
3630}
3631
3632
3633static BOOL IsFriendlyStationPredicate(Entity *entity, void *parameter)
3634{
3635 return [entity isStation] && ![(ShipEntity *)entity isHostileTo:parameter];
3636}
3637
3638
3640{
3641 if (cachedSun != nil && cachedStation == nil)
3642 {
3643 cachedStation = [self findOneEntityMatchingPredicate:IsCandidateMainStationPredicate
3644 parameter:nil];
3645 }
3646 return cachedStation;
3647}
3648
3649
3650- (StationEntity *) stationWithRole:(NSString *)role andPosition:(HPVector)position
3651{
3652 if ([role isEqualToString:@""])
3653 {
3654 return nil;
3655 }
3656
3657 float range = 1000000; // allow a little variation in position
3658
3659 NSArray *stations = [self stations];
3661 foreach (station, stations)
3662 {
3663 if (HPdistance2(position,[station position]) < range)
3664 {
3665 if ([[station primaryRole] isEqualToString:role])
3666 {
3667 return station;
3668 }
3669 }
3670 }
3671 return nil;
3672}
3673
3674
3675- (StationEntity *) stationFriendlyTo:(ShipEntity *) ship
3676{
3677 // In interstellar space we select a random friendly carrier as mainStation.
3678 // No caching: friendly status can change!
3679 return [self findOneEntityMatchingPredicate:IsFriendlyStationPredicate parameter:ship];
3680}
3681
3682
3683- (OOPlanetEntity *) planet
3684{
3685 if (cachedPlanet == nil && [allPlanets count] > 0)
3686 {
3687 cachedPlanet = [allPlanets objectAtIndex:0];
3688 }
3689 return cachedPlanet;
3690}
3691
3692
3694{
3695 if (cachedSun == nil)
3696 {
3697 cachedSun = [self findOneEntityMatchingPredicate:IsSunPredicate parameter:nil];
3698 }
3699 return cachedSun;
3700}
3701
3702
3703- (NSArray *) planets
3704{
3705 return allPlanets;
3706}
3707
3708
3709- (NSArray *) stations
3710{
3711 return [allStations allObjects];
3712}
3713
3714
3715- (NSArray *) wormholes
3716{
3717 return activeWormholes;
3718}
3719
3720
3722{
3723 /* During the demo screens, the player must remain docked in order for the
3724 UI to work. This means either enforcing invulnerability or launching
3725 the player when the station is destroyed even if on the "new game Y/N"
3726 screen.
3727
3728 The latter is a) weirder and b) harder. If your OXP relies on being
3729 able to destroy the main station before the game has even started,
3730 your OXP sucks.
3731 */
3732 OOEntityStatus playerStatus = [PLAYER status];
3733 if (playerStatus == STATUS_START_GAME) return;
3734
3735 StationEntity *theStation = [self station];
3736 if (theStation != nil) theStation->isExplicitlyNotMainStation = YES;
3738}
3739
3740
3742{
3743 Entity <OOBeaconEntity> *beaconShip = [self firstBeacon], *next = nil;
3744 while (beaconShip)
3745 {
3746 next = [beaconShip nextBeacon];
3747 [beaconShip setPrevBeacon:nil];
3748 [beaconShip setNextBeacon:nil];
3749 beaconShip = next;
3750 }
3751
3752 [self setFirstBeacon:nil];
3753 [self setLastBeacon:nil];
3754}
3755
3756
3757- (Entity <OOBeaconEntity> *) firstBeacon
3758{
3760}
3761
3762
3763- (void) setFirstBeacon:(Entity <OOBeaconEntity> *)beacon
3764{
3765 if (beacon != [self firstBeacon])
3766 {
3767 [beacon setPrevBeacon:nil];
3768 [beacon setNextBeacon:[self firstBeacon]];
3769 [[self firstBeacon] setPrevBeacon:beacon];
3770 [_firstBeacon release];
3771 _firstBeacon = [beacon weakRetain];
3772 }
3773}
3774
3775
3776- (Entity <OOBeaconEntity> *) lastBeacon
3777{
3779}
3780
3781
3782- (void) setLastBeacon:(Entity <OOBeaconEntity> *)beacon
3783{
3784 if (beacon != [self lastBeacon])
3785 {
3786 [beacon setNextBeacon:nil];
3787 [beacon setPrevBeacon:[self lastBeacon]];
3788 [[self lastBeacon] setNextBeacon:beacon];
3789 [_lastBeacon release];
3790 _lastBeacon = [beacon weakRetain];
3791 }
3792}
3793
3794
3795- (void) setNextBeacon:(Entity <OOBeaconEntity> *) beaconShip
3796{
3797 if ([beaconShip isBeacon])
3798 {
3799 [self setLastBeacon:beaconShip];
3800 if ([self firstBeacon] == nil) [self setFirstBeacon:beaconShip];
3801 }
3802 else
3803 {
3804 OOLog(@"universe.beacon.error", @"***** ERROR: Universe setNextBeacon '%@'. The ship has no beacon code set.", beaconShip);
3805 }
3806}
3807
3808
3809- (void) clearBeacon:(Entity <OOBeaconEntity> *) beaconShip
3810{
3811 Entity <OOBeaconEntity> *tmp = nil;
3812
3813 if ([beaconShip isBeacon])
3814 {
3815 if ([self firstBeacon] == beaconShip)
3816 {
3817 tmp = [[beaconShip nextBeacon] nextBeacon];
3818 [self setFirstBeacon:[beaconShip nextBeacon]];
3819 [[beaconShip prevBeacon] setNextBeacon:tmp];
3820 }
3821 else if ([self lastBeacon] == beaconShip)
3822 {
3823 tmp = [[beaconShip prevBeacon] prevBeacon];
3824 [self setLastBeacon:[beaconShip prevBeacon]];
3825 [[beaconShip nextBeacon] setPrevBeacon:tmp];
3826 }
3827 else
3828 {
3829 [[beaconShip nextBeacon] setPrevBeacon:[beaconShip prevBeacon]];
3830 [[beaconShip prevBeacon] setNextBeacon:[beaconShip nextBeacon]];
3831 }
3832 [beaconShip setBeaconCode:nil];
3833 }
3834}
3835
3836
3837- (NSDictionary *) currentWaypoints
3838{
3839 return waypoints;
3840}
3841
3842
3843- (void) defineWaypoint:(NSDictionary *)definition forKey:(NSString *)key
3844{
3845 OOWaypointEntity *waypoint = nil;
3846 BOOL preserveCompass = NO;
3847 waypoint = [waypoints objectForKey:key];
3848 if (waypoint != nil)
3849 {
3850 if ([PLAYER compassTarget] == waypoint)
3851 {
3852 preserveCompass = YES;
3853 }
3854 [self removeEntity:waypoint];
3855 [waypoints removeObjectForKey:key];
3856 }
3857 if (definition != nil)
3858 {
3859 waypoint = [OOWaypointEntity waypointWithDictionary:definition];
3860 if (waypoint != nil)
3861 {
3862 [self addEntity:waypoint];
3863 [waypoints setObject:waypoint forKey:key];
3864 if (preserveCompass)
3865 {
3866 [PLAYER setCompassTarget:waypoint];
3867 [PLAYER setNextBeacon:waypoint];
3868 }
3869 }
3870 }
3871}
3872
3873
3874- (GLfloat *) skyClearColor
3875{
3876 return skyClearColor;
3877}
3878
3879
3880- (void) setSkyColorRed:(GLfloat)red green:(GLfloat)green blue:(GLfloat)blue alpha:(GLfloat)alpha
3881{
3882 skyClearColor[0] = red;
3883 skyClearColor[1] = green;
3884 skyClearColor[2] = blue;
3885 skyClearColor[3] = alpha;
3886 [self setAirResistanceFactor:alpha];
3887}
3888
3889
3891{
3892 return (breakPatternCounter == 0);
3893}
3894
3895
3897{
3898 Entity* player = PLAYER;
3899 return ((breakPatternCounter > 5)||(!player)||([player status] == STATUS_DOCKING));
3900}
3901
3902
3903#define PROFILE_SHIP_SELECTION 0
3904
3905
3906- (BOOL) canInstantiateShip:(NSString *)shipKey
3907{
3908 NSDictionary *shipInfo = nil;
3909 NSArray *conditions = nil;
3910 NSString *condition_script = nil;
3911 shipInfo = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
3912
3913 condition_script = [shipInfo oo_stringForKey:@"condition_script"];
3914 if (condition_script != nil)
3915 {
3916 OOJSScript *condScript = [self getConditionScript:condition_script];
3917 if (condScript != nil) // should always be non-nil, but just in case
3918 {
3919 JSContext *context = OOJSAcquireContext();
3920 BOOL OK;
3921 JSBool allow_instantiation;
3922 jsval result;
3923 jsval args[] = { OOJSValueFromNativeObject(context, shipKey) };
3924
3925 OK = [condScript callMethod:OOJSID("allowSpawnShip")
3926 inContext:context
3927 withArguments:args count:sizeof args / sizeof *args
3928 result:&result];
3929
3930 if (OK) OK = JS_ValueToBoolean(context, result, &allow_instantiation);
3931
3932 OOJSRelinquishContext(context);
3933
3934 if (OK && !allow_instantiation)
3935 {
3936 /* if the script exists, the function exists, the function
3937 * returns a bool, and that bool is false, block
3938 * instantiation. Otherwise allow it as default */
3939 return NO;
3940 }
3941 }
3942 }
3943
3944 conditions = [shipInfo oo_arrayForKey:@"conditions"];
3945 if (conditions == nil) return YES;
3946
3947 // Check conditions
3948 return [PLAYER scriptTestConditions:conditions];
3949}
3950
3951
3952- (NSString *) randomShipKeyForRoleRespectingConditions:(NSString *)role
3953{
3955
3957 NSString *shipKey = nil;
3959
3960#if PROFILE_SHIP_SELECTION
3961 static unsigned long profTotal = 0, profSlowPath = 0;
3962 ++profTotal;
3963#endif
3964
3965 // Select a ship, check conditions and return it if possible.
3966 shipKey = [registry randomShipKeyForRole:role];
3967 if ([self canInstantiateShip:shipKey]) return shipKey;
3968
3969 /* If we got here, condition check failed.
3970 We now need to keep trying until we either find an acceptable ship or
3971 run out of candidates.
3972 This is special-cased because it has more overhead than the more
3973 common conditionless lookup.
3974 */
3975
3976#if PROFILE_SHIP_SELECTION
3977 ++profSlowPath;
3978 if ((profSlowPath % 10) == 0) // Only print every tenth slow path, to reduce spamminess.
3979 {
3980 OOLog(@"shipRegistry.selection.profile", @"Hit slow path in ship selection for role \"%@\", having selected ship \"%@\". Now %lu of %lu on slow path (%f%%).", role, shipKey, profSlowPath, profTotal, ((double)profSlowPath)/((double)profTotal) * 100.0f);
3981 }
3982#endif
3983
3984 pset = [[[registry probabilitySetForRole:role] mutableCopy] autorelease];
3985
3986 while ([pset count] > 0)
3987 {
3988 // Select a ship, check conditions and return it if possible.
3989 shipKey = [pset randomObject];
3990 if ([self canInstantiateShip:shipKey]) return shipKey;
3991
3992 // Condition failed -> remove ship from consideration.
3993 [pset removeObject:shipKey];
3994 }
3995
3996 // If we got here, some ships existed but all failed conditions test.
3997 return nil;
3998
4000}
4001
4002
4003- (ShipEntity *) newShipWithRole:(NSString *)role
4004{
4006
4007 ShipEntity *ship = nil;
4008 NSString *shipKey = nil;
4009 NSDictionary *shipInfo = nil;
4010 NSString *autoAI = nil;
4011
4012 shipKey = [self randomShipKeyForRoleRespectingConditions:role];
4013 if (shipKey != nil)
4014 {
4015 ship = [self newShipWithName:shipKey];
4016 if (ship != nil)
4017 {
4018 [ship setPrimaryRole:role];
4019
4020 shipInfo = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
4021 if ([shipInfo oo_fuzzyBooleanForKey:@"auto_ai" defaultValue:YES])
4022 {
4023 // Set AI based on role
4024 autoAI = [self defaultAIForRole:role];
4025 if (autoAI != nil)
4026 {
4027 [ship setAITo:autoAI];
4028 // Nikos 20090604
4029 // Pirate, trader or police with auto_ai? Follow populator rules for them.
4030 if ([role isEqualToString:@"pirate"]) [ship setBounty:20 + randf() * 50 withReason:kOOLegalStatusReasonSetup];
4031 if ([role isEqualToString:@"trader"]) [ship setBounty:0 withReason:kOOLegalStatusReasonSetup];
4032 if ([role isEqualToString:@"police"]) [ship setScanClass:CLASS_POLICE];
4033 if ([role isEqualToString:@"interceptor"])
4034 {
4035 [ship setScanClass: CLASS_POLICE];
4036 [ship setPrimaryRole:@"police"]; // to make sure interceptors get the correct pilot later on.
4037 }
4038 }
4039 if ([role isEqualToString:@"thargoid"]) [ship setScanClass: CLASS_THARGOID]; // thargoids are not on the autoAIMap
4040 }
4041 }
4042 }
4043
4044 return ship;
4045
4047}
4048
4049
4050- (OOVisualEffectEntity *) newVisualEffectWithName:(NSString *)effectKey
4051{
4053
4054 NSDictionary *effectDict = nil;
4055 OOVisualEffectEntity *effect = nil;
4056
4057 effectDict = [[OOShipRegistry sharedRegistry] effectInfoForKey:effectKey];
4058 if (effectDict == nil) return nil;
4059
4060 @try
4061 {
4062 effect = [[OOVisualEffectEntity alloc] initWithKey:effectKey definition:effectDict];
4063 }
4064 @catch (NSException *exception)
4065 {
4066 if ([[exception name] isEqual:OOLITE_EXCEPTION_DATA_NOT_FOUND])
4067 {
4068 OOLog(kOOLogException, @"***** Oolite Exception : '%@' in [Universe newVisualEffectWithName: %@ ] *****", [exception reason], effectKey);
4069 }
4070 else @throw exception;
4071 }
4072
4073 return effect;
4074
4076}
4077
4078
4079- (ShipEntity *) newSubentityWithName:(NSString *)shipKey andScaleFactor:(float)scale
4080{
4081 return [self newShipWithName:shipKey usePlayerProxy:NO isSubentity:YES andScaleFactor:scale];
4082}
4083
4084
4085- (ShipEntity *) newShipWithName:(NSString *)shipKey usePlayerProxy:(BOOL)usePlayerProxy
4086{
4087 return [self newShipWithName:shipKey usePlayerProxy:usePlayerProxy isSubentity:NO];
4088}
4089
4090- (ShipEntity *) newShipWithName:(NSString *)shipKey usePlayerProxy:(BOOL)usePlayerProxy isSubentity:(BOOL)isSubentity
4091{
4092 return [self newShipWithName:shipKey usePlayerProxy:usePlayerProxy isSubentity:isSubentity andScaleFactor:1.0f];
4093}
4094
4095- (ShipEntity *) newShipWithName:(NSString *)shipKey usePlayerProxy:(BOOL)usePlayerProxy isSubentity:(BOOL)isSubentity andScaleFactor:(float)scale
4096{
4098
4099 NSDictionary *shipDict = nil;
4100 ShipEntity *ship = nil;
4101
4102 shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
4103 if (shipDict == nil) return nil;
4104
4105 volatile Class shipClass = nil;
4106 if (isSubentity)
4107 {
4108 shipClass = [ShipEntity class];
4109 }
4110 else
4111 {
4112 shipClass = [self shipClassForShipDictionary:shipDict];
4113 if (usePlayerProxy && shipClass == [ShipEntity class])
4114 {
4115 shipClass = [ProxyPlayerEntity class];
4116 }
4117 }
4118
4119 @try
4120 {
4121 if (scale != 1.0f)
4122 {
4123 NSMutableDictionary *mShipDict = [shipDict mutableCopy];
4124 [mShipDict setObject:[NSNumber numberWithFloat:scale] forKey:@"model_scale_factor"];
4125 shipDict = [NSDictionary dictionaryWithDictionary:mShipDict];
4126 [mShipDict release];
4127 }
4128 ship = [[shipClass alloc] initWithKey:shipKey definition:shipDict];
4129 }
4130 @catch (NSException *exception)
4131 {
4132 if ([[exception name] isEqual:OOLITE_EXCEPTION_DATA_NOT_FOUND])
4133 {
4134 OOLog(kOOLogException, @"***** Oolite Exception : '%@' in [Universe newShipWithName: %@ ] *****", [exception reason], shipKey);
4135 }
4136 else @throw exception;
4137 }
4138
4139 // Set primary role to same as ship name, if ship name is also a role.
4140 // Otherwise, if caller doesn't set a role, one will be selected randomly.
4141 if ([ship hasRole:shipKey]) [ship setPrimaryRole:shipKey];
4142
4143 return ship;
4144
4146}
4147
4148
4149- (DockEntity *) newDockWithName:(NSString *)shipDataKey andScaleFactor:(float)scale
4150{
4152
4153 NSDictionary *shipDict = nil;
4154 DockEntity *dock = nil;
4155
4156 shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipDataKey];
4157 if (shipDict == nil) return nil;
4158
4159 @try
4160 {
4161 if (scale != 1.0f)
4162 {
4163 NSMutableDictionary *mShipDict = [shipDict mutableCopy];
4164 [mShipDict setObject:[NSNumber numberWithFloat:scale] forKey:@"model_scale_factor"];
4165 shipDict = [NSDictionary dictionaryWithDictionary:mShipDict];
4166 [mShipDict release];
4167 }
4168 dock = [[DockEntity alloc] initWithKey:shipDataKey definition:shipDict];
4169 }
4170 @catch (NSException *exception)
4171 {
4172 if ([[exception name] isEqual:OOLITE_EXCEPTION_DATA_NOT_FOUND])
4173 {
4174 OOLog(kOOLogException, @"***** Oolite Exception : '%@' in [Universe newDockWithName: %@ ] *****", [exception reason], shipDataKey);
4175 }
4176 else @throw exception;
4177 }
4178
4179 // Set primary role to same as name, if ship name is also a role.
4180 // Otherwise, if caller doesn't set a role, one will be selected randomly.
4181 if ([dock hasRole:shipDataKey]) [dock setPrimaryRole:shipDataKey];
4182
4183 return dock;
4184
4186}
4187
4188
4189- (ShipEntity *) newShipWithName:(NSString *)shipKey
4190{
4191 return [self newShipWithName:shipKey usePlayerProxy:NO];
4192}
4193
4194
4195- (Class) shipClassForShipDictionary:(NSDictionary *)dict
4196{
4198
4199 if (dict == nil) return Nil;
4200
4201 BOOL isStation = NO;
4202 NSString *shipRoles = [dict oo_stringForKey:@"roles"];
4203
4204 if (shipRoles != nil)
4205 {
4206 isStation = [shipRoles rangeOfString:@"station"].location != NSNotFound ||
4207 [shipRoles rangeOfString:@"carrier"].location != NSNotFound;
4208 }
4209
4210 // Note priority here: is_carrier overrides isCarrier which overrides roles.
4211 isStation = [dict oo_boolForKey:@"isCarrier" defaultValue:isStation];
4212 isStation = [dict oo_boolForKey:@"is_carrier" defaultValue:isStation];
4213
4214
4215 return isStation ? [StationEntity class] : [ShipEntity class];
4216
4218}
4219
4220
4221- (NSString *)defaultAIForRole:(NSString *)role
4222{
4223 return [autoAIMap oo_stringForKey:role];
4224}
4225
4226
4227- (OOCargoQuantity) maxCargoForShip:(NSString *) desc
4228{
4229 return [[[OOShipRegistry sharedRegistry] shipInfoForKey:desc] oo_unsignedIntForKey:@"max_cargo" defaultValue:0];
4230}
4231
4232/*
4233 * Price for an item expressed in 10ths of credits (divide by 10 to get credits)
4234 */
4235- (OOCreditsQuantity) getEquipmentPriceForKey:(NSString *)eq_key
4236{
4237 NSArray *itemData;
4238 foreach (itemData, equipmentData)
4239 {
4240 NSString *itemType = [itemData oo_stringAtIndex:EQUIPMENT_KEY_INDEX];
4241
4242 if ([itemType isEqual:eq_key])
4243 {
4244 return [itemData oo_unsignedLongLongAtIndex:EQUIPMENT_PRICE_INDEX];
4245 }
4246 }
4247 return 0;
4248}
4249
4250
4252{
4253 return commodities;
4254}
4255
4256
4257/* Converts template cargo pods to real ones */
4258- (ShipEntity *) reifyCargoPod:(ShipEntity *)cargoObj
4259{
4260 if ([cargoObj isTemplateCargoPod])
4261 {
4262 return [UNIVERSE cargoPodFromTemplate:cargoObj];
4263 }
4264 else
4265 {
4266 return cargoObj;
4267 }
4268}
4269
4270
4271- (ShipEntity *) cargoPodFromTemplate:(ShipEntity *)cargoObj
4272{
4273 ShipEntity *container = nil;
4274 // this is a template container, so we need to make a real one
4276 OOCargoQuantity co_amount = [UNIVERSE getRandomAmountOfCommodity:co_type];
4277 if (randf() < 0.5) // stops OXP monopolising pods for commodities
4278 {
4279 container = [UNIVERSE newShipWithRole:co_type]; // newShipWithRole returns retained object
4280 }
4281 if (container == nil)
4282 {
4283 container = [UNIVERSE newShipWithRole:@"cargopod"];
4284 }
4285 [container setCommodity:co_type andAmount:co_amount];
4286 return [container autorelease];
4287}
4288
4289
4290- (NSArray *) getContainersOfGoods:(OOCargoQuantity)how_many scarce:(BOOL)scarce legal:(BOOL)legal
4291{
4292 /* build list of goods allocating 0..100 for each based on how much of
4293 each quantity there is. Use a ratio of n x 100/64 for plentiful goods;
4294 reverse the probabilities for scarce goods.
4295 */
4296 NSMutableArray *accumulator = [NSMutableArray arrayWithCapacity:how_many];
4297 NSUInteger i=0, commodityCount = [commodityMarket count];
4298 OOCargoQuantity quantities[commodityCount];
4299 OOCargoQuantity total_quantity = 0;
4300
4301 NSArray *goodsKeys = [commodityMarket goods];
4302 NSString *goodsKey = nil;
4303
4304 foreach (goodsKey, goodsKeys)
4305 {
4307 if (scarce)
4308 {
4309 if (q < 64) q = 64 - q;
4310 else q = 0;
4311 }
4312 // legal YES restricts (almost) only to legal goods
4313 // legal NO allows illegal goods, but not necessarily a full hold
4314 if (legal && [commodityMarket exportLegalityForGood:goodsKey] > 0)
4315 {
4316 q &= 1; // keep a very small chance, sometimes
4317 }
4318 if (q > 64) q = 64;
4319 q *= 100; q/= 64;
4320 quantities[i++] = q;
4321 total_quantity += q;
4322 }
4323 // quantities is now used to determine which good get into the containers
4324 for (i = 0; i < how_many; i++)
4325 {
4326 NSUInteger co_type = 0;
4327
4328 int qr=0;
4329 if(total_quantity)
4330 {
4331 qr = 1+(Ranrot() % total_quantity);
4332 co_type = 0;
4333 while (qr > 0)
4334 {
4335 NSAssert((NSUInteger)co_type < commodityCount, @"Commodity type index out of range.");
4336 qr -= quantities[co_type++];
4337 }
4338 co_type--;
4339 }
4340
4341 ShipEntity *container = [cargoPods objectForKey:[goodsKeys oo_stringAtIndex:co_type]];
4342
4343 if (container != nil)
4344 {
4345 [accumulator addObject:container];
4346 }
4347 else
4348 {
4349 OOLog(@"universe.createContainer.failed", @"***** ERROR: failed to find a container to fill with %@ (%llu).", [goodsKeys oo_stringAtIndex:co_type], co_type);
4350
4351 }
4352 }
4353 return [NSArray arrayWithArray:accumulator];
4354}
4355
4356
4357- (NSArray *) getContainersOfCommodity:(OOCommodityType)commodity_name :(OOCargoQuantity)how_much
4358{
4359 NSMutableArray *accumulator = [NSMutableArray arrayWithCapacity:how_much];
4360 if (![commodities goodDefined:commodity_name])
4361 {
4362 return [NSArray array]; // empty array
4363 }
4364
4365 ShipEntity *container = [cargoPods objectForKey:commodity_name];
4366 while (how_much > 0)
4367 {
4368 if (container)
4369 {
4370 [accumulator addObject:container];
4371 }
4372 else
4373 {
4374 OOLog(@"universe.createContainer.failed", @"***** ERROR: failed to find a container to fill with %@", commodity_name);
4375 }
4376
4377 how_much--;
4378 }
4379 return [NSArray arrayWithArray:accumulator];
4380}
4381
4382
4383- (void) fillCargopodWithRandomCargo:(ShipEntity *)cargopod
4384{
4385 if (cargopod == nil || ![cargopod hasRole:@"cargopod"] || [cargopod cargoType] == CARGO_SCRIPTED_ITEM) return;
4386
4387 if ([cargopod commodityType] == nil || ![cargopod commodityAmount])
4388 {
4389 NSString *aCommodity = [self getRandomCommodity];
4390 OOCargoQuantity aQuantity = [self getRandomAmountOfCommodity:aCommodity];
4391 [cargopod setCommodity:aCommodity andAmount:aQuantity];
4392 }
4393}
4394
4395
4397{
4399}
4400
4401
4402- (OOCargoQuantity) getRandomAmountOfCommodity:(OOCommodityType)co_type
4403{
4404 OOMassUnit units;
4405
4406 if (co_type == nil) {
4407 return 0;
4408 }
4409
4410 units = [commodities massUnitForGood:co_type];
4411 switch (units)
4412 {
4413 case 0 : // TONNES
4414 return 1;
4415 case 1 : // KILOGRAMS
4416 return 1 + (Ranrot() % 6) + (Ranrot() % 6) + (Ranrot() % 6);
4417 case 2 : // GRAMS
4418 return 4 + (Ranrot() % 16) + (Ranrot() % 11) + (Ranrot() % 6);
4419 }
4420 OOLog(@"universe.commodityAmount.warning",@"Commodity %@ has an unrecognised mass unit, assuming tonnes",co_type);
4421 return 1;
4422}
4423
4424
4425- (NSDictionary *)commodityDataForType:(OOCommodityType)type
4426{
4427 return [commodityMarket definitionForGood:type];
4428}
4429
4430
4431- (NSString *) displayNameForCommodity:(OOCommodityType)co_type
4432{
4433 return [commodityMarket nameForGood:co_type];
4434}
4435
4436
4437- (NSString *) describeCommodity:(OOCommodityType)co_type amount:(OOCargoQuantity)co_amount
4438{
4439 int units;
4440 NSString *unitDesc = nil, *typeDesc = nil;
4441 NSDictionary *commodity = [self commodityDataForType:co_type];
4442
4443 if (commodity == nil) return @"";
4444
4445 units = [commodityMarket massUnitForGood:co_type];
4446 if (co_amount == 1)
4447 {
4448 switch (units)
4449 {
4450 case UNITS_KILOGRAMS : // KILOGRAM
4451 unitDesc = DESC(@"cargo-kilogram");
4452 break;
4453 case UNITS_GRAMS : // GRAM
4454 unitDesc = DESC(@"cargo-gram");
4455 break;
4456 case UNITS_TONS : // TONNE
4457 default :
4458 unitDesc = DESC(@"cargo-ton");
4459 break;
4460 }
4461 }
4462 else
4463 {
4464 switch (units)
4465 {
4466 case UNITS_KILOGRAMS : // KILOGRAMS
4467 unitDesc = DESC(@"cargo-kilograms");
4468 break;
4469 case UNITS_GRAMS : // GRAMS
4470 unitDesc = DESC(@"cargo-grams");
4471 break;
4472 case UNITS_TONS : // TONNES
4473 default :
4474 unitDesc = DESC(@"cargo-tons");
4475 break;
4476 }
4477 }
4478
4479 typeDesc = [commodityMarket nameForGood:co_type];
4480
4481 return [NSString stringWithFormat:@"%d %@ %@",co_amount, unitDesc, typeDesc];
4482}
4483
4485
4486- (void) setGameView:(MyOpenGLView *)view
4487{
4488 [gameView release];
4489 gameView = [view retain];
4490}
4491
4492
4494{
4495 return gameView;
4496}
4497
4498
4500{
4501 return [[self gameView] gameController];
4502}
4503
4504
4505- (NSDictionary *) gameSettings
4506{
4507#if OOLITE_SDL
4508 NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:12];
4509#else
4510 NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:10];
4511#endif
4512
4513 [result oo_setInteger:[PLAYER isSpeechOn] forKey:@"speechOn"];
4514 [result oo_setBool:autoSave forKey:@"autosave"];
4515 [result oo_setBool:wireframeGraphics forKey:@"wireframeGraphics"];
4516 [result oo_setBool:doProcedurallyTexturedPlanets forKey:@"procedurallyTexturedPlanets"];
4517#if OOLITE_SDL
4518 [result oo_setFloat:[gameView gammaValue] forKey:@"gammaValue"];
4519#endif
4520
4521 [result oo_setFloat:[gameView fov:NO] forKey:@"fovValue"];
4522
4523#if OOLITE_WINDOWS
4524 if ([gameView hdrOutput])
4525 {
4526 [result oo_setFloat:[gameView hdrMaxBrightness] forKey:@"hdr-max-brightness"];
4527 [result oo_setFloat:[gameView hdrPaperWhiteBrightness] forKey:@"hdr-paperwhite-brightness"];
4528 [result setObject:OOStringFromHDRToneMapper([gameView hdrToneMapper]) forKey:@"hdr-tone-mapper"];
4529 }
4530#endif
4531
4532 [result setObject:OOStringFromSDRToneMapper([gameView sdrToneMapper]) forKey:@"sdr-tone-mapper"];
4533
4534 [result setObject:OOStringFromGraphicsDetail([self detailLevel]) forKey:@"detailLevel"];
4535
4536 NSString *desc = @"UNDEFINED";
4537 switch ([[OOMusicController sharedController] mode])
4538 {
4539 case kOOMusicOff: desc = @"MUSIC_OFF"; break;
4540 case kOOMusicOn: desc = @"MUSIC_ON"; break;
4541 case kOOMusicITunes: desc = @"MUSIC_ITUNES"; break;
4542 }
4543 [result setObject:desc forKey:@"musicMode"];
4544
4545 NSDictionary *gameWindow = [NSDictionary dictionaryWithObjectsAndKeys:
4546 [NSNumber numberWithFloat:[gameView viewSize].width], @"width",
4547 [NSNumber numberWithFloat:[gameView viewSize].height], @"height",
4548 [NSNumber numberWithBool:[[self gameController] inFullScreenMode]], @"fullScreen",
4549 nil];
4550 [result setObject:gameWindow forKey:@"gameWindow"];
4551
4552 [result setObject:[PLAYER keyConfig] forKey:@"keyConfig"];
4553
4554 return [[result copy] autorelease];
4555}
4556
4557
4558- (void) useGUILightSource:(BOOL)GUILight
4559{
4560 if (GUILight != demo_light_on)
4561 {
4562 if (![self useShaders])
4563 {
4564 if (GUILight)
4565 {
4566 OOGL(glEnable(GL_LIGHT0));
4567 OOGL(glDisable(GL_LIGHT1));
4568 }
4569 else
4570 {
4571 OOGL(glEnable(GL_LIGHT1));
4572 OOGL(glDisable(GL_LIGHT0));
4573 }
4574 }
4575 // There should be nothing to do for shaders, they use the same (always on) light source
4576 // both in flight & in gui mode. According to the standard, shaders should treat lights as
4577 // always enabled. At least one non-standard shader implementation (windows' X3100 Intel
4578 // core with GM965 chipset and version 6.14.10.4990 driver) does _not_ use glDisabled lights,
4579 // making the following line necessary.
4580
4581 else OOGL(glEnable(GL_LIGHT1)); // make sure we have a light, even with shaders (!)
4582
4583 demo_light_on = GUILight;
4584 }
4585}
4586
4587
4588- (void) lightForEntity:(BOOL)isLit
4589{
4590 if (isLit != object_light_on)
4591 {
4592 if ([self useShaders])
4593 {
4594 if (isLit)
4595 {
4596 OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_diffuse));
4597 OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, sun_specular));
4598 }
4599 else
4600 {
4601 OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_off));
4602 OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, sun_off));
4603 }
4604 }
4605 else
4606 {
4607 if (!demo_light_on)
4608 {
4609 if (isLit) OOGL(glEnable(GL_LIGHT1));
4610 else OOGL(glDisable(GL_LIGHT1));
4611 }
4612 else
4613 {
4614 // If we're in demo/GUI mode we should always have a lit object.
4615 OOGL(glEnable(GL_LIGHT0));
4616
4617 // Redundant, see above.
4618 //if (isLit) OOGL(glEnable(GL_LIGHT0));
4619 //else OOGL(glDisable(GL_LIGHT0));
4620 }
4621 }
4622
4623 object_light_on = isLit;
4624 }
4625}
4626
4627
4628// global rotation matrix definitions
4629static const OOMatrix fwd_matrix =
4630 {{
4631 { 1.0f, 0.0f, 0.0f, 0.0f },
4632 { 0.0f, 1.0f, 0.0f, 0.0f },
4633 { 0.0f, 0.0f, 1.0f, 0.0f },
4634 { 0.0f, 0.0f, 0.0f, 1.0f }
4635 }};
4636static const OOMatrix aft_matrix =
4637 {{
4638 {-1.0f, 0.0f, 0.0f, 0.0f },
4639 { 0.0f, 1.0f, 0.0f, 0.0f },
4640 { 0.0f, 0.0f, -1.0f, 0.0f },
4641 { 0.0f, 0.0f, 0.0f, 1.0f }
4642 }};
4643static const OOMatrix port_matrix =
4644 {{
4645 { 0.0f, 0.0f, -1.0f, 0.0f },
4646 { 0.0f, 1.0f, 0.0f, 0.0f },
4647 { 1.0f, 0.0f, 0.0f, 0.0f },
4648 { 0.0f, 0.0f, 0.0f, 1.0f }
4649 }};
4650static const OOMatrix starboard_matrix =
4651 {{
4652 { 0.0f, 0.0f, 1.0f, 0.0f },
4653 { 0.0f, 1.0f, 0.0f, 0.0f },
4654 {-1.0f, 0.0f, 0.0f, 0.0f },
4655 { 0.0f, 0.0f, 0.0f, 1.0f }
4656 }};
4657
4658
4659- (void) getActiveViewMatrix:(OOMatrix *)outMatrix forwardVector:(Vector *)outForward upVector:(Vector *)outUp
4660{
4661 assert(outMatrix != NULL && outForward != NULL && outUp != NULL);
4662
4663 PlayerEntity *player = nil;
4664
4665 switch (viewDirection)
4666 {
4667 case VIEW_AFT:
4668 *outMatrix = aft_matrix;
4669 *outForward = vector_flip(kBasisZVector);
4670 *outUp = kBasisYVector;
4671 return;
4672
4673 case VIEW_PORT:
4674 *outMatrix = port_matrix;
4675 *outForward = vector_flip(kBasisXVector);
4676 *outUp = kBasisYVector;
4677 return;
4678
4679 case VIEW_STARBOARD:
4680 *outMatrix = starboard_matrix;
4681 *outForward = kBasisXVector;
4682 *outUp = kBasisYVector;
4683 return;
4684
4685 case VIEW_CUSTOM:
4686 player = PLAYER;
4687 *outMatrix = [player customViewMatrix];
4688 *outForward = [player customViewForwardVector];
4689 *outUp = [player customViewUpVector];
4690 return;
4691
4692 case VIEW_FORWARD:
4693 case VIEW_NONE:
4694 case VIEW_GUI_DISPLAY:
4695 case VIEW_BREAK_PATTERN:
4696 ;
4697 }
4698
4699 *outMatrix = fwd_matrix;
4700 *outForward = kBasisZVector;
4701 *outUp = kBasisYVector;
4702}
4703
4704
4706{
4707 OOMatrix m;
4708 Vector f, u;
4709
4711 return m;
4712}
4713
4714
4715/* Code adapted from http://www.crownandcutlass.com/features/technicaldetails/frustum.html
4716 * Original license is: "This page and its contents are Copyright 2000 by Mark Morley
4717 * Unless otherwise noted, you may use any and all code examples provided herein in any way you want."
4718*/
4719
4721{
4722 OOMatrix clip;
4723 GLfloat rt;
4724
4726
4727 /* Extract the numbers for the RIGHT plane */
4728 frustum[0][0] = clip.m[0][3] - clip.m[0][0];
4729 frustum[0][1] = clip.m[1][3] - clip.m[1][0];
4730 frustum[0][2] = clip.m[2][3] - clip.m[2][0];
4731 frustum[0][3] = clip.m[3][3] - clip.m[3][0];
4732
4733 /* Normalize the result */
4734 rt = 1.0f / sqrt(frustum[0][0] * frustum[0][0] + frustum[0][1] * frustum[0][1] + frustum[0][2] * frustum[0][2]);
4735 frustum[0][0] *= rt;
4736 frustum[0][1] *= rt;
4737 frustum[0][2] *= rt;
4738 frustum[0][3] *= rt;
4739
4740 /* Extract the numbers for the LEFT plane */
4741 frustum[1][0] = clip.m[0][3] + clip.m[0][0];
4742 frustum[1][1] = clip.m[1][3] + clip.m[1][0];
4743 frustum[1][2] = clip.m[2][3] + clip.m[2][0];
4744 frustum[1][3] = clip.m[3][3] + clip.m[3][0];
4745
4746 /* Normalize the result */
4747 rt = 1.0f / sqrt(frustum[1][0] * frustum[1][0] + frustum[1][1] * frustum[1][1] + frustum[1][2] * frustum[1][2]);
4748 frustum[1][0] *= rt;
4749 frustum[1][1] *= rt;
4750 frustum[1][2] *= rt;
4751 frustum[1][3] *= rt;
4752
4753 /* Extract the BOTTOM plane */
4754 frustum[2][0] = clip.m[0][3] + clip.m[0][1];
4755 frustum[2][1] = clip.m[1][3] + clip.m[1][1];
4756 frustum[2][2] = clip.m[2][3] + clip.m[2][1];
4757 frustum[2][3] = clip.m[3][3] + clip.m[3][1];
4758
4759 /* Normalize the result */
4760 rt = 1.0 / sqrt(frustum[2][0] * frustum[2][0] + frustum[2][1] * frustum[2][1] + frustum[2][2] * frustum[2][2]);
4761 frustum[2][0] *= rt;
4762 frustum[2][1] *= rt;
4763 frustum[2][2] *= rt;
4764 frustum[2][3] *= rt;
4765
4766 /* Extract the TOP plane */
4767 frustum[3][0] = clip.m[0][3] - clip.m[0][1];
4768 frustum[3][1] = clip.m[1][3] - clip.m[1][1];
4769 frustum[3][2] = clip.m[2][3] - clip.m[2][1];
4770 frustum[3][3] = clip.m[3][3] - clip.m[3][1];
4771
4772 /* Normalize the result */
4773 rt = 1.0 / sqrt(frustum[3][0] * frustum[3][0] + frustum[3][1] * frustum[3][1] + frustum[3][2] * frustum[3][2]);
4774 frustum[3][0] *= rt;
4775 frustum[3][1] *= rt;
4776 frustum[3][2] *= rt;
4777 frustum[3][3] *= rt;
4778
4779 /* Extract the FAR plane */
4780 frustum[4][0] = clip.m[0][3] - clip.m[0][2];
4781 frustum[4][1] = clip.m[1][3] - clip.m[1][2];
4782 frustum[4][2] = clip.m[2][3] - clip.m[2][2];
4783 frustum[4][3] = clip.m[3][3] - clip.m[3][2];
4784
4785 /* Normalize the result */
4786 rt = sqrt(frustum[4][0] * frustum[4][0] + frustum[4][1] * frustum[4][1] + frustum[4][2] * frustum[4][2]);
4787 frustum[4][0] *= rt;
4788 frustum[4][1] *= rt;
4789 frustum[4][2] *= rt;
4790 frustum[4][3] *= rt;
4791
4792 /* Extract the NEAR plane */
4793 frustum[5][0] = clip.m[0][3] + clip.m[0][2];
4794 frustum[5][1] = clip.m[1][3] + clip.m[1][2];
4795 frustum[5][2] = clip.m[2][3] + clip.m[2][2];
4796 frustum[5][3] = clip.m[3][3] + clip.m[3][2];
4797
4798 /* Normalize the result */
4799 rt = sqrt(frustum[5][0] * frustum[5][0] + frustum[5][1] * frustum[5][1] + frustum[5][2] * frustum[5][2]);
4800 frustum[5][0] *= rt;
4801 frustum[5][1] *= rt;
4802 frustum[5][2] *= rt;
4803 frustum[5][3] *= rt;
4804}
4805
4806
4807- (BOOL) viewFrustumIntersectsSphereAt:(Vector)position withRadius:(GLfloat)radius
4808{
4809 // position is the relative position between the camera and the object
4810 int p;
4811 for (p = 0; p < 6; p++)
4812 {
4813 if (frustum[p][0] * position.x + frustum[p][1] * position.y + frustum[p][2] * position.z + frustum[p][3] <= -radius)
4814 {
4815 return NO;
4816 }
4817 }
4818 return YES;
4819}
4820
4821
4823{
4824 int currentPostFX = [self currentPostFX];
4825 BOOL hudSeparateRenderPass = [self useShaders] && (currentPostFX == OO_POSTFX_NONE || ((currentPostFX == OO_POSTFX_CLOAK || currentPostFX == OO_POSTFX_CRTBADSIGNAL) && [self colorblindMode] == OO_POSTFX_NONE));
4826 NSSize viewSize = [gameView viewSize];
4827 OOLog(@"universe.profile.draw", @"%@", @"Begin draw");
4828
4829 if (!no_update)
4830 {
4831 if ((int)targetFramebufferSize.width != (int)viewSize.width || (int)targetFramebufferSize.height != (int)viewSize.height)
4832 {
4834 }
4835
4836 if([self useShaders])
4837 {
4838 if ([gameView msaa])
4839 {
4840 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebufferID));
4841 }
4842 else
4843 {
4844 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, targetFramebufferID));
4845 }
4846 }
4847 @try
4848 {
4849 no_update = YES; // block other attempts to draw
4850
4851 int i, v_status, vdist;
4852 Vector view_dir, view_up;
4853 OOMatrix view_matrix;
4854 int ent_count = n_entities;
4855 Entity *my_entities[ent_count];
4856 int draw_count = 0;
4857 PlayerEntity *player = PLAYER;
4858 Entity *drawthing = nil;
4859 BOOL demoShipMode = [player showDemoShips];
4860
4861 float aspect = viewSize.height/viewSize.width;
4862
4863 if (!displayGUI && wasDisplayGUI)
4864 {
4865 // reset light1 position for the shaders
4866 if (cachedSun) [UNIVERSE setMainLightPosition:HPVectorToVector([cachedSun position])]; // the main light is the sun.
4867 else [UNIVERSE setMainLightPosition:kZeroVector];
4868 }
4870 // use a non-mutable copy so this can't be changed under us.
4871 for (i = 0; i < ent_count; i++)
4872 {
4873 /* BUG: this list is ordered nearest to furthest from
4874 * the player, and we just assume that the camera is
4875 * on/near the player. So long as everything uses
4876 * depth tests, we'll get away with it; it'll just
4877 * occasionally be inefficient. - CIM */
4878 Entity *e = sortedEntities[i]; // ordered NEAREST -> FURTHEST AWAY
4879 if ([e isVisible])
4880 {
4881 my_entities[draw_count++] = [[e retain] autorelease];
4882 }
4883 }
4884
4885 v_status = [player status];
4886
4887 OOCheckOpenGLErrors(@"Universe before doing anything");
4888
4889 OOSetOpenGLState(OPENGL_STATE_OPAQUE); // FIXME: should be redundant.
4890
4891 OOGL(glClear(GL_COLOR_BUFFER_BIT));
4892
4893 if (!displayGUI)
4894 {
4895 OOGL(glClearColor(skyClearColor[0], skyClearColor[1], skyClearColor[2], skyClearColor[3]));
4896 }
4897 else
4898 {
4899 OOGL(glClearColor(0.0, 0.0, 0.0, 0.0));
4900 // If set, display background GUI image. Must be done before enabling lights to avoid dim backgrounds
4902 OOGLFrustum(-0.5, 0.5, -aspect*0.5, aspect*0.5, 1.0, MAX_CLEAR_DEPTH);
4904
4905 }
4906
4907 BOOL fogging, bpHide = [self breakPatternHide];
4908
4909 for (vdist=0;vdist<=1;vdist++)
4910 {
4911 float nearPlane = vdist ? 1.0 : INTERMEDIATE_CLEAR_DEPTH;
4912 float farPlane = vdist ? INTERMEDIATE_CLEAR_DEPTH : MAX_CLEAR_DEPTH;
4913 float ratio = (displayGUI ? 0.5 : [gameView fov:YES]) * nearPlane; // 0.5 is field of view ratio for GUIs
4914
4916 if ((displayGUI && 4*aspect >= 3) || (!displayGUI && 4*aspect <= 3))
4917 {
4918 OOGLFrustum(-ratio, ratio, -aspect*ratio, aspect*ratio, nearPlane, farPlane);
4919 }
4920 else
4921 {
4922 OOGLFrustum(-3*ratio/aspect/4, 3*ratio/aspect/4, -3*ratio/4, 3*ratio/4, nearPlane, farPlane);
4923 }
4924
4925 [self getActiveViewMatrix:&view_matrix forwardVector:&view_dir upVector:&view_up];
4926
4927 OOGLResetModelView(); // reset matrix
4929
4930 // HACK BUSTED
4931 OOGLMultModelView(OOMatrixForScale(-1.0,1.0,1.0)); // flip left and right
4932 OOGLPushModelView(); // save this flat viewpoint
4933
4934 /* OpenGL viewpoints:
4935 *
4936 * Oolite used to transform the viewpoint by the inverse of the
4937 * view position, and then transform the objects by the inverse
4938 * of their position, to get the correct view. However, as
4939 * OpenGL only uses single-precision floats, this causes
4940 * noticeable display inaccuracies relatively close to the
4941 * origin.
4942 *
4943 * Instead, we now calculate the difference between the view
4944 * position and the object using high-precision vectors, convert
4945 * the difference to a low-precision vector (since if you can
4946 * see it, it's close enough for the loss of precision not to
4947 * matter) and use that relative vector for the OpenGL transform
4948 *
4949 * Objects which reset the view matrix in their display need to be
4950 * handled a little more carefully than before.
4951 */
4952
4954 // clearing the depth buffer waits until we've set
4955 // STATE_OPAQUE so that depth writes are definitely
4956 // available.
4957 OOGL(glClear(GL_DEPTH_BUFFER_BIT));
4958
4959
4960 // Set up view transformation matrix
4961 OOMatrix flipMatrix = kIdentityMatrix;
4962 flipMatrix.m[2][2] = -1;
4963 view_matrix = OOMatrixMultiply(view_matrix, flipMatrix);
4964 Vector viewOffset = [player viewpointOffset];
4965
4966 OOGLLookAt(view_dir, kZeroVector, view_up);
4967
4968 if (EXPECT(!displayGUI || demoShipMode))
4969 {
4970 if (EXPECT(!demoShipMode)) // we're in flight
4971 {
4972 // rotate the view
4973 OOGLMultModelView([player rotationMatrix]);
4974 // translate the view
4975 // HPVect: camera-relative position
4976 OOGL(glLightModelfv(GL_LIGHT_MODEL_AMBIENT, stars_ambient));
4977 // main light position, no shaders, in-flight / shaders, in-flight and docked.
4978 if (cachedSun)
4979 {
4981 }
4982 else
4983 {
4984 // in witchspace
4985 [self setMainLightPosition:HPVectorToVector(HPvector_flip([PLAYER viewpointPosition]))];
4986 }
4987 OOGL(glLightfv(GL_LIGHT1, GL_POSITION, main_light_position));
4988 }
4989 else
4990 {
4991 OOGL(glLightModelfv(GL_LIGHT_MODEL_AMBIENT, docked_light_ambient));
4992 // main_light_position no shaders, docked/GUI.
4993 OOGL(glLightfv(GL_LIGHT0, GL_POSITION, main_light_position));
4994 // main light position, no shaders, in-flight / shaders, in-flight and docked.
4995 OOGL(glLightfv(GL_LIGHT1, GL_POSITION, main_light_position));
4996 }
4997
4998
4999 OOGL([self useGUILightSource:demoShipMode]);
5000
5001 // HACK: store view matrix for absolute drawing of active subentities (i.e., turrets, flashers).
5003
5004 int furthest = draw_count - 1;
5005 int nearest = 0;
5006 BOOL inAtmosphere = airResistanceFactor > 0.01;
5007 GLfloat fogFactor = 0.5 / airResistanceFactor;
5008 double fog_scale, half_scale;
5009 GLfloat flat_ambdiff[4] = {1.0, 1.0, 1.0, 1.0}; // for alpha
5010 GLfloat mat_no[4] = {0.0, 0.0, 0.0, 1.0}; // nothing
5011 GLfloat fog_blend;
5012
5013 OOGL(glHint(GL_FOG_HINT, [self reducedDetail] ? GL_FASTEST : GL_NICEST));
5014
5015 [self defineFrustum]; // camera is set up for this frame
5016
5018 OOCheckOpenGLErrors(@"Universe after setting up for opaque pass");
5019 OOLog(@"universe.profile.draw", @"%@", @"Begin opaque pass");
5020
5021
5022 // DRAW ALL THE OPAQUE ENTITIES
5023 for (i = furthest; i >= nearest; i--)
5024 {
5025 drawthing = my_entities[i];
5026 OOEntityStatus d_status = [drawthing status];
5027
5028 if (bpHide && !drawthing->isImmuneToBreakPatternHide) continue;
5029 if (vdist == 1 && [drawthing cameraRangeFront] > farPlane*1.5) continue;
5030 if (vdist == 0 && [drawthing cameraRangeBack] < nearPlane) continue;
5031// if (vdist == 1 && [drawthing isPlanet]) continue;
5032
5033 if (!((d_status == STATUS_COCKPIT_DISPLAY) ^ demoShipMode)) // either demo ship mode or in flight
5034 {
5035 // reset material properties
5036 // FIXME: should be part of SetState
5037 OOGL(glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, flat_ambdiff));
5038 OOGL(glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_no));
5039
5041 if (EXPECT(drawthing != player))
5042 {
5043 //translate the object
5044 // HPVect: camera relative
5046 OOGLTranslateModelView([drawthing cameraRelativePosition]);
5047 //rotate the object
5048 OOGLMultModelView([drawthing drawRotationMatrix]);
5049 }
5050 else
5051 {
5052 // Load transformation matrix
5053 OOGLLoadModelView(view_matrix);
5054 //translate the object from the viewpoint
5055 OOGLTranslateModelView(vector_flip(viewOffset));
5056 }
5057
5058 // atmospheric fog
5059 fogging = (inAtmosphere && ![drawthing isStellarObject]);
5060
5061 if (fogging)
5062 {
5063 fog_scale = BILLBOARD_DEPTH * fogFactor;
5064 half_scale = fog_scale * 0.50;
5065 OOGL(glEnable(GL_FOG));
5066 OOGL(glFogi(GL_FOG_MODE, GL_LINEAR));
5067 OOGL(glFogfv(GL_FOG_COLOR, skyClearColor));
5068 OOGL(glFogf(GL_FOG_START, half_scale));
5069 OOGL(glFogf(GL_FOG_END, fog_scale));
5070 fog_blend = OOClamp_0_1_f((magnitude([drawthing cameraRelativePosition]) - half_scale)/half_scale);
5071 [drawthing setAtmosphereFogging: [OOColor colorWithRed: skyClearColor[0] green: skyClearColor[1] blue: skyClearColor[2] alpha: fog_blend]];
5072 }
5073
5074 [self lightForEntity:demoShipMode || drawthing->isSunlit];
5075
5076 // draw the thing
5077 [drawthing drawImmediate:false translucent:false];
5078
5080
5081 // atmospheric fog
5082 if (fogging)
5083 {
5085 OOGL(glDisable(GL_FOG));
5086 }
5087
5088 }
5089
5090 if (!((d_status == STATUS_COCKPIT_DISPLAY) ^ demoShipMode)) // either in flight or in demo ship mode
5091 {
5093 if (EXPECT(drawthing != player))
5094 {
5095 //translate the object
5096 // HPVect: camera relative positions
5098 OOGLTranslateModelView([drawthing cameraRelativePosition]);
5099 //rotate the object
5100 OOGLMultModelView([drawthing drawRotationMatrix]);
5101 }
5102 else
5103 {
5104 // Load transformation matrix
5105 OOGLLoadModelView(view_matrix);
5106 //translate the object from the viewpoint
5107 OOGLTranslateModelView(vector_flip(viewOffset));
5108 }
5109
5110 // experimental - atmospheric fog
5111 fogging = (inAtmosphere && ![drawthing isStellarObject]);
5112
5113 if (fogging)
5114 {
5115 fog_scale = BILLBOARD_DEPTH * fogFactor;
5116 half_scale = fog_scale * 0.50;
5117 OOGL(glEnable(GL_FOG));
5118 OOGL(glFogi(GL_FOG_MODE, GL_LINEAR));
5119 OOGL(glFogfv(GL_FOG_COLOR, skyClearColor));
5120 OOGL(glFogf(GL_FOG_START, half_scale));
5121 OOGL(glFogf(GL_FOG_END, fog_scale));
5122 fog_blend = OOClamp_0_1_f((magnitude([drawthing cameraRelativePosition]) - half_scale)/half_scale);
5123 [drawthing setAtmosphereFogging: [OOColor colorWithRed: skyClearColor[0] green: skyClearColor[1] blue: skyClearColor[2] alpha: fog_blend]];
5124 }
5125
5126 // draw the thing
5127 [drawthing drawImmediate:false translucent:true];
5128
5129 // atmospheric fog
5130 if (fogging)
5131 {
5133 OOGL(glDisable(GL_FOG));
5134 }
5135
5137 }
5138 }
5139 }
5140
5142 }
5143
5144 // glare effects covering the entire game window
5146 OOGLFrustum(-0.5, 0.5, -aspect*0.5, aspect*0.5, 1.0, MAX_CLEAR_DEPTH);
5147 OOSetOpenGLState(OPENGL_STATE_OVERLAY); // FIXME: should be redundant.
5148 if (EXPECT(!displayGUI))
5149 {
5150 if (!bpHide && cachedSun)
5151 {
5154 }
5155 }
5156
5157 // actions when the HUD should be rendered separately from the 3d universe
5158 if (hudSeparateRenderPass)
5159 {
5160 OOCheckOpenGLErrors(@"Universe after drawing entities");
5161 OOSetOpenGLState(OPENGL_STATE_OVERLAY); // FIXME: should be redundant.
5162
5164 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
5165
5166 OOLog(@"universe.profile.secondPassDraw", @"%@", @"Begin second pass draw");
5168 OOCheckOpenGLErrors(@"Universe after drawing from custom framebuffer to screen framebuffer");
5169 OOLog(@"universe.profile.secondPassDraw", @"%@", @"End second pass drawing");
5170
5171 OOLog(@"universe.profile.drawHUD", @"%@", @"Begin HUD drawing");
5172 }
5173
5174 /* Reset for HUD drawing */
5175 OOCheckOpenGLErrors(@"Universe after drawing entities");
5176 OOLog(@"universe.profile.draw", @"%@", @"Begin HUD");
5177
5178 GLfloat lineWidth = [gameView viewSize].width / 1024.0; // restore line size
5179 if (lineWidth < 1.0) lineWidth = 1.0;
5180 if (lineWidth > 1.5) lineWidth = 1.5; // don't overscale; think of ultra-wide screen setups
5181 OOGL(GLScaledLineWidth(lineWidth));
5182
5183 HeadUpDisplay *theHUD = [player hud];
5184
5185 // If the HUD has a non-nil deferred name string, it means that a HUD switch was requested while it was being rendered.
5186 // If so, execute the deferred HUD switch now - Nikos 20110628
5187 if ([theHUD deferredHudName] != nil)
5188 {
5189 NSString *deferredName = [[theHUD deferredHudName] retain];
5190 [player switchHudTo:deferredName];
5191 [deferredName release];
5192 theHUD = [player hud]; // HUD has been changed, so point to its new address
5193 }
5194
5195 // Hiding HUD: has been a regular - non-debug - feature as of r2749, about 2 yrs ago! --Kaks 2011.10.14
5196 static float sPrevHudAlpha = -1.0f;
5197 if ([theHUD isHidden])
5198 {
5199 if (sPrevHudAlpha < 0.0f)
5200 {
5201 sPrevHudAlpha = [theHUD overallAlpha];
5202 }
5203 [theHUD setOverallAlpha:0.0f];
5204 }
5205 else if (sPrevHudAlpha >= 0.0f)
5206 {
5207 [theHUD setOverallAlpha:sPrevHudAlpha];
5208 sPrevHudAlpha = -1.0f;
5209 }
5210
5211 switch (v_status) {
5212 case STATUS_DEAD:
5213 case STATUS_ESCAPE_SEQUENCE:
5214 case STATUS_START_GAME:
5215 // no HUD rendering in these modes
5216 break;
5217 default:
5218 switch ([player guiScreen])
5219 {
5220 //case GUI_SCREEN_KEYBOARD:
5221 // no HUD rendering on this screen
5222 //break;
5223 default:
5224 [theHUD setLineWidth:lineWidth];
5225 [theHUD renderHUD];
5226 }
5227 }
5228
5229 // should come after the HUD to avoid it being overlapped by it
5230 [self drawMessage];
5231
5232#if (defined (SNAPSHOT_BUILD) && defined (OOLITE_SNAPSHOT_VERSION))
5233 [self drawWatermarkString:@"Development version " @OOLITE_SNAPSHOT_VERSION];
5234#endif
5235
5236 OOLog(@"universe.profile.drawHUD", @"%@", @"End HUD drawing");
5237 OOCheckOpenGLErrors(@"Universe after drawing HUD");
5238
5239 OOGL(glFlush()); // don't wait around for drawing to complete
5240
5241 no_update = NO; // allow other attempts to draw
5242
5243 // frame complete, when it is time to update the fps_counter, updateClocks:delta_t
5244 // in PlayerEntity.m will take care of resetting the processed frames number to 0.
5245 if (![[self gameController] isGamePaused])
5246 {
5248 }
5249 }
5250 @catch (NSException *exception)
5251 {
5252 no_update = NO; // make sure we don't get stuck in all subsequent frames.
5253
5254 if ([[exception name] hasPrefix:@"Oolite"])
5255 {
5256 [self handleOoliteException:exception];
5257 }
5258 else
5259 {
5260 OOLog(kOOLogException, @"***** Exception: %@ : %@ *****",[exception name], [exception reason]);
5261 @throw exception;
5262 }
5263 }
5264 }
5265
5266 OOLog(@"universe.profile.draw", @"%@", @"End drawing");
5267
5268 // actions when the HUD should be rendered together with the 3d universe
5269 if(!hudSeparateRenderPass)
5270 {
5271 if([self useShaders])
5272 {
5274 OOGL(glBindFramebuffer(GL_FRAMEBUFFER, defaultDrawFBO));
5275
5276 OOLog(@"universe.profile.secondPassDraw", @"%@", @"Begin second pass draw");
5278 OOLog(@"universe.profile.secondPassDraw", @"%@", @"End second pass drawing");
5279 }
5280 }
5281}
5282
5283
5285{
5286 NSSize viewSize = [gameView viewSize];
5287 if([self useShaders])
5288 {
5289 if ([gameView msaa])
5290 {
5291 // resolve MSAA framebuffer to target framebuffer
5292 OOGL(glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFramebufferID));
5293 OOGL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFramebufferID));
5294 OOGL(glBlitFramebuffer(0, 0, (GLint)viewSize.width, (GLint)viewSize.height, 0, 0, (GLint)viewSize.width, (GLint)viewSize.height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
5295 }
5296 }
5297}
5298
5299
5301{
5302 return framesDoneThisUpdate;
5303}
5304
5305
5310
5311
5312- (OOMatrix) viewMatrix
5313{
5314 return viewMatrix;
5315}
5316
5317
5319{
5321
5322 OOGL(glDisable(GL_TEXTURE_2D)); // for background sheets
5323
5324 float overallAlpha = [[PLAYER hud] overallAlpha];
5325 if (displayGUI)
5326 {
5327 if ([[self gameController] mouseInteractionMode] == MOUSE_MODE_UI_SCREEN_WITH_INTERACTION)
5328 {
5329 cursor_row = [gui drawGUI:1.0 drawCursor:YES];
5330 }
5331 else
5332 {
5333 [gui drawGUI:1.0 drawCursor:NO];
5334 }
5335 }
5336
5337 [message_gui drawGUI:[message_gui alpha] * overallAlpha drawCursor:NO];
5338 [comm_log_gui drawGUI:[comm_log_gui alpha] * overallAlpha drawCursor:NO];
5339
5341}
5342
5343
5344- (void) drawWatermarkString:(NSString *) watermarkString
5345{
5346 NSSize watermarkStringSize = OORectFromString(watermarkString, 0.0f, 0.0f, NSMakeSize(10, 10)).size;
5347
5348 OOGL(glColor4f(0.0, 1.0, 0.0, 1.0));
5349 // position the watermark string on the top right hand corner of the game window and right-align it
5350 OODrawString(watermarkString, MAIN_GUI_PIXEL_WIDTH / 2 - watermarkStringSize.width + 80,
5351 MAIN_GUI_PIXEL_HEIGHT / 2 - watermarkStringSize.height, [gameView display_z], NSMakeSize(10,10));
5352}
5353
5354
5355- (id)entityForUniversalID:(OOUniversalID)u_id
5356{
5357 if (u_id == 100)
5358 return PLAYER; // the player
5359
5360 if (MAX_ENTITY_UID < u_id)
5361 {
5362 OOLog(@"universe.badUID", @"Attempt to retrieve entity for out-of-range UID %u. (This is an internal programming error, please report it.)", u_id);
5363 return nil;
5364 }
5365
5366 if ((u_id == NO_TARGET)||(!entity_for_uid[u_id]))
5367 return nil;
5368
5369 Entity *ent = entity_for_uid[u_id];
5370 if ([ent isEffect]) // effects SHOULD NOT HAVE U_IDs!
5371 {
5372 return nil;
5373 }
5374
5375 if ([ent status] == STATUS_DEAD || [ent status] == STATUS_DOCKED)
5376 {
5377 return nil;
5378 }
5379
5380 return ent;
5381}
5382
5383
5385{
5386 NSCParameterAssert(uni != NULL);
5387 BOOL result = YES;
5388
5389 // DEBUG check for loops and short lists
5390 if (uni->n_entities > 0)
5391 {
5392 int n;
5393 Entity *checkEnt, *last;
5394
5395 last = nil;
5396
5397 n = uni->n_entities;
5398 checkEnt = uni->x_list_start;
5399 while ((n--)&&(checkEnt))
5400 {
5401 last = checkEnt;
5402 checkEnt = checkEnt->x_next;
5403 }
5404 if ((checkEnt)||(n > 0))
5405 {
5406 OOExtraLog(kOOLogEntityVerificationError, @"Broken x_next %@ list (%d) ***", uni->x_list_start, n);
5407 result = NO;
5408 }
5409
5410 n = uni->n_entities;
5411 checkEnt = last;
5412 while ((n--)&&(checkEnt)) checkEnt = checkEnt->x_previous;
5413 if ((checkEnt)||(n > 0))
5414 {
5415 OOExtraLog(kOOLogEntityVerificationError, @"Broken x_previous %@ list (%d) ***", uni->x_list_start, n);
5416 if (result)
5417 {
5418 OOExtraLog(kOOLogEntityVerificationRebuild, @"%@", @"REBUILDING x_previous list from x_next list");
5419 checkEnt = uni->x_list_start;
5420 checkEnt->x_previous = nil;
5421 while (checkEnt->x_next)
5422 {
5423 last = checkEnt;
5424 checkEnt = checkEnt->x_next;
5425 checkEnt->x_previous = last;
5426 }
5427 }
5428 }
5429
5430 n = uni->n_entities;
5431 checkEnt = uni->y_list_start;
5432 while ((n--)&&(checkEnt))
5433 {
5434 last = checkEnt;
5435 checkEnt = checkEnt->y_next;
5436 }
5437 if ((checkEnt)||(n > 0))
5438 {
5439 OOExtraLog(kOOLogEntityVerificationError, @"Broken *** broken y_next %@ list (%d) ***", uni->y_list_start, n);
5440 result = NO;
5441 }
5442
5443 n = uni->n_entities;
5444 checkEnt = last;
5445 while ((n--)&&(checkEnt)) checkEnt = checkEnt->y_previous;
5446 if ((checkEnt)||(n > 0))
5447 {
5448 OOExtraLog(kOOLogEntityVerificationError, @"Broken y_previous %@ list (%d) ***", uni->y_list_start, n);
5449 if (result)
5450 {
5451 OOExtraLog(kOOLogEntityVerificationRebuild, @"%@", @"REBUILDING y_previous list from y_next list");
5452 checkEnt = uni->y_list_start;
5453 checkEnt->y_previous = nil;
5454 while (checkEnt->y_next)
5455 {
5456 last = checkEnt;
5457 checkEnt = checkEnt->y_next;
5458 checkEnt->y_previous = last;
5459 }
5460 }
5461 }
5462
5463 n = uni->n_entities;
5464 checkEnt = uni->z_list_start;
5465 while ((n--)&&(checkEnt))
5466 {
5467 last = checkEnt;
5468 checkEnt = checkEnt->z_next;
5469 }
5470 if ((checkEnt)||(n > 0))
5471 {
5472 OOExtraLog(kOOLogEntityVerificationError, @"Broken z_next %@ list (%d) ***", uni->z_list_start, n);
5473 result = NO;
5474 }
5475
5476 n = uni->n_entities;
5477 checkEnt = last;
5478 while ((n--)&&(checkEnt)) checkEnt = checkEnt->z_previous;
5479 if ((checkEnt)||(n > 0))
5480 {
5481 OOExtraLog(kOOLogEntityVerificationError, @"Broken z_previous %@ list (%d) ***", uni->z_list_start, n);
5482 if (result)
5483 {
5484 OOExtraLog(kOOLogEntityVerificationRebuild, @"%@", @"REBUILDING z_previous list from z_next list");
5485 checkEnt = uni->z_list_start;
5486 NSCAssert(checkEnt != nil, @"Expected z-list to be non-empty."); // Previously an implicit assumption. -- Ahruman 2011-01-25
5487 checkEnt->z_previous = nil;
5488 while (checkEnt->z_next)
5489 {
5490 last = checkEnt;
5491 checkEnt = checkEnt->z_next;
5492 checkEnt->z_previous = last;
5493 }
5494 }
5495 }
5496 }
5497
5498 if (!result)
5499 {
5500 OOExtraLog(kOOLogEntityVerificationRebuild, @"%@", @"Rebuilding all linked lists from scratch");
5501 NSArray *allEntities = uni->entities;
5502 uni->x_list_start = nil;
5503 uni->y_list_start = nil;
5504 uni->z_list_start = nil;
5505
5506 Entity *ent = nil;
5507 foreach (ent, allEntities)
5508 {
5509 ent->x_next = nil;
5510 ent->x_previous = nil;
5511 ent->y_next = nil;
5512 ent->y_previous = nil;
5513 ent->z_next = nil;
5514 ent->z_previous = nil;
5516 }
5517 }
5518
5519 return result;
5520}
5521
5522
5523- (BOOL) addEntity:(Entity *) entity
5524{
5525 if (entity)
5526 {
5527 ShipEntity *se = nil;
5529 OOWaypointEntity *wp = nil;
5530
5531 if (![entity validForAddToUniverse]) return NO;
5532
5533 // don't add things twice!
5534 if ([entities containsObject:entity])
5535 return YES;
5536
5538 {
5539 // throw an exception here...
5540 OOLog(@"universe.addEntity.failed", @"***** Universe cannot addEntity:%@ -- Universe is full (%d entities out of %d)", entity, n_entities, UNIVERSE_MAX_ENTITIES);
5541#ifndef NDEBUG
5542 if (OOLogWillDisplayMessagesInClass(@"universe.maxEntitiesDump")) [self debugDumpEntities];
5543#endif
5544 return NO;
5545 }
5546
5547 if (![entity isEffect])
5548 {
5549 unsigned limiter = UNIVERSE_MAX_ENTITIES;
5550 while (entity_for_uid[next_universal_id] != nil) // skip allocated numbers
5551 {
5552 next_universal_id++; // increment keeps idkeys unique
5554 {
5556 }
5557 if (limiter-- == 0)
5558 {
5559 // Every slot has been tried! This should not happen due to previous test, but there was a problem here in 1.70.
5560 OOLog(@"universe.addEntity.failed", @"***** Universe cannot addEntity:%@ -- Could not find free slot for entity.", entity);
5561 return NO;
5562 }
5563 }
5564 [entity setUniversalID:next_universal_id];
5565 entity_for_uid[next_universal_id] = entity;
5566 if ([entity isShip])
5567 {
5568 se = (ShipEntity *)entity;
5569 if ([se isBeacon])
5570 {
5571 [self setNextBeacon:se];
5572 }
5573 if ([se isStation])
5574 {
5575 // check if it is a proper rotating station (ie. roles contains the word "station")
5576 if ([(StationEntity*)se isRotatingStation])
5577 {
5578 double stationRoll = 0.0;
5579 // check for station_roll override
5580 id definedRoll = [[se shipInfoDictionary] objectForKey:@"station_roll"];
5581
5582 if (definedRoll != nil)
5583 {
5584 stationRoll = OODoubleFromObject(definedRoll, stationRoll);
5585 }
5586 else
5587 {
5588 stationRoll = [[self currentSystemData] oo_doubleForKey:@"station_roll" defaultValue:STANDARD_STATION_ROLL];
5589 }
5590
5591 [se setRoll: stationRoll];
5592 }
5593 else
5594 {
5595 [se setRoll: 0.0];
5596 }
5597 [(StationEntity *)se setPlanet:[self planet]];
5598 if ([se maxFlightSpeed] > 0) se->isExplicitlyNotMainStation = YES; // we never want carriers to become main stations.
5599 }
5600 // stations used to have STATUS_ACTIVE, they're all STATUS_IN_FLIGHT now.
5601 if ([se status] != STATUS_COCKPIT_DISPLAY)
5602 {
5603 [se setStatus:STATUS_IN_FLIGHT];
5604 }
5605 }
5606 }
5607 else
5608 {
5609 [entity setUniversalID:NO_TARGET];
5610 if ([entity isVisualEffect])
5611 {
5612 ve = (OOVisualEffectEntity *)entity;
5613 if ([ve isBeacon])
5614 {
5615 [self setNextBeacon:ve];
5616 }
5617 }
5618 else if ([entity isWaypoint])
5619 {
5620 wp = (OOWaypointEntity *)entity;
5621 if ([wp isBeacon])
5622 {
5623 [self setNextBeacon:wp];
5624 }
5625 }
5626 }
5627
5628 // lighting considerations
5629 entity->isSunlit = YES;
5630 entity->shadingEntityID = NO_TARGET;
5631
5632 // add it to the universe
5633 [entities addObject:entity];
5634 [entity wasAddedToUniverse];
5635
5636 // maintain sorted list (and for the scanner relative position)
5637 HPVector entity_pos = entity->position;
5638 HPVector delta = HPvector_between(entity_pos, PLAYER->position);
5639 double z_distance = HPmagnitude2(delta);
5640 entity->zero_distance = z_distance;
5641 unsigned index = n_entities;
5642 sortedEntities[index] = entity;
5643 entity->zero_index = index;
5644 while ((index > 0)&&(z_distance < sortedEntities[index - 1]->zero_distance)) // bubble into place
5645 {
5647 sortedEntities[index]->zero_index = index;
5648 index--;
5649 sortedEntities[index] = entity;
5650 entity->zero_index = index;
5651 }
5652
5653 // increase n_entities...
5654 n_entities++;
5655
5656 // add entity to linked lists
5657 [entity addToLinkedLists]; // position and universe have been set - so we can do this
5658 if ([entity canCollide]) // filter only collidables disappearing
5659 {
5661 }
5662
5663 if ([entity isWormhole])
5664 {
5665 [activeWormholes addObject:entity];
5666 }
5667 else if ([entity isPlanet])
5668 {
5669 [allPlanets addObject:entity];
5670 }
5671 else if ([entity isShip])
5672 {
5673 [[se getAI] setOwner:se];
5674 [[se getAI] setState:@"GLOBAL"];
5675 if ([entity isStation])
5676 {
5677 [allStations addObject:entity];
5678 }
5679 }
5680
5681 return YES;
5682 }
5683 return NO;
5684}
5685
5686
5687- (BOOL) removeEntity:(Entity *) entity
5688{
5689 if (entity != nil && ![entity isPlayer])
5690 {
5691 /* Ensure entity won't actually be dealloced until the end of this
5692 update (or the next update if none is in progress), because
5693 there may be things pointing to it but not retaining it.
5694 */
5695 [entitiesDeadThisUpdate addObject:entity];
5696 if ([entity isStation])
5697 {
5698 [allStations removeObject:entity];
5699 if ([PLAYER getTargetDockStation] == entity)
5700 {
5701 [PLAYER setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
5702 }
5703 }
5704 return [self doRemoveEntity:entity];
5705 }
5706 return NO;
5707}
5708
5709
5710- (void) ensureEntityReallyRemoved:(Entity *)entity
5711{
5712 if ([entity universalID] != NO_TARGET)
5713 {
5714 OOLog(@"universe.unremovedEntity", @"Entity %@ dealloced without being removed from universe! (This is an internal programming error, please report it.)", entity);
5715 [self doRemoveEntity:entity];
5716 }
5717}
5718
5719
5721{
5722 BOOL updating = no_update;
5723 no_update = YES; // no drawing while we do this!
5724
5725#ifndef NDEBUG
5726 Entity* p0 = [entities objectAtIndex:0];
5727 if (!(p0->isPlayer))
5728 {
5729 OOLog(kOOLogInconsistentState, @"%@", @"***** First entity is not the player in Universe.removeAllEntitiesExceptPlayer - exiting.");
5730 exit(EXIT_FAILURE);
5731 }
5732#endif
5733
5734 // preserve wormholes
5735 NSMutableArray *savedWormholes = [activeWormholes mutableCopy];
5736
5737 while ([entities count] > 1)
5738 {
5739 Entity* ent = [entities objectAtIndex:1];
5740 if (ent->isStation) // clear out queues
5741 [(StationEntity *)ent clear];
5742 if (EXPECT(![ent isVisualEffect]))
5743 {
5744 [self removeEntity:ent];
5745 }
5746 else
5747 {
5748 // this will ensure the effectRemoved script event will run
5750 }
5751 }
5752
5753 [activeWormholes release];
5754 activeWormholes = savedWormholes; // will be cleared out by populateSpaceFromActiveWormholes
5755
5756 // maintain sorted list
5757 n_entities = 1;
5758
5759 cachedSun = nil;
5760 cachedPlanet = nil;
5762 [closeSystems release];
5763 closeSystems = nil;
5764
5765 [self resetBeacons];
5766 [waypoints removeAllObjects];
5767
5768 no_update = updating; // restore drawing
5769}
5770
5771
5773{
5774 int i;
5775 int ent_count = n_entities;
5776 if (ent_count > 0)
5777 {
5778 Entity* ent;
5779 for (i = 0; i < ent_count; i++)
5780 {
5781 ent = sortedEntities[i];
5782 if ([ent status] == STATUS_COCKPIT_DISPLAY && ![ent isPlayer])
5783 {
5784 [self removeEntity:ent];
5785 }
5786 }
5787 }
5788 demo_ship = nil;
5789}
5790
5791
5792- (ShipEntity *) makeDemoShipWithRole:(NSString *)role spinning:(BOOL)spinning
5793{
5794 if ([PLAYER dockedStation] == nil) return nil;
5795
5796 [self removeDemoShips]; // get rid of any pre-existing models on display
5797
5798 [PLAYER setShowDemoShips: YES];
5799 Quaternion q2 = { (GLfloat)M_SQRT1_2, (GLfloat)M_SQRT1_2, (GLfloat)0.0, (GLfloat)0.0 };
5800
5801 ShipEntity *ship = [self newShipWithRole:role]; // retain count = 1
5802 if (ship)
5803 {
5804 double cr = [ship collisionRadius];
5805 [ship setOrientation:q2];
5806 [ship setPositionX:0.0f y:0.0f z:3.6f * cr];
5807 [ship setScanClass:CLASS_NO_DRAW];
5808 [ship switchAITo:@"nullAI.plist"];
5810
5811 [UNIVERSE addEntity:ship]; // STATUS_IN_FLIGHT, AI state GLOBAL
5812
5813 if (spinning)
5814 {
5815 [ship setDemoShip: 1.0f];
5816 }
5817 else
5818 {
5819 [ship setDemoShip: 0.0f];
5820 }
5821 [ship setStatus:STATUS_COCKPIT_DISPLAY];
5822 // stop problems on the ship library screen
5823 // demo ships shouldn't have this equipment
5824 [ship removeEquipmentItem:@"EQ_SHIELD_BOOSTER"];
5825 [ship removeEquipmentItem:@"EQ_SHIELD_ENHANCER"];
5826 }
5827
5828 return [ship autorelease];
5829}
5830
5831
5832- (BOOL) isVectorClearFromEntity:(Entity *) e1 toDistance:(double)dist fromPoint:(HPVector) p2
5833{
5834 if (!e1)
5835 return NO;
5836
5837 HPVector f1;
5838 HPVector p1 = e1->position;
5839 HPVector v1 = p2;
5840 v1.x -= p1.x; v1.y -= p1.y; v1.z -= p1.z; // vector from entity to p2
5841
5842 double nearest = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z) - dist; // length of vector
5843
5844 if (nearest < 0.0)
5845 return YES; // within range already!
5846
5847 int i;
5848 int ent_count = n_entities;
5849 Entity* my_entities[ent_count];
5850 for (i = 0; i < ent_count; i++)
5851 my_entities[i] = [sortedEntities[i] retain]; // retained
5852
5853 if (v1.x || v1.y || v1.z)
5854 f1 = HPvector_normal(v1); // unit vector in direction of p2 from p1
5855 else
5856 f1 = make_HPvector(0, 0, 1);
5857
5858 for (i = 0; i < ent_count ; i++)
5859 {
5860 Entity *e2 = my_entities[i];
5861 if ((e2 != e1)&&([e2 canCollide]))
5862 {
5863 HPVector epos = e2->position;
5864 epos.x -= p1.x; epos.y -= p1.y; epos.z -= p1.z; // epos now holds vector from p1 to this entities position
5865
5866 double d_forward = HPdot_product(epos,f1); // distance along f1 which is nearest to e2's position
5867
5868 if ((d_forward > 0)&&(d_forward < nearest))
5869 {
5870 double cr = 1.10 * (e2->collision_radius + e1->collision_radius); // 10% safety margin
5871 HPVector p0 = e1->position;
5872 p0.x += d_forward * f1.x; p0.y += d_forward * f1.y; p0.z += d_forward * f1.z;
5873 // p0 holds nearest point on current course to center of incident object
5874 HPVector epos = e2->position;
5875 p0.x -= epos.x; p0.y -= epos.y; p0.z -= epos.z;
5876 // compare with center of incident object
5877 double dist2 = p0.x * p0.x + p0.y * p0.y + p0.z * p0.z;
5878 if (dist2 < cr*cr)
5879 {
5880 for (i = 0; i < ent_count; i++)
5881 [my_entities[i] release]; // released
5882 return NO;
5883 }
5884 }
5885 }
5886 }
5887 for (i = 0; i < ent_count; i++)
5888 [my_entities[i] release]; // released
5889 return YES;
5890}
5891
5892
5893- (Entity*) hazardOnRouteFromEntity:(Entity *) e1 toDistance:(double)dist fromPoint:(HPVector) p2
5894{
5895 if (!e1)
5896 return nil;
5897
5898 HPVector f1;
5899 HPVector p1 = e1->position;
5900 HPVector v1 = p2;
5901 v1.x -= p1.x; v1.y -= p1.y; v1.z -= p1.z; // vector from entity to p2
5902
5903 double nearest = HPmagnitude(v1) - dist; // length of vector
5904
5905 if (nearest < 0.0)
5906 return nil; // within range already!
5907
5908 Entity* result = nil;
5909 int i;
5910 int ent_count = n_entities;
5911 Entity* my_entities[ent_count];
5912 for (i = 0; i < ent_count; i++)
5913 my_entities[i] = [sortedEntities[i] retain]; // retained
5914
5915 if (v1.x || v1.y || v1.z)
5916 f1 = HPvector_normal(v1); // unit vector in direction of p2 from p1
5917 else
5918 f1 = make_HPvector(0, 0, 1);
5919
5920 for (i = 0; (i < ent_count) && (!result) ; i++)
5921 {
5922 Entity *e2 = my_entities[i];
5923 if ((e2 != e1)&&([e2 canCollide]))
5924 {
5925 HPVector epos = e2->position;
5926 epos.x -= p1.x; epos.y -= p1.y; epos.z -= p1.z; // epos now holds vector from p1 to this entities position
5927
5928 double d_forward = HPdot_product(epos,f1); // distance along f1 which is nearest to e2's position
5929
5930 if ((d_forward > 0)&&(d_forward < nearest))
5931 {
5932 double cr = 1.10 * (e2->collision_radius + e1->collision_radius); // 10% safety margin
5933 HPVector p0 = e1->position;
5934 p0.x += d_forward * f1.x; p0.y += d_forward * f1.y; p0.z += d_forward * f1.z;
5935 // p0 holds nearest point on current course to center of incident object
5936 HPVector epos = e2->position;
5937 p0.x -= epos.x; p0.y -= epos.y; p0.z -= epos.z;
5938 // compare with center of incident object
5939 double dist2 = HPmagnitude2(p0);
5940 if (dist2 < cr*cr)
5941 result = e2;
5942 }
5943 }
5944 }
5945 for (i = 0; i < ent_count; i++)
5946 [my_entities[i] release]; // released
5947 return result;
5948}
5949
5950
5951- (HPVector) getSafeVectorFromEntity:(Entity *) e1 toDistance:(double)dist fromPoint:(HPVector) p2
5952{
5953 // heuristic three
5954
5955 if (!e1)
5956 {
5957 OOLog(kOOLogParameterError, @"%@", @"***** No entity set in Universe getSafeVectorFromEntity:toDistance:fromPoint:");
5958 return kZeroHPVector;
5959 }
5960
5961 HPVector f1;
5962 HPVector result = p2;
5963 int i;
5964 int ent_count = n_entities;
5965 Entity* my_entities[ent_count];
5966 for (i = 0; i < ent_count; i++)
5967 my_entities[i] = [sortedEntities[i] retain]; // retained
5968 HPVector p1 = e1->position;
5969 HPVector v1 = p2;
5970 v1.x -= p1.x; v1.y -= p1.y; v1.z -= p1.z; // vector from entity to p2
5971
5972 double nearest = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z) - dist; // length of vector
5973
5974 if (v1.x || v1.y || v1.z)
5975 f1 = HPvector_normal(v1); // unit vector in direction of p2 from p1
5976 else
5977 f1 = make_HPvector(0, 0, 1);
5978
5979 for (i = 0; i < ent_count; i++)
5980 {
5981 Entity *e2 = my_entities[i];
5982 if ((e2 != e1)&&([e2 canCollide]))
5983 {
5984 HPVector epos = e2->position;
5985 epos.x -= p1.x; epos.y -= p1.y; epos.z -= p1.z;
5986 double d_forward = HPdot_product(epos,f1);
5987 if ((d_forward > 0)&&(d_forward < nearest))
5988 {
5989 double cr = 1.20 * (e2->collision_radius + e1->collision_radius); // 20% safety margin
5990
5991 HPVector p0 = e1->position;
5992 p0.x += d_forward * f1.x; p0.y += d_forward * f1.y; p0.z += d_forward * f1.z;
5993 // p0 holds nearest point on current course to center of incident object
5994
5995 HPVector epos = e2->position;
5996 p0.x -= epos.x; p0.y -= epos.y; p0.z -= epos.z;
5997 // compare with center of incident object
5998
5999 double dist2 = p0.x * p0.x + p0.y * p0.y + p0.z * p0.z;
6000
6001 if (dist2 < cr*cr)
6002 {
6003 result = e2->position; // center of incident object
6004 nearest = d_forward;
6005
6006 if (dist2 == 0.0)
6007 {
6008 // ie. we're on a line through the object's center !
6009 // jitter the position somewhat!
6010 result.x += ((int)(Ranrot() % 1024) - 512)/512.0; // -1.0 .. +1.0
6011 result.y += ((int)(Ranrot() % 1024) - 512)/512.0; // -1.0 .. +1.0
6012 result.z += ((int)(Ranrot() % 1024) - 512)/512.0; // -1.0 .. +1.0
6013 }
6014
6015 HPVector nearest_point = p1;
6016 nearest_point.x += d_forward * f1.x; nearest_point.y += d_forward * f1.y; nearest_point.z += d_forward * f1.z;
6017 // nearest point now holds nearest point on line to center of incident object
6018
6019 HPVector outward = nearest_point;
6020 outward.x -= result.x; outward.y -= result.y; outward.z -= result.z;
6021 if (outward.x||outward.y||outward.z)
6022 outward = HPvector_normal(outward);
6023 else
6024 outward.y = 1.0;
6025 // outward holds unit vector through the nearest point on the line from the center of incident object
6026
6027 HPVector backward = p1;
6028 backward.x -= result.x; backward.y -= result.y; backward.z -= result.z;
6029 if (backward.x||backward.y||backward.z)
6030 backward = HPvector_normal(backward);
6031 else
6032 backward.z = -1.0;
6033 // backward holds unit vector from center of the incident object to the center of the ship
6034
6035 HPVector dd = result;
6036 dd.x -= p1.x; dd.y -= p1.y; dd.z -= p1.z;
6037 double current_distance = HPmagnitude(dd);
6038
6039 // sanity check current_distance
6040 if (current_distance < cr * 1.25) // 25% safety margin
6041 current_distance = cr * 1.25;
6042 if (current_distance > cr * 5.0) // up to 2 diameters away
6043 current_distance = cr * 5.0;
6044
6045 // choose a point that's three parts backward and one part outward
6046
6047 result.x += 0.25 * (outward.x * current_distance) + 0.75 * (backward.x * current_distance); // push 'out' by this amount
6048 result.y += 0.25 * (outward.y * current_distance) + 0.75 * (backward.y * current_distance);
6049 result.z += 0.25 * (outward.z * current_distance) + 0.75 * (backward.z * current_distance);
6050
6051 }
6052 }
6053 }
6054 }
6055 for (i = 0; i < ent_count; i++)
6056 [my_entities[i] release]; // released
6057 return result;
6058}
6059
6060
6061- (ShipEntity*) addWreckageFrom:(ShipEntity *)ship withRole:(NSString *)wreckRole at:(HPVector)rpos scale:(GLfloat)scale lifetime:(GLfloat)lifetime
6062{
6063 ShipEntity* wreck = [UNIVERSE newShipWithRole:wreckRole]; // retain count = 1
6064 Quaternion q;
6065 if (wreck)
6066 {
6067 GLfloat expected_mass = 0.1f * [ship mass] * (0.75 + 0.5 * randf());
6068 GLfloat wreck_mass = [wreck mass];
6069 GLfloat scale_factor = powf(expected_mass / wreck_mass, 0.33333333f) * scale; // cube root of volume ratio
6070 [wreck rescaleBy:scale_factor writeToCache:NO];
6071
6072 [wreck setPosition:rpos];
6073
6075
6078
6079 [wreck setTemperature: 1000.0]; // take 1000e heat damage per second
6080 [wreck setHeatInsulation: 1.0e7]; // very large! so it won't cool down
6081 [wreck setEnergy: lifetime];
6082
6083 [wreck setIsWreckage:YES];
6084
6085 [UNIVERSE addEntity:wreck]; // STATUS_IN_FLIGHT, AI state GLOBAL
6087 // [wreck rescaleBy: 1.0/scale_factor];
6088 [wreck release];
6089 }
6090 return wreck;
6091}
6092
6093
6094
6095- (void) addLaserHitEffectsAt:(HPVector)pos against:(ShipEntity *)target damage:(float)damage color:(OOColor *)color
6096{
6097 // low energy, start getting small surface explosions
6098 if ([target showDamage] && [target energy] < [target maxEnergy]/2)
6099 {
6100 NSString *key = (randf() < 0.5) ? @"oolite-hull-spark" : @"oolite-hull-spark-b";
6101 NSDictionary *settings = [UNIVERSE explosionSetting:key];
6103 [burst setPosition:pos];
6104 [self addEntity: burst];
6105 if ([target energy] * randf() < damage)
6106 {
6107 ShipEntity *wreck = [self addWreckageFrom:target withRole:@"oolite-wreckage-chunk" at:pos scale:0.05 lifetime:(125.0+(randf()*200.0))];
6108 if (wreck)
6109 {
6110 Vector direction = HPVectorToVector(HPvector_normal(HPvector_subtract(pos,[target position])));
6111 [wreck setVelocity:vector_add([wreck velocity],vector_multiply_scalar(direction,10+20*randf()))];
6112 }
6113 }
6114 }
6115 else
6116 {
6118 }
6119}
6120
6121
6122- (ShipEntity *) firstShipHitByLaserFromShip:(ShipEntity *)srcEntity inDirection:(OOWeaponFacing)direction offset:(Vector)offset gettingRangeFound:(GLfloat *)range_ptr
6123{
6124 if (srcEntity == nil) return nil;
6125
6126 ShipEntity *hit_entity = nil;
6127 ShipEntity *hit_subentity = nil;
6128 HPVector p0 = [srcEntity position];
6129 Quaternion q1 = [srcEntity normalOrientation];
6130 ShipEntity *parent = [srcEntity parentEntity];
6131
6132 if (parent)
6133 {
6134 // we're a subentity!
6135 BoundingBox bbox = [srcEntity boundingBox];
6136 HPVector midfrontplane = make_HPvector(0.5 * (bbox.max.x + bbox.min.x), 0.5 * (bbox.max.y + bbox.min.y), bbox.max.z);
6137 p0 = [srcEntity absolutePositionForSubentityOffset:midfrontplane];
6138 q1 = [parent orientation];
6139 if ([parent isPlayer]) q1.w = -q1.w;
6140 }
6141
6142 double nearest = [srcEntity weaponRange];
6143 int i;
6144 int ent_count = n_entities;
6145 int ship_count = 0;
6146 ShipEntity *my_entities[ent_count];
6147
6148 for (i = 0; i < ent_count; i++)
6149 {
6150 Entity* ent = sortedEntities[i];
6151 if (ent != srcEntity && ent != parent && [ent isShip] && [ent canCollide])
6152 {
6153 my_entities[ship_count++] = [(ShipEntity *)ent retain];
6154 }
6155 }
6156
6157
6158 Vector u1, f1, r1;
6159 basis_vectors_from_quaternion(q1, &r1, &u1, &f1);
6160 p0 = HPvector_add(p0, vectorToHPVector(OOVectorMultiplyMatrix(offset, OOMatrixFromBasisVectors(r1, u1, f1))));
6161
6162 switch (direction)
6163 {
6165 case WEAPON_FACING_NONE:
6166 break;
6167
6168 case WEAPON_FACING_AFT:
6170 break;
6171
6172 case WEAPON_FACING_PORT:
6173 quaternion_rotate_about_axis(&q1, u1, M_PI/2.0);
6174 break;
6175
6177 quaternion_rotate_about_axis(&q1, u1, -M_PI/2.0);
6178 break;
6179 }
6180
6181 basis_vectors_from_quaternion(q1, &r1, NULL, &f1);
6182 HPVector p1 = HPvector_add(p0, vectorToHPVector(vector_multiply_scalar(f1, nearest))); //endpoint
6183
6184 for (i = 0; i < ship_count; i++)
6185 {
6186 ShipEntity *e2 = my_entities[i];
6187
6188 // check outermost bounding sphere
6189 GLfloat cr = e2->collision_radius;
6190 Vector rpos = HPVectorToVector(HPvector_subtract(e2->position, p0));
6191 Vector v_off = make_vector(dot_product(rpos, r1), dot_product(rpos, u1), dot_product(rpos, f1));
6192 if (v_off.z > 0.0 && v_off.z < nearest + cr && // ahead AND within range
6193 v_off.x < cr && v_off.x > -cr && v_off.y < cr && v_off.y > -cr && // AND not off to one side or another
6194 v_off.x * v_off.x + v_off.y * v_off.y < cr * cr) // AND not off to both sides
6195 {
6196 ShipEntity *entHit = nil;
6197 GLfloat hit = [(ShipEntity *)e2 doesHitLine:p0 :p1 :&entHit]; // octree detection
6198
6199 if (hit > 0.0 && hit < nearest)
6200 {
6201 if ([entHit isSubEntity])
6202 {
6203 hit_subentity = entHit;
6204 }
6205 hit_entity = e2;
6206 nearest = hit;
6207 p1 = HPvector_add(p0, vectorToHPVector(vector_multiply_scalar(f1, nearest)));
6208 }
6209 }
6210 }
6211
6212 if (hit_entity)
6213 {
6214 // I think the above code does not guarantee that the closest hit_subentity belongs to the closest hit_entity.
6215 if (hit_subentity && [hit_subentity owner] == hit_entity) [hit_entity setSubEntityTakingDamage:hit_subentity];
6216
6217 if (range_ptr != NULL)
6218 {
6219 *range_ptr = nearest;
6220 }
6221 }
6222
6223 for (i = 0; i < ship_count; i++) [my_entities[i] release]; // released
6224
6225 return hit_entity;
6226}
6227
6228
6230{
6231 PlayerEntity *player = PLAYER;
6232 Entity *hit_entity = nil;
6233 OOScalar nearest2 = SCANNER_MAX_RANGE - 100; // 100m shorter than range at which target is lost
6234 nearest2 *= nearest2;
6235 int i;
6236 int ent_count = n_entities;
6237 int ship_count = 0;
6238 Entity *my_entities[ent_count];
6239
6240 for (i = 0; i < ent_count; i++)
6241 {
6242 if (([sortedEntities[i] isShip] && ![sortedEntities[i] isPlayer]) || [sortedEntities[i] isWormhole])
6243 {
6244 my_entities[ship_count++] = [sortedEntities[i] retain];
6245 }
6246 }
6247
6248 Quaternion q1 = [player normalOrientation];
6249 Vector u1, f1, r1;
6250 basis_vectors_from_quaternion(q1, &r1, &u1, &f1);
6251 Vector offset = [player weaponViewOffset];
6252
6253 HPVector p1 = HPvector_add([player position], vectorToHPVector(OOVectorMultiplyMatrix(offset, OOMatrixFromBasisVectors(r1, u1, f1))));
6254
6255 // Note: deliberately tied to view direction, not weapon facing. All custom views count as forward for targeting.
6256 switch (viewDirection)
6257 {
6258 case VIEW_AFT :
6260 break;
6261 case VIEW_PORT :
6262 quaternion_rotate_about_axis(&q1, u1, 0.5 * M_PI);
6263 break;
6264 case VIEW_STARBOARD :
6265 quaternion_rotate_about_axis(&q1, u1, -0.5 * M_PI);
6266 break;
6267 default:
6268 break;
6269 }
6270 basis_vectors_from_quaternion(q1, &r1, NULL, &f1);
6271
6272 for (i = 0; i < ship_count; i++)
6273 {
6274 Entity *e2 = my_entities[i];
6275 if ([e2 canCollide] && [e2 scanClass] != CLASS_NO_DRAW)
6276 {
6277 Vector rp = HPVectorToVector(HPvector_subtract([e2 position], p1));
6278 OOScalar dist2 = magnitude2(rp);
6279 if (dist2 < nearest2)
6280 {
6281 OOScalar df = dot_product(f1, rp);
6282 if (df > 0.0 && df * df < nearest2)
6283 {
6284 OOScalar du = dot_product(u1, rp);
6285 OOScalar dr = dot_product(r1, rp);
6287 if (du * du + dr * dr < cr * cr)
6288 {
6289 hit_entity = e2;
6290 nearest2 = dist2;
6291 }
6292 }
6293 }
6294 }
6295 }
6296 // check for MASC'M
6297 if (hit_entity != nil && [hit_entity isShip])
6298 {
6299 ShipEntity* ship = (ShipEntity*)hit_entity;
6300 if ([ship isJammingScanning] && ![player hasMilitaryScannerFilter])
6301 {
6302 hit_entity = nil;
6303 }
6304 }
6305
6306 for (i = 0; i < ship_count; i++)
6307 {
6308 [my_entities[i] release];
6309 }
6310
6311 return hit_entity;
6312}
6313
6314
6316{
6317 OOWeaponFacing targetFacing;
6318 Vector laserPortOffset = kZeroVector;
6319 PlayerEntity *player = PLAYER;
6320
6321 switch (viewDirection)
6322 {
6323 case VIEW_FORWARD:
6324 targetFacing = WEAPON_FACING_FORWARD;
6325 laserPortOffset = [[player forwardWeaponOffset] oo_vectorAtIndex:0];
6326 break;
6327
6328 case VIEW_AFT:
6329 targetFacing = WEAPON_FACING_AFT;
6330 laserPortOffset = [[player aftWeaponOffset] oo_vectorAtIndex:0];
6331 break;
6332
6333 case VIEW_PORT:
6334 targetFacing = WEAPON_FACING_PORT;
6335 laserPortOffset = [[player portWeaponOffset] oo_vectorAtIndex:0];
6336 break;
6337
6338 case VIEW_STARBOARD:
6339 targetFacing = WEAPON_FACING_STARBOARD;
6340 laserPortOffset = [[player starboardWeaponOffset] oo_vectorAtIndex:0];
6341 break;
6342
6343 default:
6344 // Match behaviour of -firstEntityTargetedByPlayer.
6345 targetFacing = WEAPON_FACING_FORWARD;
6346 laserPortOffset = [[player forwardWeaponOffset] oo_vectorAtIndex:0];
6347 }
6348
6349 return [self firstShipHitByLaserFromShip:PLAYER inDirection:targetFacing offset:laserPortOffset gettingRangeFound:NULL];
6350}
6351
6352
6353- (NSArray *) entitiesWithinRange:(double)range ofEntity:(Entity *)entity
6354{
6355 if (entity == nil) return nil;
6356
6357 return [self findShipsMatchingPredicate:YESPredicate
6358 parameter:NULL
6359 inRange:range
6360 ofEntity:entity];
6361}
6362
6363
6364- (unsigned) countShipsWithRole:(NSString *)role inRange:(double)range ofEntity:(Entity *)entity
6365{
6366 return [self countShipsMatchingPredicate:HasRolePredicate
6367 parameter:role
6368 inRange:range
6369 ofEntity:entity];
6370}
6371
6372
6373- (unsigned) countShipsWithRole:(NSString *)role
6374{
6375 return [self countShipsWithRole:role inRange:-1 ofEntity:nil];
6376}
6377
6378
6379- (unsigned) countShipsWithPrimaryRole:(NSString *)role inRange:(double)range ofEntity:(Entity *)entity
6380{
6381 return [self countShipsMatchingPredicate:HasPrimaryRolePredicate
6382 parameter:role
6383 inRange:range
6384 ofEntity:entity];
6385}
6386
6387
6388- (unsigned) countShipsWithScanClass:(OOScanClass)scanClass inRange:(double)range ofEntity:(Entity *)entity
6389{
6390 return [self countShipsMatchingPredicate:HasScanClassPredicate
6391 parameter:[NSNumber numberWithInt:scanClass]
6392 inRange:range
6393 ofEntity:entity];
6394}
6395
6396
6397- (unsigned) countShipsWithPrimaryRole:(NSString *)role
6398{
6399 return [self countShipsWithPrimaryRole:role inRange:-1 ofEntity:nil];
6400}
6401
6402
6403- (unsigned) countEntitiesMatchingPredicate:(EntityFilterPredicate)predicate
6404 parameter:(void *)parameter
6405 inRange:(double)range
6406 ofEntity:(Entity *)e1
6407{
6408 unsigned i, found = 0;
6409 HPVector p1;
6410 double distance, cr;
6411
6412 if (predicate == NULL) predicate = YESPredicate;
6413
6414 if (e1 != nil) p1 = e1->position;
6415 else p1 = kZeroHPVector;
6416
6417 for (i = 0; i < n_entities; i++)
6418 {
6419 Entity *e2 = sortedEntities[i];
6420 if (e2 != e1 && predicate(e2, parameter))
6421 {
6422 if (range < 0) distance = -1; // Negative range means infinity
6423 else
6424 {
6425 cr = range + e2->collision_radius;
6426 distance = HPdistance2(e2->position, p1) - cr * cr;
6427 }
6428 if (distance < 0)
6429 {
6430 found++;
6431 }
6432 }
6433 }
6434
6435 return found;
6436}
6437
6438
6439- (unsigned) countShipsMatchingPredicate:(EntityFilterPredicate)predicate
6440 parameter:(void *)parameter
6441 inRange:(double)range
6442 ofEntity:(Entity *)entity
6443{
6444 if (predicate != NULL)
6445 {
6447 {
6448 IsShipPredicate, NULL,
6449 predicate, parameter
6450 };
6451
6452 return [self countEntitiesMatchingPredicate:ANDPredicate
6453 parameter:&param
6454 inRange:range
6455 ofEntity:entity];
6456 }
6457 else
6458 {
6459 return [self countEntitiesMatchingPredicate:IsShipPredicate
6460 parameter:NULL
6461 inRange:range
6462 ofEntity:entity];
6463 }
6464}
6465
6466
6467OOINLINE BOOL EntityInRange(HPVector p1, Entity *e2, float range)
6468{
6469 if (range < 0) return YES;
6470 float cr = range + e2->collision_radius;
6471 return HPdistance2(e2->position,p1) < cr * cr;
6472}
6473
6474
6475// NOTE: OOJSSystem relies on this returning entities in distance-from-player order.
6476// This can be easily changed by removing the [reference isPlayer] conditions in FindJSVisibleEntities().
6477- (NSMutableArray *) findEntitiesMatchingPredicate:(EntityFilterPredicate)predicate
6478 parameter:(void *)parameter
6479 inRange:(double)range
6480 ofEntity:(Entity *)e1
6481{
6483
6484 unsigned i;
6485 HPVector p1;
6486 NSMutableArray *result = nil;
6487
6489
6490 if (predicate == NULL) predicate = YESPredicate;
6491
6492 result = [NSMutableArray arrayWithCapacity:n_entities];
6493
6494 if (e1 != nil) p1 = [e1 position];
6495 else p1 = kZeroHPVector;
6496
6497 for (i = 0; i < n_entities; i++)
6498 {
6499 Entity *e2 = sortedEntities[i];
6500
6501 if (e1 != e2 &&
6502 EntityInRange(p1, e2, range) &&
6503 predicate(e2, parameter))
6504 {
6505 [result addObject:e2];
6506 }
6507 }
6508
6510
6511 return result;
6512
6514}
6515
6516
6517- (id) findOneEntityMatchingPredicate:(EntityFilterPredicate)predicate
6518 parameter:(void *)parameter
6519{
6520 unsigned i;
6521 Entity *candidate = nil;
6522
6524
6525 if (predicate == NULL) predicate = YESPredicate;
6526
6527 for (i = 0; i < n_entities; i++)
6528 {
6529 candidate = sortedEntities[i];
6530 if (predicate(candidate, parameter)) return candidate;
6531 }
6532
6534
6535 return nil;
6536}
6537
6538
6539- (NSMutableArray *) findShipsMatchingPredicate:(EntityFilterPredicate)predicate
6540 parameter:(void *)parameter
6541 inRange:(double)range
6542 ofEntity:(Entity *)entity
6543{
6544 if (predicate != NULL)
6545 {
6547 {
6548 IsShipPredicate, NULL,
6549 predicate, parameter
6550 };
6551
6552 return [self findEntitiesMatchingPredicate:ANDPredicate
6553 parameter:&param
6554 inRange:range
6555 ofEntity:entity];
6556 }
6557 else
6558 {
6559 return [self findEntitiesMatchingPredicate:IsShipPredicate
6560 parameter:NULL
6561 inRange:range
6562 ofEntity:entity];
6563 }
6564}
6565
6566
6567- (NSMutableArray *) findVisualEffectsMatchingPredicate:(EntityFilterPredicate)predicate
6568 parameter:(void *)parameter
6569 inRange:(double)range
6570 ofEntity:(Entity *)entity
6571{
6572 if (predicate != NULL)
6573 {
6575 {
6577 predicate, parameter
6578 };
6579
6580 return [self findEntitiesMatchingPredicate:ANDPredicate
6581 parameter:&param
6582 inRange:range
6583 ofEntity:entity];
6584 }
6585 else
6586 {
6587 return [self findEntitiesMatchingPredicate:IsVisualEffectPredicate
6588 parameter:NULL
6589 inRange:range
6590 ofEntity:entity];
6591 }
6592}
6593
6594
6595- (id) nearestEntityMatchingPredicate:(EntityFilterPredicate)predicate
6596 parameter:(void *)parameter
6597 relativeToEntity:(Entity *)entity
6598{
6599 unsigned i;
6600 HPVector p1;
6601 float rangeSq = INFINITY;
6602 id result = nil;
6603
6604 if (predicate == NULL) predicate = YESPredicate;
6605
6606 if (entity != nil) p1 = [entity position];
6607 else p1 = kZeroHPVector;
6608
6609 for (i = 0; i < n_entities; i++)
6610 {
6611 Entity *e2 = sortedEntities[i];
6612 float distanceToReferenceEntitySquared = (float)HPdistance2(p1, [e2 position]);
6613
6614 if (entity != e2 &&
6615 distanceToReferenceEntitySquared < rangeSq &&
6616 predicate(e2, parameter))
6617 {
6618 result = e2;
6619 rangeSq = distanceToReferenceEntitySquared;
6620 }
6621 }
6622
6623 return [[result retain] autorelease];
6624}
6625
6626
6627- (id) nearestShipMatchingPredicate:(EntityFilterPredicate)predicate
6628 parameter:(void *)parameter
6629 relativeToEntity:(Entity *)entity
6630{
6631 if (predicate != NULL)
6632 {
6634 {
6635 IsShipPredicate, NULL,
6636 predicate, parameter
6637 };
6638
6639 return [self nearestEntityMatchingPredicate:ANDPredicate
6640 parameter:&param
6641 relativeToEntity:entity];
6642 }
6643 else
6644 {
6645 return [self nearestEntityMatchingPredicate:IsShipPredicate
6646 parameter:NULL
6647 relativeToEntity:entity];
6648 }
6649}
6650
6651
6653{
6654 return universal_time;
6655}
6656
6657
6659{
6660 return time_delta;
6661}
6662
6663
6665{
6666 unsigned i;
6667
6669
6670 for (i = 0; i < n_entities; i++)
6671 {
6672 [universeRegion checkEntity:sortedEntities[i]]; // sorts out which region it's in
6673 }
6674
6675 if (![[self gameController] isGamePaused])
6676 {
6678 }
6679
6680 // do check for entities that can't see the sun!
6682}
6683
6684
6686{
6688 else return @"-";
6689}
6690
6691
6693{
6694 dumpCollisionInfo = YES;
6695}
6696
6697
6699{
6700 return viewDirection;
6701}
6702
6703
6704- (void) setViewDirection:(OOViewID) vd
6705{
6706 NSString *ms = nil;
6707 BOOL guiSelected = NO;
6708
6709 if ((viewDirection == vd) && (vd != VIEW_CUSTOM) && (!displayGUI))
6710 return;
6711
6712 switch (vd)
6713 {
6714 case VIEW_FORWARD:
6715 ms = DESC(@"forward-view-string");
6716 break;
6717
6718 case VIEW_AFT:
6719 ms = DESC(@"aft-view-string");
6720 break;
6721
6722 case VIEW_PORT:
6723 ms = DESC(@"port-view-string");
6724 break;
6725
6726 case VIEW_STARBOARD:
6727 ms = DESC(@"starboard-view-string");
6728 break;
6729
6730 case VIEW_CUSTOM:
6731 ms = [PLAYER customViewDescription];
6732 break;
6733
6734 case VIEW_GUI_DISPLAY:
6735 [self setDisplayText:YES];
6736 [self setMainLightPosition:(Vector){ DEMO_LIGHT_POSITION }];
6737 guiSelected = YES;
6738 break;
6739
6740 default:
6741 guiSelected = YES;
6742 break;
6743 }
6744
6745 if (guiSelected)
6746 {
6748 }
6749 else
6750 {
6751 displayGUI = NO; // switch off any text displays
6753 }
6754
6755 if (viewDirection != vd || viewDirection == VIEW_CUSTOM)
6756 {
6757 #if (ALLOW_CUSTOM_VIEWS_WHILE_PAUSED)
6758 BOOL gamePaused = [[self gameController] isGamePaused];
6759 #else
6760 BOOL gamePaused = NO;
6761 #endif
6762 // view notifications for when the player switches to/from gui!
6763 //if (EXPECT(viewDirection == VIEW_GUI_DISPLAY || vd == VIEW_GUI_DISPLAY )) [PLAYER noteViewDidChangeFrom:viewDirection toView:vd];
6764 viewDirection = vd;
6765 if (ms && !gamePaused)
6766 {
6767 [self addMessage:ms forCount:3];
6768 }
6769 else if (gamePaused)
6770 {
6772 }
6773 }
6774}
6775
6776
6777- (void) enterGUIViewModeWithMouseInteraction:(BOOL)mouseInteraction
6778{
6780 [self setViewDirection:VIEW_GUI_DISPLAY];
6781 if (viewDirection != vd) {
6782 PlayerEntity *player = PLAYER;
6783 JSContext *context = OOJSAcquireContext();
6784 ShipScriptEvent(context, player, "viewDirectionChanged", OOJSValueFromViewID(context, viewDirection), OOJSValueFromViewID(context, vd));
6785 OOJSRelinquishContext(context);
6786 }
6788}
6789
6790
6791- (NSString *) soundNameForCustomSoundKey:(NSString *)key
6792{
6793 NSString *result = nil;
6794 NSMutableSet *seen = nil;
6795 id object = [customSounds objectForKey:key];
6796
6797 if ([object isKindOfClass:[NSArray class]] && [object count] > 0)
6798 {
6799 key = [object oo_stringAtIndex:Ranrot() % [object count]];
6800 }
6801 else
6802 {
6803 object=nil;
6804 }
6805
6806 result = [[OOCacheManager sharedCache] objectForKey:key inCache:@"resolved custom sounds"];
6807 if (result == nil)
6808 {
6809 // Resolve sound, allowing indirection within customsounds.plist
6810 seen = [NSMutableSet set];
6811 result = key;
6812 if (object == nil || ([result hasPrefix:@"["] && [result hasSuffix:@"]"]))
6813 {
6814 for (;;)
6815 {
6816 [seen addObject:result];
6817 object = [customSounds objectForKey:result];
6818 if( [object isKindOfClass:[NSArray class]] && [object count] > 0)
6819 {
6820 result = [object oo_stringAtIndex:Ranrot() % [object count]];
6821 if ([key hasPrefix:@"["] && [key hasSuffix:@"]"]) key=result;
6822 }
6823 else
6824 {
6825 if ([object isKindOfClass:[NSString class]])
6826 result = object;
6827 else
6828 result = nil;
6829 }
6830 if (result == nil || ![result hasPrefix:@"["] || ![result hasSuffix:@"]"]) break;
6831 if ([seen containsObject:result])
6832 {
6833 OOLogERR(@"sound.customSounds.recursion", @"recursion in customsounds.plist for '%@' (at '%@'), no sound will be played.", key, result);
6834 result = nil;
6835 break;
6836 }
6837 }
6838 }
6839
6840 if (result == nil) result = @"__oolite-no-sound";
6841 [[OOCacheManager sharedCache] setObject:result forKey:key inCache:@"resolved custom sounds"];
6842 }
6843
6844 if ([result isEqualToString:@"__oolite-no-sound"])
6845 {
6846 OOLog(@"sound.customSounds", @"Could not resolve sound name in customsounds.plist for '%@', no sound will be played.", key);
6847 result = nil;
6848 }
6849 return result;
6850}
6851
6852
6853- (NSDictionary *) screenTextureDescriptorForKey:(NSString *)key
6854{
6855 id value = [screenBackgrounds objectForKey:key];
6856 while ([value isKindOfClass:[NSArray class]]) value = [value objectAtIndex:Ranrot() % [value count]];
6857
6858 if ([value isKindOfClass:[NSString class]]) value = [NSDictionary dictionaryWithObject:value forKey:@"name"];
6859 else if (![value isKindOfClass:[NSDictionary class]]) value = nil;
6860
6861 // Start loading the texture, and return nil if it doesn't exist.
6862 if (![[self gui] preloadGUITexture:value]) value = nil;
6863
6864 return value;
6865}
6866
6867
6868- (void) setScreenTextureDescriptorForKey:(NSString *)key descriptor:(NSDictionary *)desc
6869{
6870 NSMutableDictionary *sbCopy = [screenBackgrounds mutableCopy];
6871 if (desc == nil)
6872 {
6873 [sbCopy removeObjectForKey:key];
6874 }
6875 else
6876 {
6877 [sbCopy setObject:desc forKey:key];
6878 }
6879 [screenBackgrounds release];
6880 screenBackgrounds = [sbCopy copy];
6881 [sbCopy release];
6882}
6883
6884
6886{
6887 if (currentMessage) [currentMessage release];
6889}
6890
6891
6892- (void) setMessageGuiBackgroundColor:(OOColor *)some_color
6893{
6894 [message_gui setBackgroundColor:some_color];
6895}
6896
6897
6898- (void) displayMessage:(NSString *) text forCount:(OOTimeDelta)count
6899{
6900 if (![currentMessage isEqual:text] || universal_time >= messageRepeatTime)
6901 {
6902 if (currentMessage) [currentMessage release];
6903 currentMessage = [text retain];
6905 [self showGUIMessage:text withScroll:YES andColor:[message_gui textColor] overDuration:count];
6906 }
6907}
6908
6909
6910- (void) displayCountdownMessage:(NSString *) text forCount:(OOTimeDelta)count
6911{
6913 {
6914 if (currentMessage) [currentMessage release];
6915 currentMessage = [text retain];
6917 [self showGUIMessage:text withScroll:NO andColor:[message_gui textColor] overDuration:count];
6918 }
6919}
6920
6921
6922- (void) addDelayedMessage:(NSString *)text forCount:(OOTimeDelta)count afterDelay:(double)delay
6923{
6924 NSMutableDictionary *msgDict = [NSMutableDictionary dictionaryWithCapacity:2];
6925 [msgDict setObject:text forKey:@"message"];
6926 [msgDict setObject:[NSNumber numberWithDouble:count] forKey:@"duration"];
6927 [self performSelector:@selector(addDelayedMessage:) withObject:msgDict afterDelay:delay];
6928}
6929
6930
6931- (void) addDelayedMessage:(NSDictionary *) textdict
6932{
6933 NSString *msg = nil;
6934 OOTimeDelta msg_duration;
6935
6936 msg = [textdict oo_stringForKey:@"message"];
6937 if (msg == nil) return;
6938 msg_duration = [textdict oo_nonNegativeDoubleForKey:@"duration" defaultValue:3.0];
6939
6940 [self addMessage:msg forCount:msg_duration];
6941}
6942
6943
6944- (void) addMessage:(NSString *)text forCount:(OOTimeDelta)count
6945{
6946 [self addMessage:text forCount:count forceDisplay:NO];
6947}
6948
6949
6950- (void) speakWithSubstitutions:(NSString *)text
6951{
6952#if OOLITE_SPEECH_SYNTH
6953 //speech synthesis
6954
6955 PlayerEntity* player = PLAYER;
6956 if ([player isSpeechOn] > OOSPEECHSETTINGS_OFF)
6957 {
6958 NSString *systemSaid = nil;
6959 NSString *h_systemSaid = nil;
6960
6961 NSString *systemName = [self getSystemName:systemID];
6962
6963 systemSaid = systemName;
6964
6965 NSString *h_systemName = [self getSystemName:[player targetSystemID]];
6966 h_systemSaid = h_systemName;
6967
6968 NSString *spokenText = text;
6969 if (speechArray != nil)
6970 {
6971 NSEnumerator *speechEnumerator = nil;
6972 NSArray *thePair = nil;
6973
6974 for (speechEnumerator = [speechArray objectEnumerator]; (thePair = [speechEnumerator nextObject]); )
6975 {
6976 NSString *original_phrase = [thePair oo_stringAtIndex:0];
6977
6978 NSUInteger replacementIndex;
6979#if OOLITE_MAC_OS_X
6980 replacementIndex = 1;
6981#elif OOLITE_ESPEAK
6982 replacementIndex = [thePair count] > 2 ? 2 : 1;
6983#endif
6984
6985 NSString *replacement_phrase = [thePair oo_stringAtIndex:replacementIndex];
6986 if (![replacement_phrase isEqualToString:@"_"])
6987 {
6988 spokenText = [spokenText stringByReplacingOccurrencesOfString:original_phrase withString:replacement_phrase];
6989 }
6990 }
6991 spokenText = [spokenText stringByReplacingOccurrencesOfString:systemName withString:systemSaid];
6992 spokenText = [spokenText stringByReplacingOccurrencesOfString:h_systemName withString:h_systemSaid];
6993 }
6994 [self stopSpeaking];
6995 [self startSpeakingString:spokenText];
6996 }
6997#endif // OOLITE_SPEECH_SYNTH
6998}
6999
7000
7001- (void) addMessage:(NSString *) text forCount:(OOTimeDelta) count forceDisplay:(BOOL) forceDisplay
7002{
7003 if (![currentMessage isEqual:text] || forceDisplay || universal_time >= messageRepeatTime)
7004 {
7005 if ([PLAYER isSpeechOn] == OOSPEECHSETTINGS_ALL)
7006 {
7007 [self speakWithSubstitutions:text];
7008 }
7009
7010 [self showGUIMessage:text withScroll:YES andColor:[message_gui textColor] overDuration:count];
7011
7012 [PLAYER doScriptEvent:OOJSID("consoleMessageReceived") withArgument:text];
7013
7014 [currentMessage release];
7015 currentMessage = [text retain];
7017 }
7018}
7019
7020
7021- (void) addCommsMessage:(NSString *)text forCount:(OOTimeDelta)count
7022{
7023 [self addCommsMessage:text forCount:count andShowComms:_autoCommLog logOnly:NO];
7024}
7025
7026
7027- (void) addCommsMessage:(NSString *)text forCount:(OOTimeDelta)count andShowComms:(BOOL)showComms logOnly:(BOOL)logOnly
7028{
7029 if ([PLAYER showDemoShips]) return;
7030
7031 NSString *expandedMessage = OOExpand(text);
7032
7033 if (![currentMessage isEqualToString:expandedMessage] || universal_time >= messageRepeatTime)
7034 {
7035 PlayerEntity* player = PLAYER;
7036
7037 if (!logOnly)
7038 {
7039 if ([player isSpeechOn] >= OOSPEECHSETTINGS_COMMS)
7040 {
7041 // EMMSTRAN: should say "Incoming message from ..." when prefixed with sender name.
7042 NSString *format = OOExpandKey(@"speech-synthesis-incoming-message-@");
7043 [self speakWithSubstitutions:[NSString stringWithFormat:format, expandedMessage]];
7044 }
7045
7046 [self showGUIMessage:expandedMessage withScroll:YES andColor:[message_gui textCommsColor] overDuration:count];
7047
7048 [currentMessage release];
7051 }
7052
7053 [comm_log_gui printLongText:expandedMessage align:GUI_ALIGN_LEFT color:nil fadeTime:0.0 key:nil addToArray:[player commLog]];
7054
7055 if (showComms) [self showCommsLog:6.0];
7056 }
7057}
7058
7059
7060- (void) showCommsLog:(OOTimeDelta)how_long
7061{
7062 [comm_log_gui setAlpha:1.0];
7063 if (![self permanentCommLog]) [comm_log_gui fadeOutFromTime:[self getTime] overDuration:how_long];
7064}
7065
7066
7067- (void) showGUIMessage:(NSString *)text withScroll:(BOOL)scroll andColor:(OOColor *)selectedColor overDuration:(OOTimeDelta)how_long
7068{
7069 if (scroll)
7070 {
7071 [message_gui printLongText:text align:GUI_ALIGN_CENTER color:selectedColor fadeTime:how_long key:nil addToArray:nil];
7072 }
7073 else
7074 {
7075 [message_gui printLineNoScroll:text align:GUI_ALIGN_CENTER color:selectedColor fadeTime:how_long key:nil addToArray:nil];
7076 }
7077 [message_gui setAlpha:1.0f];
7078}
7079
7080
7082{
7083 if (EXPECT_NOT([PLAYER status] == STATUS_START_GAME))
7084 {
7085 return; // no need to be adding ships as this is not a "real" game
7086 }
7087 JSContext *context = OOJSAcquireContext();
7088 [PLAYER doWorldScriptEvent:OOJSIDFromString(system_repopulator) inContext:context withArguments:NULL count:0 timeLimit:kOOJSLongTimeLimit];
7089 OOJSRelinquishContext(context);
7091}
7092
7093
7094- (void) update:(OOTimeDelta)inDeltaT
7095{
7096 volatile OOTimeDelta delta_t = inDeltaT * [self timeAccelerationFactor];
7097 NSUInteger sessionID = _sessionID;
7098 OOLog(@"universe.profile.update", @"%@", @"Begin update");
7099 if (EXPECT(!no_update))
7100 {
7101 next_repopulation -= delta_t;
7102 if (next_repopulation < 0)
7103 {
7104 [self repopulateSystem];
7105 }
7106
7107 unsigned i, ent_count = n_entities;
7108 Entity *my_entities[ent_count];
7109
7111
7112 // use a retained copy so this can't be changed under us.
7113 for (i = 0; i < ent_count; i++)
7114 {
7115 my_entities[i] = [sortedEntities[i] retain]; // explicitly retain each one
7116 }
7117
7118 NSString * volatile update_stage = @"initialisation";
7119#ifndef NDEBUG
7120 id volatile update_stage_param = nil;
7121#endif
7122
7123 @try
7124 {
7125 PlayerEntity *player = PLAYER;
7126
7127 skyClearColor[0] = 0.0;
7128 skyClearColor[1] = 0.0;
7129 skyClearColor[2] = 0.0;
7130 skyClearColor[3] = 0.0;
7131
7132 time_delta = delta_t;
7133 universal_time += delta_t;
7134
7135 if (EXPECT_NOT([player showDemoShips] && [player guiScreen] == GUI_SCREEN_SHIPLIBRARY))
7136 {
7137 update_stage = @"demo management";
7138
7140 {
7141 if (ent_count > 1)
7142 {
7143 Vector vel;
7144 Quaternion q2 = kIdentityQuaternion;
7145
7147
7148 switch (demo_stage)
7149 {
7150 case DEMO_FLY_IN:
7151 [demo_ship setPosition:[demo_ship destination]]; // ideal position
7154 break;
7155 case DEMO_SHOW_THING:
7156 vel = make_vector(0, 0, DEMO2_VANISHING_DISTANCE * demo_ship->collision_radius * 6.0);
7157 [demo_ship setVelocity:vel];
7160 break;
7161 case DEMO_FLY_OUT:
7162 // change the demo_ship here
7163 [self removeEntity:demo_ship];
7164 demo_ship = nil;
7165
7166/* NSString *shipDesc = nil;
7167 NSString *shipName = nil;
7168 NSDictionary *shipDict = nil; */
7169
7171 demo_ship = [self newShipWithName:[[self demoShipData] oo_stringForKey:kOODemoShipKey] usePlayerProxy:NO];
7172
7173 if (demo_ship != nil)
7174 {
7175 [demo_ship removeEquipmentItem:@"EQ_SHIELD_BOOSTER"];
7176 [demo_ship removeEquipmentItem:@"EQ_SHIELD_ENHANCER"];
7177
7178 [demo_ship switchAITo:@"nullAI.plist"];
7180 [demo_ship setScanClass: CLASS_NO_DRAW];
7181 [demo_ship setStatus: STATUS_COCKPIT_DISPLAY]; // prevents it getting escorts on addition
7182 [demo_ship setDemoShip: 1.0f];
7183 [demo_ship setDemoStartTime: universal_time];
7184 if ([self addEntity:demo_ship])
7185 {
7186 [demo_ship release]; // We now own a reference through the entity list.
7187 [demo_ship setStatus:STATUS_COCKPIT_DISPLAY];
7189 [demo_ship setPositionX:0.0f y:0.0f z:demo_start_z];
7190 [demo_ship setDestination: make_HPvector(0.0f, 0.0f, demo_start_z * 0.01f)]; // ideal position
7191 [demo_ship setVelocity:kZeroVector];
7192 [demo_ship setScanClass: CLASS_NO_DRAW];
7193// [gui setText:shipName != nil ? shipName : [demo_ship displayName] forRow:19 align:GUI_ALIGN_CENTER];
7194
7196
7200 }
7201 else
7202 {
7203 demo_ship = nil;
7204 }
7205 }
7206 break;
7207 }
7208 }
7209 }
7210 else if (demo_stage == DEMO_FLY_IN)
7211 {
7213 [demo_ship setPositionX:0.0f y:[demo_ship destination].y * delta z:demo_start_z + ([demo_ship destination].z - demo_start_z) * delta ];
7214 }
7215 }
7216
7217 update_stage = @"update:entity";
7218 NSMutableSet *zombies = nil;
7219 OOLog(@"universe.profile.update", @"%@", update_stage);
7220 for (i = 0; i < ent_count; i++)
7221 {
7222 Entity *thing = my_entities[i];
7223#ifndef NDEBUG
7224 update_stage_param = thing;
7225 update_stage = @"update:entity [%@]";
7226#endif
7227 // Game Over code depends on regular delta_t updates to the dead player entity. Ignore the player entity, even when dead.
7228 if (EXPECT_NOT([thing status] == STATUS_DEAD && ![entitiesDeadThisUpdate containsObject:thing] && ![thing isPlayer]))
7229 {
7230 if (zombies == nil) zombies = [NSMutableSet set];
7231 [zombies addObject:thing];
7232 continue;
7233 }
7234
7235 [thing update:delta_t];
7237 {
7238 // Game was reset (in player update); end this update: cycle.
7239 break;
7240 }
7241
7242#ifndef NDEBUG
7243 update_stage = @"update:list maintenance [%@]";
7244#endif
7245
7246 // maintain distance-from-player list
7247 GLfloat z_distance = thing->zero_distance;
7248
7249 int index = thing->zero_index;
7250 while (index > 0 && z_distance < sortedEntities[index - 1]->zero_distance)
7251 {
7252 sortedEntities[index] = sortedEntities[index - 1]; // bubble up the list, usually by just one position
7253 sortedEntities[index - 1] = thing;
7254 thing->zero_index = index - 1;
7255 sortedEntities[index]->zero_index = index;
7256 index--;
7257 }
7258
7259 // update deterministic AI
7260 if ([thing isShip])
7261 {
7262#ifndef NDEBUG
7263 update_stage = @"update:think [%@]";
7264#endif
7265 AI* theShipsAI = [(ShipEntity *)thing getAI];
7266 if (theShipsAI)
7267 {
7268 double thinkTime = [theShipsAI nextThinkTime];
7269 if ((universal_time > thinkTime)||(thinkTime == 0.0))
7270 {
7272 [theShipsAI think];
7273 }
7274 }
7275 }
7276 }
7277#ifndef NDEBUG
7278 update_stage_param = nil;
7279#endif
7280
7281 if (zombies != nil)
7282 {
7283 update_stage = @"shootin' zombies";
7284 NSEnumerator *zombieEnum = nil;
7285 Entity *zombie = nil;
7286 for (zombieEnum = [zombies objectEnumerator]; (zombie = [zombieEnum nextObject]); )
7287 {
7288 OOLogERR(@"universe.zombie", @"Found dead entity %@ in active entity list, removing. This is an internal error, please report it.", zombie);
7289 [self removeEntity:zombie];
7290 }
7291 }
7292
7293 // Maintain x/y/z order lists
7294 update_stage = @"updating linked lists";
7295 OOLog(@"universe.profile.update", @"%@", update_stage);
7296 for (i = 0; i < ent_count; i++)
7297 {
7299 }
7300
7301 // detect collisions and light ships that can see the sun
7302
7303 update_stage = @"collision and shadow detection";
7304 OOLog(@"universe.profile.update", @"%@", update_stage);
7305 [self filterSortedLists];
7307
7308 // do any required check and maintenance of linked lists
7309
7311 {
7312 MaintainLinkedLists(self);
7314 }
7315 }
7316 @catch (NSException *exception)
7317 {
7318 if ([[exception name] hasPrefix:@"Oolite"])
7319 {
7320 [self handleOoliteException:exception];
7321 }
7322 else
7323 {
7324#ifndef NDEBUG
7325 if (update_stage_param != nil) update_stage = [NSString stringWithFormat:update_stage, update_stage_param];
7326#endif
7327 OOLog(kOOLogException, @"***** Exception during [%@] in [Universe update:] : %@ : %@ *****", update_stage, [exception name], [exception reason]);
7328 @throw exception;
7329 }
7330 }
7331
7332 // dispose of the non-mutable copy and everything it references neatly
7333 update_stage = @"clean up";
7334 OOLog(@"universe.profile.update", @"%@", update_stage);
7335 for (i = 0; i < ent_count; i++)
7336 {
7337 [my_entities[i] release]; // explicitly release each one
7338 }
7339 /* Garbage collection is going to result in a significant
7340 * pause when it happens. Doing it here is better than doing
7341 * it in the middle of the update when it might slow a
7342 * function into the timelimiter through no fault of its
7343 * own. JS_MaybeGC will only run a GC when it's
7344 * necessary. Merely checking is not significant in terms of
7345 * time. - CIM: 4/8/2013
7346 */
7347 update_stage = @"JS Garbage Collection";
7348 OOLog(@"universe.profile.update", @"%@", update_stage);
7349#ifndef NDEBUG
7350 JSContext *context = OOJSAcquireContext();
7351 uint32 gcbytes1 = JS_GetGCParameter(JS_GetRuntime(context),JSGC_BYTES);
7352 OOJSRelinquishContext(context);
7353#endif
7355#ifndef NDEBUG
7356 context = OOJSAcquireContext();
7357 uint32 gcbytes2 = JS_GetGCParameter(JS_GetRuntime(context),JSGC_BYTES);
7358 OOJSRelinquishContext(context);
7359 if (gcbytes2 < gcbytes1)
7360 {
7361 OOLog(@"universe.profile.jsgc",@"Unplanned JS Garbage Collection from %d to %d",gcbytes1,gcbytes2);
7362 }
7363#endif
7364
7365
7366 }
7367 else
7368 {
7369 // always perform player's dead updates: allows deferred JS resets.
7370 if ([PLAYER status] == STATUS_DEAD) [PLAYER update:delta_t];
7371 }
7372
7373 [entitiesDeadThisUpdate autorelease];
7375 entitiesDeadThisUpdate = [[NSMutableSet alloc] initWithCapacity:n_entities];
7376
7377#if NEW_PLANETS
7378 [self prunePreloadingPlanetMaterials];
7379#endif
7380
7381 OOLog(@"universe.profile.update", @"%@", @"Update complete");
7382}
7383
7384
7385#ifndef NDEBUG
7387{
7389}
7390
7391
7392- (void) setTimeAccelerationFactor:(double)newTimeAccelerationFactor
7393{
7394 if (newTimeAccelerationFactor < TIME_ACCELERATION_FACTOR_MIN || newTimeAccelerationFactor > TIME_ACCELERATION_FACTOR_MAX)
7395 {
7396 newTimeAccelerationFactor = TIME_ACCELERATION_FACTOR_DEFAULT;
7397 }
7398 timeAccelerationFactor = newTimeAccelerationFactor;
7399}
7400#else
7401- (double) timeAccelerationFactor
7402{
7403 return 1.0;
7404}
7405
7406
7407- (void) setTimeAccelerationFactor:(double)newTimeAccelerationFactor
7408{
7409}
7410#endif
7411
7412
7414{
7415 return ECMVisualFXEnabled;
7416}
7417
7418
7419- (void) setECMVisualFXEnabled:(BOOL)isEnabled
7420{
7421 ECMVisualFXEnabled = isEnabled;
7422}
7423
7424
7426{
7427 /*
7428 Eric, 17-10-2010: raised the area to be not filtered out, from the combined collision size to 2x this size.
7429 This allows this filtered list to be used also for proximity_alert and not only for collisions. Before the
7430 proximity_alert could only trigger when already very near a collision. To late for ships to react.
7431 This does raise the number of entities in the collision chain with as result that the number of pairs to compair
7432 becomes significant larger. However, almost all of these extra pairs are dealt with by a simple distance check.
7433 I currently see no noticeable negative effect while playing, but this change might still give some trouble I missed.
7434 */
7435 Entity *e0, *next, *prev;
7436 OOHPScalar start, finish, next_start, next_finish, prev_start, prev_finish;
7437
7438 // using the z_list - set or clear collisionTestFilter and clear collision_chain
7439 e0 = z_list_start;
7440 while (e0)
7441 {
7442 e0->collisionTestFilter = [e0 canCollide]?0:3;
7443 e0->collision_chain = nil;
7444 e0 = e0->z_next;
7445 }
7446 // done.
7447
7448 /* We need to check the lists in both ascending and descending order
7449 * to catch some cases with interposition of entities. We set cTF =
7450 * 1 on the way up, and |= 2 on the way down. Therefore it's only 3
7451 * at the end of the list if it was caught both ways on the same
7452 * list. - CIM: 7/11/2012 */
7453
7454 // start with the z_list
7455 e0 = z_list_start;
7456 while (e0)
7457 {
7458 // here we are either at the start of the list or just past a gap
7459 start = e0->position.z - 2.0f * e0->collision_radius;
7460 finish = start + 4.0f * e0->collision_radius;
7461 next = e0->z_next;
7462 while ((next)&&(next->collisionTestFilter == 3)) // next has been eliminated from the list of possible colliders - so skip it
7463 next = next->z_next;
7464 if (next)
7465 {
7466 next_start = next->position.z - 2.0f * next->collision_radius;
7467 if (next_start < finish)
7468 {
7469 // e0 and next overlap
7470 while ((next)&&(next_start < finish))
7471 {
7472 // skip forward to the next gap or the end of the list
7473 next_finish = next_start + 4.0f * next->collision_radius;
7474 if (next_finish > finish)
7475 finish = next_finish;
7476 e0 = next;
7477 next = e0->z_next;
7478 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated - so skip it
7479 next = next->z_next;
7480 if (next)
7481 next_start = next->position.z - 2.0f * next->collision_radius;
7482 }
7483 // now either (next == nil) or (next_start >= finish)-which would imply a gap!
7484 }
7485 else
7486 {
7487 // e0 is a singleton
7488 e0->collisionTestFilter = 1;
7489 }
7490 }
7491 else // (next == nil)
7492 {
7493 // at the end of the list so e0 is a singleton
7494 e0->collisionTestFilter = 1;
7495 }
7496 e0 = next;
7497 }
7498 // list filtered upwards, now filter downwards
7499 // e0 currently = end of z list
7500 while (e0)
7501 {
7502 // here we are either at the start of the list or just past a gap
7503 start = e0->position.z + 2.0f * e0->collision_radius;
7504 finish = start - 4.0f * e0->collision_radius;
7505 prev = e0->z_previous;
7506 while ((prev)&&(prev->collisionTestFilter == 3)) // next has been eliminated from the list of possible colliders - so skip it
7507 prev = prev->z_previous;
7508 if (prev)
7509 {
7510 prev_start = prev->position.z + 2.0f * prev->collision_radius;
7511 if (prev_start > finish)
7512 {
7513 // e0 and next overlap
7514 while ((prev)&&(prev_start > finish))
7515 {
7516 // skip forward to the next gap or the end of the list
7517 prev_finish = prev_start - 4.0f * prev->collision_radius;
7518 if (prev_finish < finish)
7519 finish = prev_finish;
7520 e0 = prev;
7521 prev = e0->z_previous;
7522 while ((prev)&&(prev->collisionTestFilter==3)) // next has been eliminated - so skip it
7523 prev = prev->z_previous;
7524 if (prev)
7525 prev_start = prev->position.z + 2.0f * prev->collision_radius;
7526 }
7527 // now either (prev == nil) or (prev_start <= finish)-which would imply a gap!
7528 }
7529 else
7530 {
7531 // e0 is a singleton
7532 e0->collisionTestFilter |= 2;
7533 }
7534 }
7535 else // (prev == nil)
7536 {
7537 // at the end of the list so e0 is a singleton
7538 e0->collisionTestFilter |= 2;
7539 }
7540 e0 = prev;
7541 }
7542 // done! list filtered
7543
7544 // then with the y_list, z_list singletons now create more gaps..
7545 e0 = y_list_start;
7546 while (e0)
7547 {
7548 // here we are either at the start of the list or just past a gap
7549 start = e0->position.y - 2.0f * e0->collision_radius;
7550 finish = start + 4.0f * e0->collision_radius;
7551 next = e0->y_next;
7552 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated from the list of possible colliders - so skip it
7553 next = next->y_next;
7554 if (next)
7555 {
7556
7557 next_start = next->position.y - 2.0f * next->collision_radius;
7558 if (next_start < finish)
7559 {
7560 // e0 and next overlap
7561 while ((next)&&(next_start < finish))
7562 {
7563 // skip forward to the next gap or the end of the list
7564 next_finish = next_start + 4.0f * next->collision_radius;
7565 if (next_finish > finish)
7566 finish = next_finish;
7567 e0 = next;
7568 next = e0->y_next;
7569 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated - so skip it
7570 next = next->y_next;
7571 if (next)
7572 next_start = next->position.y - 2.0f * next->collision_radius;
7573 }
7574 // now either (next == nil) or (next_start >= finish)-which would imply a gap!
7575 }
7576 else
7577 {
7578 // e0 is a singleton
7579 e0->collisionTestFilter = 1;
7580 }
7581 }
7582 else // (next == nil)
7583 {
7584 // at the end of the list so e0 is a singleton
7585 e0->collisionTestFilter = 1;
7586 }
7587 e0 = next;
7588 }
7589 // list filtered upwards, now filter downwards
7590 // e0 currently = end of y list
7591 while (e0)
7592 {
7593 // here we are either at the start of the list or just past a gap
7594 start = e0->position.y + 2.0f * e0->collision_radius;
7595 finish = start - 4.0f * e0->collision_radius;
7596 prev = e0->y_previous;
7597 while ((prev)&&(prev->collisionTestFilter == 3)) // next has been eliminated from the list of possible colliders - so skip it
7598 prev = prev->y_previous;
7599 if (prev)
7600 {
7601 prev_start = prev->position.y + 2.0f * prev->collision_radius;
7602 if (prev_start > finish)
7603 {
7604 // e0 and next overlap
7605 while ((prev)&&(prev_start > finish))
7606 {
7607 // skip forward to the next gap or the end of the list
7608 prev_finish = prev_start - 4.0f * prev->collision_radius;
7609 if (prev_finish < finish)
7610 finish = prev_finish;
7611 e0 = prev;
7612 prev = e0->y_previous;
7613 while ((prev)&&(prev->collisionTestFilter==3)) // next has been eliminated - so skip it
7614 prev = prev->y_previous;
7615 if (prev)
7616 prev_start = prev->position.y + 2.0f * prev->collision_radius;
7617 }
7618 // now either (prev == nil) or (prev_start <= finish)-which would imply a gap!
7619 }
7620 else
7621 {
7622 // e0 is a singleton
7623 e0->collisionTestFilter |= 2;
7624 }
7625 }
7626 else // (prev == nil)
7627 {
7628 // at the end of the list so e0 is a singleton
7629 e0->collisionTestFilter |= 2;
7630 }
7631 e0 = prev;
7632 }
7633 // done! list filtered
7634
7635 // finish with the x_list
7636 e0 = x_list_start;
7637 while (e0)
7638 {
7639 // here we are either at the start of the list or just past a gap
7640 start = e0->position.x - 2.0f * e0->collision_radius;
7641 finish = start + 4.0f * e0->collision_radius;
7642 next = e0->x_next;
7643 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated from the list of possible colliders - so skip it
7644 next = next->x_next;
7645 if (next)
7646 {
7647 next_start = next->position.x - 2.0f * next->collision_radius;
7648 if (next_start < finish)
7649 {
7650 // e0 and next overlap
7651 while ((next)&&(next_start < finish))
7652 {
7653 // skip forward to the next gap or the end of the list
7654 next_finish = next_start + 4.0f * next->collision_radius;
7655 if (next_finish > finish)
7656 finish = next_finish;
7657 e0 = next;
7658 next = e0->x_next;
7659 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated - so skip it
7660 next = next->x_next;
7661 if (next)
7662 next_start = next->position.x - 2.0f * next->collision_radius;
7663 }
7664 // now either (next == nil) or (next_start >= finish)-which would imply a gap!
7665 }
7666 else
7667 {
7668 // e0 is a singleton
7669 e0->collisionTestFilter = 1;
7670 }
7671 }
7672 else // (next == nil)
7673 {
7674 // at the end of the list so e0 is a singleton
7675 e0->collisionTestFilter = 1;
7676 }
7677 e0 = next;
7678 }
7679 // list filtered upwards, now filter downwards
7680 // e0 currently = end of x list
7681 while (e0)
7682 {
7683 // here we are either at the start of the list or just past a gap
7684 start = e0->position.x + 2.0f * e0->collision_radius;
7685 finish = start - 4.0f * e0->collision_radius;
7686 prev = e0->x_previous;
7687 while ((prev)&&(prev->collisionTestFilter == 3)) // next has been eliminated from the list of possible colliders - so skip it
7688 prev = prev->x_previous;
7689 if (prev)
7690 {
7691 prev_start = prev->position.x + 2.0f * prev->collision_radius;
7692 if (prev_start > finish)
7693 {
7694 // e0 and next overlap
7695 while ((prev)&&(prev_start > finish))
7696 {
7697 // skip forward to the next gap or the end of the list
7698 prev_finish = prev_start - 4.0f * prev->collision_radius;
7699 if (prev_finish < finish)
7700 finish = prev_finish;
7701 e0 = prev;
7702 prev = e0->x_previous;
7703 while ((prev)&&(prev->collisionTestFilter==3)) // next has been eliminated - so skip it
7704 prev = prev->x_previous;
7705 if (prev)
7706 prev_start = prev->position.x + 2.0f * prev->collision_radius;
7707 }
7708 // now either (prev == nil) or (prev_start <= finish)-which would imply a gap!
7709 }
7710 else
7711 {
7712 // e0 is a singleton
7713 e0->collisionTestFilter |= 2;
7714 }
7715 }
7716 else // (prev == nil)
7717 {
7718 // at the end of the list so e0 is a singleton
7719 e0->collisionTestFilter |= 2;
7720 }
7721 e0 = prev;
7722 }
7723 // done! list filtered
7724
7725 // repeat the y_list - so gaps from the x_list influence singletons
7726 e0 = y_list_start;
7727 while (e0)
7728 {
7729 // here we are either at the start of the list or just past a gap
7730 start = e0->position.y - 2.0f * e0->collision_radius;
7731 finish = start + 4.0f * e0->collision_radius;
7732 next = e0->y_next;
7733 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated from the list of possible colliders - so skip it
7734 next = next->y_next;
7735 if (next)
7736 {
7737 next_start = next->position.y - 2.0f * next->collision_radius;
7738 if (next_start < finish)
7739 {
7740 // e0 and next overlap
7741 while ((next)&&(next_start < finish))
7742 {
7743 // skip forward to the next gap or the end of the list
7744 next_finish = next_start + 4.0f * next->collision_radius;
7745 if (next_finish > finish)
7746 finish = next_finish;
7747 e0 = next;
7748 next = e0->y_next;
7749 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated - so skip it
7750 next = next->y_next;
7751 if (next)
7752 next_start = next->position.y - 2.0f * next->collision_radius;
7753 }
7754 // now either (next == nil) or (next_start >= finish)-which would imply a gap!
7755 }
7756 else
7757 {
7758 // e0 is a singleton
7759 e0->collisionTestFilter = 1;
7760 }
7761 }
7762 else // (next == nil)
7763 {
7764 // at the end of the list so e0 is a singleton
7765 e0->collisionTestFilter = 1;
7766 }
7767 e0 = next;
7768 }
7769 // e0 currently = end of y list
7770 while (e0)
7771 {
7772 // here we are either at the start of the list or just past a gap
7773 start = e0->position.y + 2.0f * e0->collision_radius;
7774 finish = start - 4.0f * e0->collision_radius;
7775 prev = e0->y_previous;
7776 while ((prev)&&(prev->collisionTestFilter == 3)) // next has been eliminated from the list of possible colliders - so skip it
7777 prev = prev->y_previous;
7778 if (prev)
7779 {
7780 prev_start = prev->position.y + 2.0f * prev->collision_radius;
7781 if (prev_start > finish)
7782 {
7783 // e0 and next overlap
7784 while ((prev)&&(prev_start > finish))
7785 {
7786 // skip forward to the next gap or the end of the list
7787 prev_finish = prev_start - 4.0f * prev->collision_radius;
7788 if (prev_finish < finish)
7789 finish = prev_finish;
7790 e0 = prev;
7791 prev = e0->y_previous;
7792 while ((prev)&&(prev->collisionTestFilter==3)) // next has been eliminated - so skip it
7793 prev = prev->y_previous;
7794 if (prev)
7795 prev_start = prev->position.y + 2.0f * prev->collision_radius;
7796 }
7797 // now either (prev == nil) or (prev_start <= finish)-which would imply a gap!
7798 }
7799 else
7800 {
7801 // e0 is a singleton
7802 e0->collisionTestFilter |= 2;
7803 }
7804 }
7805 else // (prev == nil)
7806 {
7807 // at the end of the list so e0 is a singleton
7808 e0->collisionTestFilter |= 2;
7809 }
7810 e0 = prev;
7811 }
7812 // done! list filtered
7813
7814 // finally, repeat the z_list - this time building collision chains...
7815 e0 = z_list_start;
7816 while (e0)
7817 {
7818 // here we are either at the start of the list or just past a gap
7819 start = e0->position.z - 2.0f * e0->collision_radius;
7820 finish = start + 4.0f * e0->collision_radius;
7821 next = e0->z_next;
7822 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated from the list of possible colliders - so skip it
7823 next = next->z_next;
7824 if (next)
7825 {
7826 next_start = next->position.z - 2.0f * next->collision_radius;
7827 if (next_start < finish)
7828 {
7829 // e0 and next overlap
7830 while ((next)&&(next_start < finish))
7831 {
7832 // chain e0 to next in collision
7833 e0->collision_chain = next;
7834 // skip forward to the next gap or the end of the list
7835 next_finish = next_start + 4.0f * next->collision_radius;
7836 if (next_finish > finish)
7837 finish = next_finish;
7838 e0 = next;
7839 next = e0->z_next;
7840 while ((next)&&(next->collisionTestFilter==3)) // next has been eliminated - so skip it
7841 next = next->z_next;
7842 if (next)
7843 next_start = next->position.z - 2.0f * next->collision_radius;
7844 }
7845 // now either (next == nil) or (next_start >= finish)-which would imply a gap!
7846 e0->collision_chain = nil; // end the collision chain
7847 }
7848 else
7849 {
7850 // e0 is a singleton
7851 e0->collisionTestFilter = 1;
7852 }
7853 }
7854 else // (next == nil)
7855 {
7856 // at the end of the list so e0 is a singleton
7857 e0->collisionTestFilter = 1;
7858 }
7859 e0 = next;
7860 }
7861 // e0 currently = end of z list
7862 while (e0)
7863 {
7864 // here we are either at the start of the list or just past a gap
7865 start = e0->position.z + 2.0f * e0->collision_radius;
7866 finish = start - 4.0f * e0->collision_radius;
7867 prev = e0->z_previous;
7868 while ((prev)&&(prev->collisionTestFilter == 3)) // next has been eliminated from the list of possible colliders - so skip it
7869 prev = prev->z_previous;
7870 if (prev)
7871 {
7872 prev_start = prev->position.z + 2.0f * prev->collision_radius;
7873 if (prev_start > finish)
7874 {
7875 // e0 and next overlap
7876 while ((prev)&&(prev_start > finish))
7877 {
7878 // e0 probably already in collision chain at this point, but if it
7879 // isn't we have to insert it
7880 if (prev->collision_chain != e0)
7881 {
7882 if (prev->collision_chain == nil)
7883 {
7884 // easy, just add it onto the start of the chain
7885 prev->collision_chain = e0;
7886 }
7887 else
7888 {
7889 /* not nil and not e0 shouldn't be possible, I think.
7890 * if it is, that implies that e0->collision_chain is nil, though
7891 * so: */
7892 if (e0->collision_chain == nil)
7893 {
7894 e0->collision_chain = prev->collision_chain;
7895 prev->collision_chain = e0;
7896 }
7897 else
7898 {
7899 /* This shouldn't happen... If it does, we accept
7900 * missing collision checks and move on */
7901 OOLog(@"general.error.inconsistentState",@"Unexpected state in collision chain builder prev=%@, prev->c=%@, e0=%@, e0->c=%@",prev,prev->collision_chain,e0,e0->collision_chain);
7902 }
7903 }
7904 }
7905 // skip forward to the next gap or the end of the list
7906 prev_finish = prev_start - 4.0f * prev->collision_radius;
7907 if (prev_finish < finish)
7908 finish = prev_finish;
7909 e0 = prev;
7910 prev = e0->z_previous;
7911 while ((prev)&&(prev->collisionTestFilter==3)) // next has been eliminated - so skip it
7912 prev = prev->z_previous;
7913 if (prev)
7914 prev_start = prev->position.z + 2.0f * prev->collision_radius;
7915 }
7916 // now either (prev == nil) or (prev_start <= finish)-which would imply a gap!
7917
7918 // all the collision chains are already terminated somewhere
7919 // at this point so no need to set e0->collision_chain = nil
7920 }
7921 else
7922 {
7923 // e0 is a singleton
7924 e0->collisionTestFilter |= 2;
7925 }
7926 }
7927 else // (prev == nil)
7928 {
7929 // at the end of the list so e0 is a singleton
7930 e0->collisionTestFilter |= 2;
7931 }
7932 e0 = prev;
7933 }
7934 // done! list filtered
7935}
7936
7937
7938- (void) setGalaxyTo:(OOGalaxyID) g
7939{
7940 [self setGalaxyTo:g andReinit:NO];
7941}
7942
7943
7944- (void) setGalaxyTo:(OOGalaxyID) g andReinit:(BOOL) forced
7945{
7946 int i;
7947 NSAutoreleasePool *pool = nil;
7948
7949 if (galaxyID != g || forced) {
7950 galaxyID = g;
7951
7952 // systems
7953 pool = [[NSAutoreleasePool alloc] init];
7954
7955 for (i = 0; i < 256; i++)
7956 {
7957 if (system_names[i])
7958 {
7959 [system_names[i] release];
7960 }
7961 system_names[i] = [[systemManager getProperty:@"name" forSystem:i inGalaxy:g] retain];
7962
7963 }
7964 [pool release];
7965 }
7966}
7967
7968
7969- (void) setSystemTo:(OOSystemID) s
7970{
7971 NSDictionary *systemData;
7972 PlayerEntity *player = PLAYER;
7973 OOEconomyID economy;
7974 NSString *scriptName;
7975
7977
7978 systemID = s;
7979 targetSystemID = s;
7980
7981 systemData = [self generateSystemData:targetSystemID];
7982 economy = [systemData oo_unsignedCharForKey:KEY_ECONOMY];
7983 scriptName = [systemData oo_stringForKey:@"market_script" defaultValue:nil];
7984
7987}
7988
7989
7991{
7992 return systemID;
7993}
7994
7995
7996- (NSDictionary *) descriptions
7997{
7998 if (_descriptions == nil)
7999 {
8000 // Load internal descriptions.plist for use in early init, OXP verifier etc.
8001 // It will be replaced by merged version later if running the game normally.
8002 _descriptions = [NSDictionary dictionaryWithContentsOfFile:[[[ResourceManager builtInPath]
8003 stringByAppendingPathComponent:@"Config"]
8004 stringByAppendingPathComponent:@"descriptions.plist"]];
8005
8006 [self verifyDescriptions];
8007 }
8008 return _descriptions;
8009}
8010
8011
8012static void VerifyDesc(NSString *key, id desc);
8013
8014
8015static void VerifyDescString(NSString *key, NSString *desc)
8016{
8017 if ([desc rangeOfString:@"%n"].location != NSNotFound)
8018 {
8019 OOLog(@"descriptions.verify.percentN", @"***** FATAL: descriptions.plist entry \"%@\" contains the dangerous control sequence %%n.", key);
8020 exit(EXIT_FAILURE);
8021 }
8022}
8023
8024
8025static void VerifyDescArray(NSString *key, NSArray *desc)
8026{
8027 id subDesc = nil;
8028 foreach (subDesc, desc)
8029 {
8030 VerifyDesc(key, subDesc);
8031 }
8032}
8033
8034
8035static void VerifyDesc(NSString *key, id desc)
8036{
8037 if ([desc isKindOfClass:[NSString class]])
8038 {
8039 VerifyDescString(key, desc);
8040 }
8041 else if ([desc isKindOfClass:[NSArray class]])
8042 {
8043 VerifyDescArray(key, desc);
8044 }
8045 else if ([desc isKindOfClass:[NSNumber class]])
8046 {
8047 // No verification needed.
8048 }
8049 else
8050 {
8051 OOLogERR(@"descriptions.verify.badType", @"***** FATAL: descriptions.plist entry for \"%@\" is neither a string nor an array.", key);
8052 exit(EXIT_FAILURE);
8053 }
8054}
8055
8056
8058{
8059 /*
8060 Ensure that no descriptions.plist entries contain the %n format code,
8061 which can be used to smash the stack and potentially call arbitrary
8062 functions.
8063
8064 %n is deliberately not supported in Foundation/CoreFoundation under
8065 Mac OS X, but unfortunately GNUstep implements it.
8066 -- Ahruman 2011-05-05
8067 */
8068
8069 NSString *key = nil;
8070 if (_descriptions == nil)
8071 {
8072 OOLog(@"descriptions.verify", @"%@", @"***** FATAL: Tried to verify descriptions, but descriptions was nil - unable to load any descriptions.plist file.");
8073 exit(EXIT_FAILURE);
8074 }
8075 foreachkey (key, _descriptions)
8076 {
8077 VerifyDesc(key, [_descriptions objectForKey:key]);
8078 }
8079}
8080
8081
8083{
8084 [_descriptions autorelease];
8085 _descriptions = [[ResourceManager dictionaryFromFilesNamed:@"descriptions.plist" inFolder:@"Config" andMerge:YES] retain];
8086 [self verifyDescriptions];
8087}
8088
8089
8090- (NSDictionary *) explosionSetting:(NSString *)explosion
8091{
8092 return [explosionSettings oo_dictionaryForKey:explosion defaultValue:nil];
8093}
8094
8095
8096- (NSArray *) scenarios
8097{
8098 return _scenarios;
8099}
8100
8101
8103{
8104 [_scenarios autorelease];
8105 _scenarios = [[ResourceManager arrayFromFilesNamed:@"scenarios.plist" inFolder:@"Config" andMerge:YES] retain];
8106}
8107
8108
8109- (NSDictionary *) characters
8110{
8111 return characters;
8112}
8113
8114
8115- (NSDictionary *) missiontext
8116{
8117 return missiontext;
8118}
8119
8120
8121- (NSString *)descriptionForKey:(NSString *)key
8122{
8123 return [self chooseStringForKey:key inDictionary:[self descriptions]];
8124}
8125
8126
8127- (NSString *)descriptionForArrayKey:(NSString *)key index:(unsigned)index
8128{
8129 NSArray *array = [[self descriptions] oo_arrayForKey:key];
8130 if ([array count] <= index) return nil; // Catches nil array
8131 return [array objectAtIndex:index];
8132}
8133
8134
8135- (BOOL) descriptionBooleanForKey:(NSString *)key
8136{
8137 return [[self descriptions] oo_boolForKey:key];
8138}
8139
8140
8145
8146
8147- (NSString *) keyForPlanetOverridesForSystem:(OOSystemID) s inGalaxy:(OOGalaxyID) g
8148{
8149 return [NSString stringWithFormat:@"%d %d", g, s];
8150}
8151
8152
8153- (NSString *) keyForInterstellarOverridesForSystems:(OOSystemID) s1 :(OOSystemID) s2 inGalaxy:(OOGalaxyID) g
8154{
8155 return [NSString stringWithFormat:@"interstellar: %d %d %d", g, s1, s2];
8156}
8157
8158
8159- (NSDictionary *) generateSystemData:(OOSystemID) s
8160{
8161 return [self generateSystemData:s useCache:YES];
8162}
8163
8164
8165// cache isn't handled this way any more
8166- (NSDictionary *) generateSystemData:(OOSystemID) s useCache:(BOOL) useCache
8167{
8169
8170// TODO: At the moment this method is only called for systems in the
8171// same galaxy. At some point probably needs generalising to have a
8172// galaxynumber parameter.
8173 NSString *systemKey = [NSString stringWithFormat:@"%u %u",[PLAYER galaxyNumber],s];
8174
8175 return [systemManager getPropertiesForSystemKey:systemKey];
8176
8178}
8179
8180
8181- (NSDictionary *) currentSystemData
8182{
8184
8185 if (![self inInterstellarSpace])
8186 {
8187 return [self generateSystemData:systemID];
8188 }
8189 else
8190 {
8191 static NSDictionary *interstellarDict = nil;
8192 if (interstellarDict == nil)
8193 {
8194 NSString *interstellarName = DESC(@"interstellar-space");
8195 NSString *notApplicable = DESC(@"not-applicable");
8196 NSNumber *minusOne = [NSNumber numberWithInt:-1];
8197 NSNumber *zero = [NSNumber numberWithInt:0];
8198 interstellarDict = [[NSDictionary alloc] initWithObjectsAndKeys:
8199 interstellarName, KEY_NAME,
8200 minusOne, KEY_GOVERNMENT,
8201 minusOne, KEY_ECONOMY,
8202 minusOne, KEY_TECHLEVEL,
8203 zero, KEY_POPULATION,
8204 zero, KEY_PRODUCTIVITY,
8205 zero, KEY_RADIUS,
8206 notApplicable, KEY_INHABITANTS,
8207 notApplicable, KEY_DESCRIPTION,
8208 nil];
8209 }
8210
8211 return interstellarDict;
8212 }
8213
8215}
8216
8217
8219{
8220 return [self sun] == nil;
8221}
8222
8223
8224
8225
8226// layer 2
8227// used by legacy script engine and sun going nova
8228- (void) setSystemDataKey:(NSString *)key value:(NSObject *)object fromManifest:(NSString *)manifest
8229{
8230 [self setSystemDataForGalaxy:galaxyID planet:systemID key:key value:object fromManifest:manifest forLayer:OO_LAYER_OXP_DYNAMIC];
8231}
8232
8233
8234- (void) setSystemDataForGalaxy:(OOGalaxyID)gnum planet:(OOSystemID)pnum key:(NSString *)key value:(id)object fromManifest:(NSString *)manifest forLayer:(OOSystemLayer)layer
8235{
8236 static BOOL sysdataLocked = NO;
8237 if (sysdataLocked)
8238 {
8239 OOLogERR(@"script.error", @"%@", @"System properties cannot be set during 'systemInformationChanged' events to avoid infinite loops.");
8240 return;
8241 }
8242
8243 BOOL sameGalaxy = (gnum == [PLAYER currentGalaxyID]);
8244 BOOL sameSystem = (sameGalaxy && pnum == [self currentSystemID]);
8245
8246 // trying to set unsettable properties?
8247 if ([key isEqualToString:KEY_RADIUS] && sameGalaxy && sameSystem) // buggy if we allow this key to be set while in the system
8248 {
8249 OOLogERR(@"script.error", @"System property '%@' cannot be set while in the system.",key);
8250 return;
8251 }
8252
8253 if ([key isEqualToString:@"coordinates"]) // setting this in game would be very confusing
8254 {
8255 OOLogERR(@"script.error", @"System property '%@' cannot be set.",key);
8256 return;
8257 }
8258
8259
8260 NSString *overrideKey = [NSString stringWithFormat:@"%u %u", gnum, pnum];
8261 NSDictionary *sysInfo = nil;
8262
8263 // short range map fix
8265
8266 if (object != nil) {
8267 // long range map fixes
8268 if ([key isEqualToString:KEY_NAME])
8269 {
8270 object=(id)[[(NSString *)object lowercaseString] capitalizedString];
8271 if(sameGalaxy)
8272 {
8273 if (system_names[pnum]) [system_names[pnum] release];
8274 system_names[pnum] = [(NSString *)object retain];
8275 }
8276 }
8277 else if ([key isEqualToString:@"sun_radius"])
8278 {
8279 if ([object doubleValue] < 1000.0 || [object doubleValue] > 10000000.0 )
8280 {
8281 object = ([object doubleValue] < 1000.0 ? (id)@"1000.0" : (id)@"10000000.0"); // works!
8282 }
8283 }
8284 else if ([key hasPrefix:@"corona_"])
8285 {
8286 object = (id)[NSString stringWithFormat:@"%f",OOClamp_0_1_f([object floatValue])];
8287 }
8288 }
8289
8290 [systemManager setProperty:key forSystemKey:overrideKey andLayer:layer toValue:object fromManifest:manifest];
8291
8292
8293 // Apply changes that can be effective immediately, issue warning if they can't be changed just now
8294 if (sameSystem)
8295 {
8297
8298 OOSunEntity* the_sun = [self sun];
8299 /* KEY_ECONOMY used to be here, but resetting the main station
8300 * market while the player is in the system is likely to cause
8301 * more trouble than it's worth. Let them leave and come back
8302 * - CIM */
8303 if ([key isEqualToString:KEY_TECHLEVEL])
8304 {
8305 if([self station]){
8306 [[self station] setEquivalentTechLevel:[object intValue]];
8307 [[self station] setLocalShipyard:[self shipsForSaleForSystem:systemID
8308 withTL:[object intValue] atTime:[PLAYER clockTime]]];
8309 }
8310 }
8311 else if ([key isEqualToString:@"sun_color"] || [key isEqualToString:@"star_count_multiplier"] ||
8312 [key isEqualToString:@"nebula_count_multiplier"] || [key hasPrefix:@"sky_"])
8313 {
8314 SkyEntity *the_sky = nil;
8315 int i;
8316
8317 for (i = n_entities - 1; i > 0; i--)
8318 if ((sortedEntities[i]) && ([sortedEntities[i] isKindOfClass:[SkyEntity class]]))
8319 the_sky = (SkyEntity*)sortedEntities[i];
8320
8321 if (the_sky != nil)
8322 {
8324
8325 if ([key isEqualToString:@"sun_color"])
8326 {
8327 OOColor *color = [the_sky skyColor];
8328 if (the_sun != nil)
8329 {
8330 [the_sun setSunColor:color];
8331 [the_sun getDiffuseComponents:sun_diffuse];
8332 [the_sun getSpecularComponents:sun_specular];
8333 }
8334 for (i = n_entities - 1; i > 0; i--)
8335 if ((sortedEntities[i]) && ([sortedEntities[i] isKindOfClass:[DustEntity class]]))
8337 }
8338 }
8339 }
8340 else if (the_sun != nil && ([key hasPrefix:@"sun_"] || [key hasPrefix:@"corona_"]))
8341 {
8343 }
8344 else if ([key isEqualToString:@"texture"])
8345 {
8346 [[self planet] setUpPlanetFromTexture:(NSString *)object];
8347 }
8348 else if ([key isEqualToString:@"texture_hsb_color"])
8349 {
8350 [[self planet] setUpPlanetFromTexture: [[self planet] textureFileName]];
8351 }
8352 else if ([key isEqualToString:@"air_color"])
8353 {
8354 [[self planet] setAirColor:[OOColor brightColorWithDescription:object]];
8355 }
8356 else if ([key isEqualToString:@"illumination_color"])
8357 {
8358 [[self planet] setIlluminationColor:[OOColor colorWithDescription:object]];
8359 }
8360 else if ([key isEqualToString:@"air_color_mix_ratio"])
8361 {
8362 [[self planet] setAirColorMixRatio:[sysInfo oo_floatForKey:key]];
8363 }
8364 }
8365
8366 sysdataLocked = YES;
8367 [PLAYER doScriptEvent:OOJSID("systemInformationChanged") withArguments:[NSArray arrayWithObjects:[NSNumber numberWithInt:gnum],[NSNumber numberWithInt:pnum],key,object,nil]];
8368 sysdataLocked = NO;
8369
8370}
8371
8372
8373- (NSDictionary *) generateSystemDataForGalaxy:(OOGalaxyID)gnum planet:(OOSystemID)pnum
8374{
8375 NSString *systemKey = [self keyForPlanetOverridesForSystem:pnum inGalaxy:gnum];
8376 return [systemManager getPropertiesForSystemKey:systemKey];
8377}
8378
8379
8380- (NSArray *) systemDataKeysForGalaxy:(OOGalaxyID)gnum planet:(OOSystemID)pnum
8381{
8382 return [[self generateSystemDataForGalaxy:gnum planet:pnum] allKeys];
8383}
8384
8385
8386/* Only called from OOJSSystemInfo. */
8387- (id) systemDataForGalaxy:(OOGalaxyID)gnum planet:(OOSystemID)pnum key:(NSString *)key
8388{
8389 return [systemManager getProperty:key forSystem:pnum inGalaxy:gnum];
8390}
8391
8392
8393- (NSString *) getSystemName:(OOSystemID) sys
8394{
8395 return [self getSystemName:sys forGalaxy:galaxyID];
8396}
8397
8398
8399- (NSString *) getSystemName:(OOSystemID) sys forGalaxy:(OOGalaxyID) gnum
8400{
8401 return [systemManager getProperty:@"name" forSystem:sys inGalaxy:gnum];
8402}
8403
8404
8405- (OOGovernmentID) getSystemGovernment:(OOSystemID) sys
8406{
8407 return [[systemManager getProperty:@"government" forSystem:sys inGalaxy:galaxyID] unsignedCharValue];
8408}
8409
8410
8411- (NSString *) getSystemInhabitants:(OOSystemID) sys
8412{
8413 return [self getSystemInhabitants:sys plural:YES];
8414}
8415
8416
8417- (NSString *) getSystemInhabitants:(OOSystemID) sys plural:(BOOL)plural
8418{
8419 NSString *ret = nil;
8420 if (!plural)
8421 {
8422 ret = [systemManager getProperty:KEY_INHABITANT forSystem:sys inGalaxy:galaxyID];
8423 }
8424 if (ret != nil) // the singular form might be absent.
8425 {
8426 return ret;
8427 }
8428 else
8429 {
8430 return [systemManager getProperty:KEY_INHABITANTS forSystem:sys inGalaxy:galaxyID];
8431 }
8432}
8433
8434
8435- (NSPoint) coordinatesForSystem:(OOSystemID)s
8436{
8438}
8439
8440
8441- (OOSystemID) findSystemFromName:(NSString *) sysName
8442{
8443 if (sysName == nil) return -1; // no match found!
8444
8445 NSString *system_name = nil;
8446 NSString *match = [sysName lowercaseString];
8447 int i;
8448 for (i = 0; i < 256; i++)
8449 {
8450 system_name = [system_names[i] lowercaseString];
8451 if ([system_name isEqualToString:match])
8452 {
8453 return i;
8454 }
8455 }
8456 return -1; // no match found!
8457}
8458
8459
8460- (OOSystemID) findSystemAtCoords:(NSPoint) coords withGalaxy:(OOGalaxyID) g
8461{
8462 OOLog(@"deprecated.function", @"%@", @"findSystemAtCoords");
8463 return [self findSystemNumberAtCoords:coords withGalaxy:g includingHidden:YES];
8464}
8465
8466
8467- (NSMutableArray *) nearbyDestinationsWithinRange:(double)range
8468{
8469 NSMutableArray *result = [NSMutableArray arrayWithCapacity:16];
8470
8471 range = OOClamp_0_max_d(range, MAX_JUMP_RANGE); // limit to systems within 7LY
8472 NSPoint here = [PLAYER galaxy_coordinates];
8473
8474 for (unsigned short i = 0; i < 256; i++)
8475 {
8476 NSPoint there = [self coordinatesForSystem:i];
8477 double dist = distanceBetweenPlanetPositions(here.x, here.y, there.x, there.y);
8478 if (dist <= range && (i != systemID || [self inInterstellarSpace])) // if we are in interstellar space, it's OK to include the system we (mis)jumped from
8479 {
8480 [result addObject: [NSDictionary dictionaryWithObjectsAndKeys:
8481 [NSNumber numberWithDouble:dist], @"distance",
8482 [NSNumber numberWithInt:i], @"sysID",
8483 [[self generateSystemData:i] oo_stringForKey:@"sun_gone_nova" defaultValue:@"0"], @"nova",
8484 nil]];
8485 }
8486 }
8487
8488 return result;
8489}
8490
8491
8492- (OOSystemID) findNeighbouringSystemToCoords:(NSPoint) coords withGalaxy:(OOGalaxyID) g
8493{
8494
8495 double distance;
8496 int n,i,j;
8497 double min_dist = 10000.0;
8498
8499 // make list of connected systems
8500 BOOL connected[256];
8501 for (i = 0; i < 256; i++)
8502 connected[i] = NO;
8503 connected[0] = YES; // system zero is always connected (true for galaxies 0..7)
8504 for (n = 0; n < 3; n++) //repeat three times for surety
8505 {
8506 for (i = 0; i < 256; i++) // flood fill out from system zero
8507 {
8508 NSPoint ipos = [systemManager getCoordinatesForSystem:i inGalaxy:g];
8509 for (j = 0; j < 256; j++)
8510 {
8511 NSPoint jpos = [systemManager getCoordinatesForSystem:j inGalaxy:g];
8512 double dist = distanceBetweenPlanetPositions(ipos.x,ipos.y,jpos.x,jpos.y);
8513 if (dist <= MAX_JUMP_RANGE)
8514 {
8515 connected[j] |= connected[i];
8516 connected[i] |= connected[j];
8517 }
8518 }
8519 }
8520 }
8521 OOSystemID system = 0;
8522 for (i = 0; i < 256; i++)
8523 {
8524 NSPoint ipos = [systemManager getCoordinatesForSystem:i inGalaxy:g];
8525 distance = distanceBetweenPlanetPositions((int)coords.x, (int)coords.y, ipos.x, ipos.y);
8526 if ((connected[i])&&(distance < min_dist)&&(distance != 0.0))
8527 {
8528 min_dist = distance;
8529 system = i;
8530 }
8531 }
8532
8533 return system;
8534}
8535
8536
8537/* This differs from the above function in that it can return a system
8538 * exactly at the specified coordinates */
8539- (OOSystemID) findConnectedSystemAtCoords:(NSPoint) coords withGalaxy:(OOGalaxyID) g
8540{
8541
8542 double distance;
8543 int n,i,j;
8544 double min_dist = 10000.0;
8545
8546 // make list of connected systems
8547 BOOL connected[256];
8548 for (i = 0; i < 256; i++)
8549 connected[i] = NO;
8550 connected[0] = YES; // system zero is always connected (true for galaxies 0..7)
8551 for (n = 0; n < 3; n++) //repeat three times for surety
8552 {
8553 for (i = 0; i < 256; i++) // flood fill out from system zero
8554 {
8555 NSPoint ipos = [systemManager getCoordinatesForSystem:i inGalaxy:g];
8556 for (j = 0; j < 256; j++)
8557 {
8558 NSPoint jpos = [systemManager getCoordinatesForSystem:j inGalaxy:g];
8559 double dist = distanceBetweenPlanetPositions(ipos.x,ipos.y,jpos.x,jpos.y);
8560 if (dist <= MAX_JUMP_RANGE)
8561 {
8562 connected[j] |= connected[i];
8563 connected[i] |= connected[j];
8564 }
8565 }
8566 }
8567 }
8568 OOSystemID system = 0;
8569 for (i = 0; i < 256; i++)
8570 {
8571 NSPoint ipos = [systemManager getCoordinatesForSystem:i inGalaxy:g];
8572 distance = distanceBetweenPlanetPositions((int)coords.x, (int)coords.y, ipos.x, ipos.y);
8573 if ((connected[i])&&(distance < min_dist))
8574 {
8575 min_dist = distance;
8576 system = i;
8577 }
8578 }
8579
8580 return system;
8581}
8582
8583
8584- (OOSystemID) findSystemNumberAtCoords:(NSPoint) coords withGalaxy:(OOGalaxyID)g includingHidden:(BOOL)hidden
8585{
8586 /*
8587 NOTE: this previously used NSNotFound as the default value, but
8588 returned an int, which would truncate on 64-bit systems. I assume
8589 no-one was using it in a context where the default value was returned.
8590 -- Ahruman 2012-08-25
8591 */
8593 unsigned distance, dx, dy;
8594 OOSystemID i;
8595 unsigned min_dist = 10000;
8596
8597 for (i = 0; i < 256; i++)
8598 {
8599 if (!hidden) {
8600 NSDictionary *systemInfo = [systemManager getPropertiesForSystem:i inGalaxy:g];
8601 NSInteger concealment = [systemInfo oo_intForKey:@"concealment" defaultValue:OO_SYSTEMCONCEALMENT_NONE];
8602 if (concealment >= OO_SYSTEMCONCEALMENT_NOTHING) {
8603 // system is not known
8604 continue;
8605 }
8606 }
8607 NSPoint ipos = [systemManager getCoordinatesForSystem:i inGalaxy:g];
8608 dx = ABS(coords.x - ipos.x);
8609 dy = ABS(coords.y - ipos.y);
8610
8611 if (dx > dy) distance = (dx + dx + dy) / 2;
8612 else distance = (dx + dy + dy) / 2;
8613
8614 if (distance < min_dist)
8615 {
8616 min_dist = distance;
8617 system = i;
8618 }
8619 // with coincident systems choose only if ABOVE
8620 if ((distance == min_dist)&&(coords.y > ipos.y))
8621 {
8622 system = i;
8623 }
8624 // or if EQUAL but already selected
8625 else if ((distance == min_dist)&&(coords.y == ipos.y)&&(i==[PLAYER targetSystemID]))
8626 {
8627 system = i;
8628 }
8629 }
8630 return system;
8631}
8632
8633
8634- (NSPoint) findSystemCoordinatesWithPrefix:(NSString *) p_fix
8635{
8636 return [self findSystemCoordinatesWithPrefix:p_fix exactMatch:NO];
8637}
8638
8639
8640- (NSPoint) findSystemCoordinatesWithPrefix:(NSString *) p_fix exactMatch:(BOOL) exactMatch
8641{
8642 NSString *system_name = nil;
8643 NSPoint system_coords = NSMakePoint(-1.0,-1.0);
8644 int i;
8645 int result = -1;
8646 for (i = 0; i < 256; i++)
8647 {
8648 system_found[i] = NO;
8649 system_name = [system_names[i] lowercaseString];
8650 if ((exactMatch && [system_name isEqualToString:p_fix]) || (!exactMatch && [system_name hasPrefix:p_fix]))
8651 {
8652 /* Only used in player-based search routines */
8653 NSDictionary *systemInfo = [systemManager getPropertiesForSystem:i inGalaxy:galaxyID];
8654 NSInteger concealment = [systemInfo oo_intForKey:@"concealment" defaultValue:OO_SYSTEMCONCEALMENT_NONE];
8655 if (concealment >= OO_SYSTEMCONCEALMENT_NONAME) {
8656 // system is not known
8657 continue;
8658 }
8659
8660 system_found[i] = YES;
8661 if (result < 0)
8662 {
8663 system_coords = [systemManager getCoordinatesForSystem:i inGalaxy:galaxyID];
8664 result = i;
8665 }
8666 }
8667 }
8668 return system_coords;
8669}
8670
8671
8673{
8674 return (BOOL*)system_found;
8675}
8676
8677
8678- (NSString*)systemNameIndex:(OOSystemID)index
8679{
8680 return system_names[index & 255];
8681}
8682
8683
8684- (NSDictionary *) routeFromSystem:(OOSystemID) start toSystem:(OOSystemID) goal optimizedBy:(OORouteType) optimizeBy
8685{
8686 /*
8687 time_cost = distance * distance
8688 jump_cost = jumps * max_total_distance + distance = max_total_tistance + distance
8689
8690 max_total_distance is 7 * 256
8691
8692 max_time_cost = max_planets * max_time_cost = 256 * (7 * 7)
8693 max_jump_cost = max_planets * max_jump_cost = 256 * (7 * 256 + 7)
8694 */
8695
8696 // no interstellar space for start and/or goal please
8697 if (start == -1 || goal == -1) return nil;
8698
8699#ifdef CACHE_ROUTE_FROM_SYSTEM_RESULTS
8700
8701 static NSDictionary *c_route = nil;
8702 static OOSystemID c_start, c_goal;
8703 static OORouteType c_optimizeBy;
8704
8705 if (c_route != nil && c_start == start && c_goal == goal && c_optimizeBy == optimizeBy)
8706 {
8707 return c_route;
8708 }
8709
8710#endif
8711
8712 unsigned i, j;
8713
8714 if (start > 255 || goal > 255) return nil;
8715
8716 NSArray *neighbours[256];
8717 BOOL concealed[256];
8718 for (i = 0; i < 256; i++)
8719 {
8720 NSDictionary *systemInfo = [systemManager getPropertiesForSystem:i inGalaxy:galaxyID];
8721 NSInteger concealment = [systemInfo oo_intForKey:@"concealment" defaultValue:OO_SYSTEMCONCEALMENT_NONE];
8722 if (concealment >= OO_SYSTEMCONCEALMENT_NOTHING) {
8723 // system is not known
8724 neighbours[i] = [NSArray array];
8725 concealed[i] = YES;
8726 }
8727 else
8728 {
8729 neighbours[i] = [self neighboursToSystem:i];
8730 concealed[i] = NO;
8731 }
8732 }
8733
8734 RouteElement *cheapest[256] = {0};
8735
8736 double maxCost = optimizeBy == OPTIMIZED_BY_TIME ? 256 * (7 * 7) : 256 * (7 * 256 + 7);
8737
8738 NSMutableArray *curr = [NSMutableArray arrayWithCapacity:256];
8739 [curr addObject:cheapest[start] = [RouteElement elementWithLocation:start parent:-1 cost:0 distance:0 time:0 jumps: 0]];
8740
8741 NSMutableArray *next = [NSMutableArray arrayWithCapacity:256];
8742 while ([curr count] != 0)
8743 {
8744 for (i = 0; i < [curr count]; i++) {
8745 RouteElement *elemI = [curr objectAtIndex:i];
8746 NSArray *ns = neighbours[[elemI location]];
8747 for (j = 0; j < [ns count]; j++)
8748 {
8749 RouteElement *ce = cheapest[[elemI location]];
8750 OOSystemID n = [ns oo_intAtIndex:j];
8751 if (concealed[n])
8752 {
8753 continue;
8754 }
8755 OOSystemID c = [ce location];
8756
8757 NSPoint cpos = [systemManager getCoordinatesForSystem:c inGalaxy:galaxyID];
8758 NSPoint npos = [systemManager getCoordinatesForSystem:n inGalaxy:galaxyID];
8759
8760 double lastDistance = distanceBetweenPlanetPositions(npos.x,npos.y,cpos.x,cpos.y);
8761 double lastTime = lastDistance * lastDistance;
8762
8763 double distance = [ce distance] + lastDistance;
8764 double time = [ce time] + lastTime;
8765 double cost = [ce cost] + (optimizeBy == OPTIMIZED_BY_TIME ? lastTime : 7 * 256 + lastDistance);
8766 int jumps = [ce jumps] + 1;
8767
8768 if (cost < maxCost && (cheapest[n] == nil || [cheapest[n] cost] > cost)) {
8769 RouteElement *e = [RouteElement elementWithLocation:n parent:c cost:cost distance:distance time:time jumps:jumps];
8770 cheapest[n] = e;
8771 [next addObject:e];
8772
8773 if (n == goal && cost < maxCost)
8774 maxCost = cost;
8775 }
8776 }
8777 }
8778 [curr setArray:next];
8779 [next removeAllObjects];
8780 }
8781
8782
8783 if (!cheapest[goal]) return nil;
8784
8785 NSMutableArray *route = [NSMutableArray arrayWithCapacity:256];
8786 RouteElement *e = cheapest[goal];
8787 for (;;)
8788 {
8789 [route insertObject:[NSNumber numberWithInt:[e location]] atIndex:0];
8790 if ([e parent] == -1) break;
8791 e = cheapest[[e parent]];
8792 }
8793
8794#ifdef CACHE_ROUTE_FROM_SYSTEM_RESULTS
8795 c_start = start;
8796 c_goal = goal;
8797 c_optimizeBy = optimizeBy;
8798 [c_route release];
8799 c_route = [[NSDictionary alloc] initWithObjectsAndKeys: route, @"route", [NSNumber numberWithDouble:[cheapest[goal] distance]], @"distance", nil];
8800
8801 return c_route;
8802#else
8803 return [NSDictionary dictionaryWithObjectsAndKeys:
8804 route, @"route",
8805 [NSNumber numberWithDouble:[cheapest[goal] distance]], @"distance",
8806 [NSNumber numberWithDouble:[cheapest[goal] time]], @"time",
8807 [NSNumber numberWithInt:[cheapest[goal] jumps]], @"jumps",
8808 nil];
8809#endif
8810}
8811
8812
8813- (NSArray *) neighboursToSystem: (OOSystemID) s
8814{
8815 if (s == systemID && closeSystems != nil)
8816 {
8817 return closeSystems;
8818 }
8819 NSArray *neighbours = [systemManager getNeighbourIDsForSystem:s inGalaxy:galaxyID];
8820
8821 if (s == systemID)
8822 {
8823 [closeSystems release];
8824 closeSystems = [neighbours copy];
8825 return closeSystems;
8826 }
8827 return neighbours;
8828}
8829
8830
8831/*
8832 Planet texture preloading.
8833
8834 In order to hide the cost of synthesizing textures, we want to start
8835 rendering them asynchronously as soon as there's a hint they may be needed
8836 soon: when a system is selected on one of the charts, and when beginning a
8837 jump. However, it would be a Bad Ideaâ„¢ to allow an arbitrary number of
8838 planets to be queued, since you can click on lots of systems quite
8839 quickly on the long-range chart.
8840
8841 To rate-limit this, we track the materials that are being preloaded and
8842 only queue the ones for a new system if there are no more than two in the
8843 queue. (Currently, each system will have at most two materials, the main
8844 planet and the main planet's atmosphere, but it may be worth adding the
8845 ability to declare planets in planetinfo.plist instead of using scripts so
8846 that they can also benefit from preloading.)
8847
8848 The preloading materials list is pruned before preloading, and also once
8849 per frame so textures can fall out of the regular cache.
8850 -- Ahruman 2009-12-19
8851
8852 DISABLED due to crashes on some Windows systems. Textures generated here
8853 remain in the sRecentTextures cache when released, suggesting a retain
8854 imbalance somewhere. Cannot reproduce under Mac OS X. Needs further
8855 analysis before reenabling.
8856 http://www.aegidian.org/bb/viewtopic.php?f=3&t=12109
8857 -- Ahruman 2012-06-29
8858*/
8859- (void) preloadPlanetTexturesForSystem:(OOSystemID)s
8860{
8861// #if NEW_PLANETS
8862#if 0
8863 [self prunePreloadingPlanetMaterials];
8864
8865 if ([_preloadingPlanetMaterials count] < 3)
8866 {
8867 if (_preloadingPlanetMaterials == nil) _preloadingPlanetMaterials = [[NSMutableArray alloc] initWithCapacity:4];
8868
8869 OOPlanetEntity *planet = [[OOPlanetEntity alloc] initAsMainPlanetForSystem:s];
8870 OOMaterial *surface = [planet material];
8871 // can be nil if texture mis-defined
8872 if (surface != nil)
8873 {
8874 // if it's already loaded, no need to continue
8875 if (![surface isFinishedLoading])
8876 {
8877 [_preloadingPlanetMaterials addObject:surface];
8878
8879 // In some instances (retextured planets atm), the main planet might not have an atmosphere defined.
8880 // Trying to add nil to _preloadingPlanetMaterials will prematurely terminate the calling function.(!) --Kaks 20100107
8881 OOMaterial *atmo = [planet atmosphereMaterial];
8882 if (atmo != nil) [_preloadingPlanetMaterials addObject:atmo];
8883 }
8884 }
8885
8886 [planet release];
8887 }
8888#endif
8889}
8890
8891
8892- (NSDictionary *) globalSettings
8893{
8894 return globalSettings;
8895}
8896
8897
8898- (NSArray *) equipmentData
8899{
8900 return equipmentData;
8901}
8902
8903
8905{
8907}
8908
8909
8914
8915
8916- (NSString *) timeDescription:(double) interval
8917{
8918 double r_time = interval;
8919 NSString* result = @"";
8920
8921 if (r_time > 86400)
8922 {
8923 int days = floor(r_time / 86400);
8924 r_time -= 86400 * days;
8925 result = [NSString stringWithFormat:@"%@ %d day%@", result, days, (days > 1) ? @"s" : @""];
8926 }
8927 if (r_time > 3600)
8928 {
8929 int hours = floor(r_time / 3600);
8930 r_time -= 3600 * hours;
8931 result = [NSString stringWithFormat:@"%@ %d hour%@", result, hours, (hours > 1) ? @"s" : @""];
8932 }
8933 if (r_time > 60)
8934 {
8935 int mins = floor(r_time / 60);
8936 r_time -= 60 * mins;
8937 result = [NSString stringWithFormat:@"%@ %d minute%@", result, mins, (mins > 1) ? @"s" : @""];
8938 }
8939 if (r_time > 0)
8940 {
8941 int secs = floor(r_time);
8942 result = [NSString stringWithFormat:@"%@ %d second%@", result, secs, (secs > 1) ? @"s" : @""];
8943 }
8944 return [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
8945}
8946
8947
8948- (NSString *) shortTimeDescription:(double) interval
8949{
8950 double r_time = interval;
8951 NSString* result = @"";
8952 int parts = 0;
8953
8954 if (interval <= 0.0)
8955 return DESC(@"contracts-no-time");
8956
8957 if (r_time > 86400)
8958 {
8959 int days = floor(r_time / 86400);
8960 r_time -= 86400 * days;
8961 result = [NSString stringWithFormat:@"%@ %d %@", result, days, DESC_PLURAL(@"contracts-day-word", days)];
8962 parts++;
8963 }
8964 if (r_time > 3600)
8965 {
8966 int hours = floor(r_time / 3600);
8967 r_time -= 3600 * hours;
8968 result = [NSString stringWithFormat:@"%@ %d %@", result, hours, DESC_PLURAL(@"contracts-hour-word", hours)];
8969 parts++;
8970 }
8971 if (parts < 2 && r_time > 60)
8972 {
8973 int mins = floor(r_time / 60);
8974 r_time -= 60 * mins;
8975 result = [NSString stringWithFormat:@"%@ %d %@", result, mins, DESC_PLURAL(@"contracts-minute-word", mins)];
8976 parts++;
8977 }
8978 if (parts < 2 && r_time > 0)
8979 {
8980 int secs = floor(r_time);
8981 result = [NSString stringWithFormat:@"%@ %d %@", result, secs, DESC_PLURAL(@"contracts-second-word", secs)];
8982 }
8983 return [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
8984}
8985
8986
8987- (void) makeSunSkimmer:(ShipEntity *) ship andSetAI:(BOOL)setAI
8988{
8989 if (setAI) [ship switchAITo:@"oolite-traderAI.js"]; // perfectly acceptable for both route 2 & 3
8990 [ship setFuel:(Ranrot()&31)];
8991 // slow ships need extra insulation or they will burn up when sunskimming. (Tested at biggest sun in G3: Aenqute)
8992 float minInsulation = 1000 / [ship maxFlightSpeed] + 1;
8993 if ([ship heatInsulation] < minInsulation) [ship setHeatInsulation:minInsulation];
8994}
8995
8996
8998{
9000
9001 // adjust basic seed by market random factor
9002 // which for (very bad) historical reasons is 0x80
9003
9004 ret.f ^= 0x80; // XOR back to front
9005 ret.e ^= ret.f; // XOR
9006 ret.d ^= ret.e; // XOR
9007 ret.c ^= ret.d; // XOR
9008 ret.b ^= ret.c; // XOR
9009 ret.a ^= ret.b; // XOR
9010
9011 return ret;
9012}
9013
9014
9015- (void) loadStationMarkets:(NSArray *)marketData
9016{
9017 if (marketData == nil)
9018 {
9019 return;
9020 }
9021
9022 NSArray *stations = [self stations];
9024 NSDictionary *savedMarket = nil;
9025
9026 foreach (savedMarket, marketData)
9027 {
9028 HPVector pos = [savedMarket oo_hpvectorForKey:@"position"];
9029 foreach (station, stations)
9030 {
9031 // must be deterministic and secondary
9032 if ([station allowsSaving] && station != [UNIVERSE station])
9033 {
9034 // allow a km of drift just in case
9035 if (HPdistance2(pos,[station position]) < 1000000)
9036 {
9037 [station setLocalMarket:[savedMarket oo_arrayForKey:@"market"]];
9038 break;
9039 }
9040 }
9041 }
9042 }
9043
9044}
9045
9046
9048{
9049 NSMutableArray *markets = [[NSMutableArray alloc] init];
9050 NSArray *stations = [self stations];
9051
9053 NSMutableDictionary *savedMarket = nil;
9054
9055 OOCommodityMarket *stationMarket = nil;
9056
9057 foreach (station, stations)
9058 {
9059 // must be deterministic and secondary
9060 if ([station allowsSaving] && station != [UNIVERSE station])
9061 {
9062 stationMarket = [station localMarket];
9063 if (stationMarket != nil)
9064 {
9065 savedMarket = [NSMutableDictionary dictionaryWithCapacity:2];
9066 [savedMarket setObject:[stationMarket saveStationAmounts] forKey:@"market"];
9067 [savedMarket setObject:ArrayFromHPVector([station position]) forKey:@"position"];
9068 [markets addObject:savedMarket];
9069 }
9070 }
9071 }
9072
9073 return [markets autorelease];
9074}
9075
9076
9077- (NSArray *) shipsForSaleForSystem:(OOSystemID)s withTL:(OOTechLevelID)specialTL atTime:(OOTimeAbsolute)current_time
9078{
9079 RANROTSeed saved_seed = RANROTGetFullSeed();
9080 Random_Seed ship_seed = [self marketSeed];
9081
9082 NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionary];
9083
9084 float tech_price_boost = (ship_seed.a + ship_seed.b) / 256.0;
9085 unsigned i;
9086 PlayerEntity *player = PLAYER;
9088 RANROTSeed personalitySeed = RanrotSeedFromRandomSeed(ship_seed);
9089
9090 for (i = 0; i < 256; i++)
9091 {
9092 long long reference_time = 0x1000000 * floor(current_time / 0x1000000);
9093
9094 long long c_time = ship_seed.a * 0x10000 + ship_seed.b * 0x100 + ship_seed.c;
9095 double ship_sold_time = reference_time + c_time;
9096
9097 if (ship_sold_time < 0)
9098 ship_sold_time += 0x1000000; // wraparound
9099
9100 double days_until_sale = (ship_sold_time - current_time) / 86400.0;
9101
9102 NSMutableArray *keysForShips = [NSMutableArray arrayWithArray:[registry playerShipKeys]];
9103 unsigned si;
9104 for (si = 0; si < [keysForShips count]; si++)
9105 {
9106 //eliminate any ships that fail a 'conditions test'
9107 NSString *key = [keysForShips oo_stringAtIndex:si];
9108 NSDictionary *dict = [registry shipyardInfoForKey:key];
9109 NSArray *conditions = [dict oo_arrayForKey:@"conditions"];
9110
9111 if (![player scriptTestConditions:conditions])
9112 {
9113 [keysForShips removeObjectAtIndex:si--];
9114 }
9115 NSString *condition_script = [dict oo_stringForKey:@"condition_script"];
9116 if (condition_script != nil)
9117 {
9118 OOJSScript *condScript = [self getConditionScript:condition_script];
9119 if (condScript != nil) // should always be non-nil, but just in case
9120 {
9121 JSContext *context = OOJSAcquireContext();
9122 BOOL OK;
9123 JSBool allow_purchase;
9124 jsval result;
9125 jsval args[] = { OOJSValueFromNativeObject(context, key) };
9126
9127 OK = [condScript callMethod:OOJSID("allowOfferShip")
9128 inContext:context
9129 withArguments:args count:sizeof args / sizeof *args
9130 result:&result];
9131
9132 if (OK) OK = JS_ValueToBoolean(context, result, &allow_purchase);
9133
9134 OOJSRelinquishContext(context);
9135
9136 if (OK && !allow_purchase)
9137 {
9138 /* if the script exists, the function exists, the function
9139 * returns a bool, and that bool is false, block
9140 * purchase. Otherwise allow it as default */
9141 [keysForShips removeObjectAtIndex:si--];
9142 }
9143 }
9144 }
9145
9146 }
9147
9148 NSDictionary *systemInfo = [self generateSystemData:s];
9149 OOTechLevelID techlevel;
9150 if (specialTL != NSNotFound)
9151 {
9152 //if we are passed a tech level use that
9153 techlevel = specialTL;
9154 }
9155 else
9156 {
9157 //otherwise use default for system
9158 techlevel = [systemInfo oo_unsignedIntForKey:KEY_TECHLEVEL];
9159 }
9160 unsigned ship_index = (ship_seed.d * 0x100 + ship_seed.e) % [keysForShips count];
9161 NSString *ship_key = [keysForShips oo_stringAtIndex:ship_index];
9162 NSDictionary *ship_info = [registry shipyardInfoForKey:ship_key];
9163 OOTechLevelID ship_techlevel = [ship_info oo_intForKey:KEY_TECHLEVEL];
9164
9165 double chance = 1.0 - pow(1.0 - [ship_info oo_doubleForKey:KEY_CHANCE], MAX((OOTechLevelID)1, techlevel - ship_techlevel));
9166
9167 // seed random number generator
9168 int superRand1 = ship_seed.a * 0x10000 + ship_seed.c * 0x100 + ship_seed.e;
9169 uint32_t superRand2 = ship_seed.b * 0x10000 + ship_seed.d * 0x100 + ship_seed.f;
9170 ranrot_srand(superRand2);
9171
9172 NSDictionary* shipBaseDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:ship_key];
9173
9174 if ((days_until_sale > 0.0) && (days_until_sale < 30.0) && (ship_techlevel <= techlevel) && (randf() < chance) && (shipBaseDict != nil))
9175 {
9176 NSMutableDictionary* shipDict = [NSMutableDictionary dictionaryWithDictionary:shipBaseDict];
9177 NSMutableString* shortShipDescription = [NSMutableString stringWithCapacity:256];
9178 NSString *shipName = [shipDict oo_stringForKey:@"display_name" defaultValue:[shipDict oo_stringForKey:KEY_NAME]];
9179 OOCreditsQuantity price = [ship_info oo_unsignedIntForKey:KEY_PRICE];
9180 OOCreditsQuantity base_price = price;
9181 NSMutableArray* extras = [NSMutableArray arrayWithArray:[[ship_info oo_dictionaryForKey:KEY_STANDARD_EQUIPMENT] oo_arrayForKey:KEY_EQUIPMENT_EXTRAS]];
9182 NSString* fwdWeaponString = [[ship_info oo_dictionaryForKey:KEY_STANDARD_EQUIPMENT] oo_stringForKey:KEY_EQUIPMENT_FORWARD_WEAPON];
9183 NSString* aftWeaponString = [[ship_info oo_dictionaryForKey:KEY_STANDARD_EQUIPMENT] oo_stringForKey:KEY_EQUIPMENT_AFT_WEAPON];
9184
9185 NSMutableArray* options = [NSMutableArray arrayWithArray:[ship_info oo_arrayForKey:KEY_OPTIONAL_EQUIPMENT]];
9186 OOCargoQuantity maxCargo = [shipDict oo_unsignedIntForKey:@"max_cargo"];
9187
9188 // more info for potential purchasers - how to reveal this I'm not yet sure...
9189 //NSString* brochure_desc = [self brochureDescriptionWithDictionary: ship_dict standardEquipment: extras optionalEquipment: options];
9190 //NSLog(@"%@ Brochure description : \"%@\"", [ship_dict objectForKey:KEY_NAME], brochure_desc);
9191
9192 [shortShipDescription appendFormat:@"%@:", shipName];
9193
9194 OOWeaponFacingSet availableFacings = [ship_info oo_unsignedIntForKey:KEY_WEAPON_FACINGS defaultValue:VALID_WEAPON_FACINGS] & VALID_WEAPON_FACINGS;
9195
9196 OOWeaponType fwdWeapon = OOWeaponTypeFromEquipmentIdentifierSloppy(fwdWeaponString);
9197 OOWeaponType aftWeapon = OOWeaponTypeFromEquipmentIdentifierSloppy(aftWeaponString);
9198 //port and starboard weapons are not modified in the shipyard
9199 // apply fwd and aft weapons to the ship
9200 if (fwdWeapon && fwdWeaponString) [shipDict setObject:fwdWeaponString forKey:KEY_EQUIPMENT_FORWARD_WEAPON];
9201 if (aftWeapon && aftWeaponString) [shipDict setObject:aftWeaponString forKey:KEY_EQUIPMENT_AFT_WEAPON];
9202
9203 int passengerBerthCount = 0;
9204 BOOL customised = NO;
9205 BOOL weaponCustomized = NO;
9206
9207 NSString *fwdWeaponDesc = nil;
9208
9209 NSString *shortExtrasKey = @"shipyard-first-extra";
9210
9211 // for testing condition scripts
9212 ShipEntity *testship = [[ProxyPlayerEntity alloc] initWithKey:ship_key definition:shipDict];
9213 // customise the ship (if chance = 1, then ship will get all possible add ons)
9214 while ((randf() < chance) && ([options count]))
9215 {
9216 chance *= chance; //decrease the chance of a further customisation (unless it is 1, which might be a bug)
9217 int optionIndex = Ranrot() % [options count];
9218 NSString *equipmentKey = [options oo_stringAtIndex:optionIndex];
9220
9221 if (item != nil)
9222 {
9223 OOTechLevelID eqTechLevel = [item techLevel];
9224 OOCreditsQuantity eqPrice = [item price] / 10; // all amounts are x/10 due to being represented in tenths of credits.
9225 NSString *eqShortDesc = [item name];
9226
9227 if ([item techLevel] > techlevel)
9228 {
9229 // Cap maximum tech level.
9230 eqTechLevel = MIN(eqTechLevel, 15U);
9231
9232 // Higher tech items are rarer!
9233 if (randf() * (eqTechLevel - techlevel) < 1.0)
9234 {
9235 // All included equip has a 10% discount.
9236 eqPrice *= (tech_price_boost + eqTechLevel - techlevel) * 90 / 100;
9237 }
9238 else
9239 break; // Bar this upgrade.
9240 }
9241
9242 if ([item incompatibleEquipment] != nil && extras != nil)
9243 {
9244 NSEnumerator *keyEnum = nil;
9245 id key = nil;
9246 BOOL incompatible = NO;
9247
9248 for (keyEnum = [[item incompatibleEquipment] objectEnumerator]; (key = [keyEnum nextObject]); )
9249 {
9250 if ([extras containsObject:key])
9251 {
9252 [options removeObject:equipmentKey];
9253 incompatible = YES;
9254 break;
9255 }
9256 }
9257 if (incompatible) break;
9258
9259 // make sure the incompatible equipment is not choosen later on.
9260 for (keyEnum = [[item incompatibleEquipment] objectEnumerator]; (key = [keyEnum nextObject]); )
9261 {
9262 if ([options containsObject:key])
9263 {
9264 [options removeObject:key];
9265 }
9266 }
9267 }
9268
9269 /* Check condition scripts */
9270 NSString *condition_script = [item conditionScript];
9271 if (condition_script != nil)
9272 {
9273 OOJSScript *condScript = [self getConditionScript:condition_script];
9274 if (condScript != nil) // should always be non-nil, but just in case
9275 {
9276 JSContext *JScontext = OOJSAcquireContext();
9277 BOOL OK;
9278 JSBool allow_addition;
9279 jsval result;
9280 jsval args[] = { OOJSValueFromNativeObject(JScontext, equipmentKey) , OOJSValueFromNativeObject(JScontext, testship) , OOJSValueFromNativeObject(JScontext, @"newShip")};
9281
9282 OK = [condScript callMethod:OOJSID("allowAwardEquipment")
9283 inContext:JScontext
9284 withArguments:args count:sizeof args / sizeof *args
9285 result:&result];
9286
9287 if (OK) OK = JS_ValueToBoolean(JScontext, result, &allow_addition);
9288
9289 OOJSRelinquishContext(JScontext);
9290
9291 if (OK && !allow_addition)
9292 {
9293 /* if the script exists, the function exists, the function
9294 * returns a bool, and that bool is false, block
9295 * addition. Otherwise allow it as default */
9296 break;
9297 }
9298 }
9299 }
9300
9301
9302 if ([item requiresEquipment] != nil && extras != nil)
9303 {
9304 NSEnumerator *keyEnum = nil;
9305 id key = nil;
9306 BOOL missing = NO;
9307
9308 for (keyEnum = [[item requiresEquipment] objectEnumerator]; (key = [keyEnum nextObject]); )
9309 {
9310 if (![extras containsObject:key])
9311 {
9312 missing = YES;
9313 }
9314 }
9315 if (missing) break;
9316 }
9317
9318 if ([item requiresAnyEquipment] != nil && extras != nil)
9319 {
9320 NSEnumerator *keyEnum = nil;
9321 id key = nil;
9322 BOOL missing = YES;
9323
9324 for (keyEnum = [[item requiresAnyEquipment] objectEnumerator]; (key = [keyEnum nextObject]); )
9325 {
9326 if ([extras containsObject:key])
9327 {
9328 missing = NO;
9329 }
9330 }
9331 if (missing) break;
9332 }
9333
9334 // Special case, NEU has to be compatible with EEU inside equipment.plist
9335 // but we can only have either one or the other on board.
9336 if ([equipmentKey isEqualTo:@"EQ_NAVAL_ENERGY_UNIT"])
9337 {
9338 if ([extras containsObject:@"EQ_ENERGY_UNIT"])
9339 {
9340 [options removeObject:equipmentKey];
9341 break;
9342 }
9343 }
9344
9345 if ([equipmentKey hasPrefix:@"EQ_WEAPON"])
9346 {
9348 //fit best weapon forward
9349 if (availableFacings & WEAPON_FACING_FORWARD && [new_weapon weaponThreatAssessment] > [fwdWeapon weaponThreatAssessment])
9350 {
9351 //again remember to divide price by 10 to get credits from tenths of credit
9352 price -= [self getEquipmentPriceForKey:fwdWeaponString] * 90 / 1000; // 90% credits
9353 price += eqPrice;
9354 fwdWeaponString = equipmentKey;
9355 fwdWeapon = new_weapon;
9356 [shipDict setObject:fwdWeaponString forKey:KEY_EQUIPMENT_FORWARD_WEAPON];
9357 weaponCustomized = YES;
9358 fwdWeaponDesc = eqShortDesc;
9359 }
9360 else
9361 {
9362 //if less good than current forward, try fitting is to rear
9363 if (availableFacings & WEAPON_FACING_AFT && (isWeaponNone(aftWeapon) || [new_weapon weaponThreatAssessment] > [aftWeapon weaponThreatAssessment]))
9364 {
9365 price -= [self getEquipmentPriceForKey:aftWeaponString] * 90 / 1000; // 90% credits
9366 price += eqPrice;
9367 aftWeaponString = equipmentKey;
9368 aftWeapon = new_weapon;
9369 [shipDict setObject:aftWeaponString forKey:KEY_EQUIPMENT_AFT_WEAPON];
9370 }
9371 else
9372 {
9373 [options removeObject:equipmentKey]; //dont try again
9374 }
9375 }
9376
9377 }
9378 else
9379 {
9380 if ([equipmentKey isEqualToString:@"EQ_PASSENGER_BERTH"])
9381 {
9382 if ((maxCargo >= PASSENGER_BERTH_SPACE) && (randf() < chance))
9383 {
9384 maxCargo -= PASSENGER_BERTH_SPACE;
9385 price += eqPrice;
9386 [extras addObject:equipmentKey];
9387 passengerBerthCount++;
9388 customised = YES;
9389 }
9390 else
9391 {
9392 // remove the option if there's no space left
9393 [options removeObject:equipmentKey];
9394 }
9395 }
9396 else
9397 {
9398 price += eqPrice;
9399 [extras addObject:equipmentKey];
9400 if ([item isVisible])
9401 {
9402 NSString *item = eqShortDesc;
9403 [shortShipDescription appendString:OOExpandKey(shortExtrasKey, item)];
9404 shortExtrasKey = @"shipyard-additional-extra";
9405 }
9406 customised = YES;
9407 [options removeObject:equipmentKey]; //dont add twice
9408 }
9409 }
9410 }
9411 else
9412 {
9413 [options removeObject:equipmentKey];
9414 }
9415 } // end adding optional equipment
9416 [testship release];
9417 // i18n: Some languages require that no conversion to lower case string takes place.
9418 BOOL lowercaseIgnore = [[self descriptions] oo_boolForKey:@"lowercase_ignore"];
9419
9420 if (passengerBerthCount)
9421 {
9422 NSString* npb = (passengerBerthCount > 1)? [NSString stringWithFormat:@"%d ", passengerBerthCount] : (id)@"";
9423 NSString* ppb = DESC_PLURAL(@"passenger-berth", passengerBerthCount);
9424 NSString* extraPassengerBerthsDescription = [NSString stringWithFormat:DESC(@"extra-@-@-(passenger-berths)"), npb, ppb];
9425 NSString *item = extraPassengerBerthsDescription;
9426 [shortShipDescription appendString:OOExpandKey(shortExtrasKey, item)];
9427 shortExtrasKey = @"shipyard-additional-extra";
9428 }
9429
9430 if (!customised)
9431 {
9432 [shortShipDescription appendString:OOExpandKey(@"shipyard-standard-customer-model")];
9433 }
9434
9435 if (weaponCustomized)
9436 {
9437 NSString *weapon = (lowercaseIgnore ? fwdWeaponDesc : [fwdWeaponDesc lowercaseString]);
9438 [shortShipDescription appendString:OOExpandKey(@"shipyard-forward-weapon-upgraded", weapon)];
9439 }
9440 if (price > base_price)
9441 {
9442 price = base_price + cunningFee(price - base_price, 0.05);
9443 }
9444
9445 [shortShipDescription appendString:OOExpandKey(@"shipyard-price", price)];
9446
9447 NSString *shipID = [NSString stringWithFormat:@"%06x-%06x", superRand1, superRand2];
9448
9449 uint16_t personality = RanrotWithSeed(&personalitySeed) & ENTITY_PERSONALITY_MAX;
9450
9451 NSDictionary *ship_info_dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
9452 shipID, SHIPYARD_KEY_ID,
9453 ship_key, SHIPYARD_KEY_SHIPDATA_KEY,
9454 shipDict, SHIPYARD_KEY_SHIP,
9455 shortShipDescription, KEY_SHORT_DESCRIPTION,
9456 [NSNumber numberWithUnsignedLongLong:price], SHIPYARD_KEY_PRICE,
9457 extras, KEY_EQUIPMENT_EXTRAS,
9458 [NSNumber numberWithUnsignedShort:personality], SHIPYARD_KEY_PERSONALITY,
9459 NULL];
9460
9461 [resultDictionary setObject:ship_info_dictionary forKey:shipID]; // should order them fairly randomly
9462 }
9463
9464 // next contract
9465 rotate_seed(&ship_seed);
9466 rotate_seed(&ship_seed);
9467 rotate_seed(&ship_seed);
9468 rotate_seed(&ship_seed);
9469 }
9470
9471 NSMutableArray *resultArray = [[[resultDictionary allValues] mutableCopy] autorelease];
9472 [resultArray sortUsingFunction:compareName context:NULL];
9473
9474 // remove identically priced ships of the same name
9475 i = 1;
9476
9477 while (i < [resultArray count])
9478 {
9479 if (compareName([resultArray objectAtIndex:i - 1], [resultArray objectAtIndex:i], nil) == NSOrderedSame )
9480 {
9481 [resultArray removeObjectAtIndex: i];
9482 }
9483 else
9484 {
9485 i++;
9486 }
9487 }
9488
9489 RANROTSetFullSeed(saved_seed);
9490
9491 return [NSArray arrayWithArray:resultArray];
9492}
9493
9494
9495static OOComparisonResult compareName(id dict1, id dict2, void *context)
9496{
9497 NSDictionary *ship1 = [(NSDictionary *)dict1 oo_dictionaryForKey:SHIPYARD_KEY_SHIP];
9498 NSDictionary *ship2 = [(NSDictionary *)dict2 oo_dictionaryForKey:SHIPYARD_KEY_SHIP];
9499 NSString *name1 = [ship1 oo_stringForKey:KEY_NAME];
9500 NSString *name2 = [ship2 oo_stringForKey:KEY_NAME];
9501
9502 NSComparisonResult result = [[name1 lowercaseString] compare:[name2 lowercaseString]];
9503 if (result != NSOrderedSame)
9504 return result;
9505 else
9506 return comparePrice(dict1, dict2, context);
9507}
9508
9509
9510static OOComparisonResult comparePrice(id dict1, id dict2, void *context)
9511{
9512 NSNumber *price1 = [(NSDictionary *)dict1 objectForKey:SHIPYARD_KEY_PRICE];
9513 NSNumber *price2 = [(NSDictionary *)dict2 objectForKey:SHIPYARD_KEY_PRICE];
9514
9515 return [price1 compare:price2];
9516}
9517
9518
9519- (OOCreditsQuantity) tradeInValueForCommanderDictionary:(NSDictionary *)dict
9520{
9521 // get basic information about the craft
9522 OOCreditsQuantity base_price = 0ULL;
9523 NSString *ship_desc = [dict oo_stringForKey:@"ship_desc"];
9524 NSDictionary *shipyard_info = [[OOShipRegistry sharedRegistry] shipyardInfoForKey:ship_desc];
9525 // This checks a rare, but possible case. If the ship for which we are trying to calculate a trade in value
9526 // does not have a shipyard dictionary entry, report it and set its base price to 0 -- Nikos 20090613.
9527 if (shipyard_info == nil)
9528 {
9529 OOLogERR(@"universe.tradeInValueForCommanderDictionary.valueCalculationError",
9530 @"Shipyard dictionary entry for ship %@ required for trade in value calculation, but does not exist. Setting ship value to 0.", ship_desc);
9531 }
9532 else
9533 {
9534 base_price = [shipyard_info oo_unsignedLongLongForKey:SHIPYARD_KEY_PRICE defaultValue:0ULL];
9535 }
9536
9537 if(base_price == 0ULL) return base_price;
9538
9539 OOCreditsQuantity scrap_value = 351; // translates to 250 cr.
9540
9541 OOWeaponType ship_fwd_weapon = [OOEquipmentType equipmentTypeWithIdentifier:[dict oo_stringForKey:@"forward_weapon"]];
9542 OOWeaponType ship_aft_weapon = [OOEquipmentType equipmentTypeWithIdentifier:[dict oo_stringForKey:@"aft_weapon"]];
9543 OOWeaponType ship_port_weapon = [OOEquipmentType equipmentTypeWithIdentifier:[dict oo_stringForKey:@"port_weapon"]];
9544 OOWeaponType ship_starboard_weapon = [OOEquipmentType equipmentTypeWithIdentifier:[dict oo_stringForKey:@"starboard_weapon"]];
9545 unsigned ship_missiles = [dict oo_unsignedIntForKey:@"missiles"];
9546 unsigned ship_max_passengers = [dict oo_unsignedIntForKey:@"max_passengers"];
9547 NSMutableArray *ship_extra_equipment = [NSMutableArray arrayWithArray:[[dict oo_dictionaryForKey:@"extra_equipment"] allKeys]];
9548
9549 NSDictionary *basic_info = [shipyard_info oo_dictionaryForKey:KEY_STANDARD_EQUIPMENT];
9550 unsigned base_missiles = [basic_info oo_unsignedIntForKey:KEY_EQUIPMENT_MISSILES];
9551 OOCreditsQuantity base_missiles_value = base_missiles * [UNIVERSE getEquipmentPriceForKey:@"EQ_MISSILE"] / 10;
9552 NSString *base_weapon_key = [basic_info oo_stringForKey:KEY_EQUIPMENT_FORWARD_WEAPON];
9553 OOCreditsQuantity base_weapons_value = [UNIVERSE getEquipmentPriceForKey:base_weapon_key] / 10;
9554 NSMutableArray *base_extra_equipment = [NSMutableArray arrayWithArray:[basic_info oo_arrayForKey:KEY_EQUIPMENT_EXTRAS]];
9555 NSString *weapon_key = nil;
9556
9557 // was aft_weapon defined as standard equipment ?
9558 base_weapon_key = [basic_info oo_stringForKey:KEY_EQUIPMENT_AFT_WEAPON defaultValue:nil];
9559 if (base_weapon_key != nil)
9560 base_weapons_value += [UNIVERSE getEquipmentPriceForKey:base_weapon_key] / 10;
9561
9562 OOCreditsQuantity ship_main_weapons_value = 0;
9563 OOCreditsQuantity ship_other_weapons_value = 0;
9564 OOCreditsQuantity ship_missiles_value = 0;
9565
9566 // calculate the actual value for the missiles present on board.
9567 NSArray *missileRoles = [dict oo_arrayForKey:@"missile_roles"];
9568 if (missileRoles != nil)
9569 {
9570 unsigned i;
9571 for (i = 0; i < ship_missiles; i++)
9572 {
9573 NSString *missile_desc = [missileRoles oo_stringAtIndex:i];
9574 if (missile_desc != nil && ![missile_desc isEqualToString:@"NONE"])
9575 {
9576 ship_missiles_value += [UNIVERSE getEquipmentPriceForKey:missile_desc] / 10;
9577 }
9578 }
9579 }
9580 else
9581 ship_missiles_value = ship_missiles * [UNIVERSE getEquipmentPriceForKey:@"EQ_MISSILE"] / 10;
9582
9583 // needs to be a signed value, we can then subtract from the base price, if less than standard equipment.
9584 long long extra_equipment_value = ship_max_passengers * [UNIVERSE getEquipmentPriceForKey:@"EQ_PASSENGER_BERTH"]/10;
9585
9586 // add on missile values
9587 extra_equipment_value += ship_missiles_value - base_missiles_value;
9588
9589 // work out weapon values
9590 if (ship_fwd_weapon)
9591 {
9592 weapon_key = OOEquipmentIdentifierFromWeaponType(ship_fwd_weapon);
9593 ship_main_weapons_value = [UNIVERSE getEquipmentPriceForKey:weapon_key] / 10;
9594 }
9595 if (ship_aft_weapon)
9596 {
9597 weapon_key = OOEquipmentIdentifierFromWeaponType(ship_aft_weapon);
9598 if (base_weapon_key != nil) // aft weapon was defined as a base weapon
9599 {
9600 ship_main_weapons_value += [UNIVERSE getEquipmentPriceForKey:weapon_key] / 10; //take weapon downgrades into account
9601 }
9602 else
9603 {
9604 ship_other_weapons_value += [UNIVERSE getEquipmentPriceForKey:weapon_key] / 10;
9605 }
9606 }
9607 if (ship_port_weapon)
9608 {
9609 weapon_key = OOEquipmentIdentifierFromWeaponType(ship_port_weapon);
9610 ship_other_weapons_value += [UNIVERSE getEquipmentPriceForKey:weapon_key] / 10;
9611 }
9612 if (ship_starboard_weapon)
9613 {
9614 weapon_key = OOEquipmentIdentifierFromWeaponType(ship_starboard_weapon);
9615 ship_other_weapons_value += [UNIVERSE getEquipmentPriceForKey:weapon_key] / 10;
9616 }
9617
9618 // add on extra weapons, take away the value of the base weapons
9619 extra_equipment_value += ship_other_weapons_value;
9620 extra_equipment_value += ship_main_weapons_value - base_weapons_value;
9621
9622 NSInteger i;
9623 NSString *eq_key = nil;
9624
9625 // shipyard.plist settings might have duplicate keys.
9626 // cull possible duplicates from inside base equipment
9627 for (i = [base_extra_equipment count]-1; i > 0;i--)
9628 {
9629 eq_key = [base_extra_equipment oo_stringAtIndex:i];
9630 if ([base_extra_equipment indexOfObject:eq_key inRange:NSMakeRange(0, i-1)] != NSNotFound)
9631 [base_extra_equipment removeObjectAtIndex:i];
9632 }
9633
9634 // do we at least have the same equipment as a standard ship?
9635 for (i = [base_extra_equipment count]-1; i >= 0; i--)
9636 {
9637 eq_key = [base_extra_equipment oo_stringAtIndex:i];
9638 if ([ship_extra_equipment containsObject:eq_key])
9639 [ship_extra_equipment removeObject:eq_key];
9640 else // if the ship has less equipment than standard, deduct the missing equipent's price
9641 extra_equipment_value -= ([UNIVERSE getEquipmentPriceForKey:eq_key] / 10);
9642 }
9643
9644 // remove portable equipment from the totals
9645 OOEquipmentType *item = nil;
9646
9647 for (i = [ship_extra_equipment count]-1; i >= 0; i--)
9648 {
9649 eq_key = [ship_extra_equipment oo_stringAtIndex:i];
9651 if ([item isPortableBetweenShips]) [ship_extra_equipment removeObjectAtIndex:i];
9652 }
9653
9654 // add up what we've got left.
9655 for (i = [ship_extra_equipment count]-1; i >= 0; i--)
9656 extra_equipment_value += ([UNIVERSE getEquipmentPriceForKey:[ship_extra_equipment oo_stringAtIndex:i]] / 10);
9657
9658 // 10% discount for second hand value, steeper reduction if worse than standard.
9659 extra_equipment_value *= extra_equipment_value < 0 ? 1.4 : 0.9;
9660
9661 // we'll return at least the scrap value
9662 // TODO: calculate scrap value based on the size of the ship.
9663 if ((long long)scrap_value > (long long)base_price + extra_equipment_value) return scrap_value;
9664
9665 return base_price + extra_equipment_value;
9666}
9667
9668
9669- (NSString *) brochureDescriptionWithDictionary:(NSDictionary *)dict standardEquipment:(NSArray *)extras optionalEquipment:(NSArray *)options
9670{
9671 NSMutableArray *mut_extras = [NSMutableArray arrayWithArray:extras];
9672 NSString *allOptions = [options componentsJoinedByString:@" "];
9673
9674 NSMutableString *desc = [NSMutableString stringWithFormat:@"The %@.", [dict oo_stringForKey: KEY_NAME]];
9675
9676 // cargo capacity and expansion
9677 OOCargoQuantity max_cargo = [dict oo_unsignedIntForKey:@"max_cargo"];
9678 if (max_cargo)
9679 {
9680 OOCargoQuantity extra_cargo = [dict oo_unsignedIntForKey:@"extra_cargo" defaultValue:15];
9681 [desc appendFormat:@" Cargo capacity %dt", max_cargo];
9682 BOOL canExpand = ([allOptions rangeOfString:@"EQ_CARGO_BAY"].location != NSNotFound);
9683 if (canExpand)
9684 [desc appendFormat:@" (expandable to %dt at most starports)", max_cargo + extra_cargo];
9685 [desc appendString:@"."];
9686 }
9687
9688 // speed
9689 float top_speed = [dict oo_intForKey:@"max_flight_speed"];
9690 [desc appendFormat:@" Top speed %.3fLS.", 0.001 * top_speed];
9691
9692 // passenger berths
9693 if ([mut_extras count])
9694 {
9695 unsigned n_berths = 0;
9696 unsigned i;
9697 for (i = 0; i < [mut_extras count]; i++)
9698 {
9699 NSString* item_key = [mut_extras oo_stringAtIndex:i];
9700 if ([item_key isEqual:@"EQ_PASSENGER_BERTH"])
9701 {
9702 n_berths++;
9703 [mut_extras removeObjectAtIndex:i--];
9704 }
9705 }
9706 if (n_berths)
9707 {
9708 if (n_berths == 1)
9709 [desc appendString:@" Includes luxury accomodation for a single passenger."];
9710 else
9711 [desc appendFormat:@" Includes luxury accomodation for %d passengers.", n_berths];
9712 }
9713 }
9714
9715 // standard fittings
9716 if ([mut_extras count])
9717 {
9718 [desc appendString:@"\nComes with"];
9719 unsigned i, j;
9720 for (i = 0; i < [mut_extras count]; i++)
9721 {
9722 NSString* item_key = [mut_extras oo_stringAtIndex:i];
9723 NSString* item_desc = nil;
9724 for (j = 0; ((j < [equipmentData count])&&(!item_desc)) ; j++)
9725 {
9726 NSString *eq_type = [[equipmentData oo_arrayAtIndex:j] oo_stringAtIndex:EQUIPMENT_KEY_INDEX];
9727 if ([eq_type isEqual:item_key])
9728 item_desc = [[equipmentData oo_arrayAtIndex:j] oo_stringAtIndex:EQUIPMENT_SHORT_DESC_INDEX];
9729 }
9730 if (item_desc)
9731 {
9732 switch ([mut_extras count] - i)
9733 {
9734 case 1:
9735 [desc appendFormat:@" %@ fitted as standard.", item_desc];
9736 break;
9737 case 2:
9738 [desc appendFormat:@" %@ and", item_desc];
9739 break;
9740 default:
9741 [desc appendFormat:@" %@,", item_desc];
9742 break;
9743 }
9744 }
9745 }
9746 }
9747
9748 // optional fittings
9749 if ([options count])
9750 {
9751 [desc appendString:@"\nCan additionally be outfitted with"];
9752 unsigned i, j;
9753 for (i = 0; i < [options count]; i++)
9754 {
9755 NSString* item_key = [options oo_stringAtIndex:i];
9756 NSString* item_desc = nil;
9757 for (j = 0; ((j < [equipmentData count])&&(!item_desc)) ; j++)
9758 {
9759 NSString *eq_type = [[equipmentData oo_arrayAtIndex:j] oo_stringAtIndex:EQUIPMENT_KEY_INDEX];
9760 if ([eq_type isEqual:item_key])
9761 item_desc = [[equipmentData oo_arrayAtIndex:j] oo_stringAtIndex:EQUIPMENT_SHORT_DESC_INDEX];
9762 }
9763 if (item_desc)
9764 {
9765 switch ([options count] - i)
9766 {
9767 case 1:
9768 [desc appendFormat:@" %@ at suitably equipped starports.", item_desc];
9769 break;
9770 case 2:
9771 [desc appendFormat:@" %@ and/or", item_desc];
9772 break;
9773 default:
9774 [desc appendFormat:@" %@,", item_desc];
9775 break;
9776 }
9777 }
9778 }
9779 }
9780
9781 return desc;
9782}
9783
9784
9786{
9787 return kZeroHPVector;
9788}
9789
9790
9792{
9793 // this should be fairly close to {0,0,0,1}
9794 Quaternion q_result;
9795
9796// CIM: seems to be no reason why this should be a per-system constant
9797// - trying it without resetting the RNG for now
9798// seed_RNG_only_for_planet_description(system_seed);
9799
9800 q_result.x = (gen_rnd_number() - 128)/1024.0;
9801 q_result.y = (gen_rnd_number() - 128)/1024.0;
9802 q_result.z = (gen_rnd_number() - 128)/1024.0;
9803 q_result.w = 1.0;
9804 quaternion_normalize(&q_result);
9805
9806 return q_result;
9807}
9808
9809// FIXME: should use vector functions
9810- (HPVector) getSunSkimStartPositionForShip:(ShipEntity*) ship
9811{
9812 if (!ship)
9813 {
9814 OOLog(kOOLogParameterError, @"%@", @"***** No ship set in Universe getSunSkimStartPositionForShip:");
9815 return kZeroHPVector;
9816 }
9817 OOSunEntity* the_sun = [self sun];
9818 // get vector from sun position to ship
9819 if (!the_sun)
9820 {
9821 OOLog(kOOLogInconsistentState, @"%@", @"***** No sun set in Universe getSunSkimStartPositionForShip:");
9822 return kZeroHPVector;
9823 }
9824 HPVector v0 = the_sun->position;
9825 HPVector v1 = ship->position;
9826 v1.x -= v0.x; v1.y -= v0.y; v1.z -= v0.z; // vector from sun to ship
9827 if (v1.x||v1.y||v1.z)
9828 v1 = HPvector_normal(v1);
9829 else
9830 v1.z = 1.0;
9831 double radius = SUN_SKIM_RADIUS_FACTOR * the_sun->collision_radius - 250.0; // 250 m inside the skim radius
9832 v1.x *= radius; v1.y *= radius; v1.z *= radius;
9833 v1.x += v0.x; v1.y += v0.y; v1.z += v0.z;
9834
9835 return v1;
9836}
9837
9838// FIXME: should use vector functions
9839- (HPVector) getSunSkimEndPositionForShip:(ShipEntity*) ship
9840{
9841 OOSunEntity* the_sun = [self sun];
9842 if (!ship)
9843 {
9844 OOLog(kOOLogParameterError, @"%@", @"***** No ship set in Universe getSunSkimEndPositionForShip:");
9845 return kZeroHPVector;
9846 }
9847 // get vector from sun position to ship
9848 if (!the_sun)
9849 {
9850 OOLog(kOOLogInconsistentState, @"%@", @"***** No sun set in Universe getSunSkimEndPositionForShip:");
9851 return kZeroHPVector;
9852 }
9853 HPVector v0 = the_sun->position;
9854 HPVector v1 = ship->position;
9855 v1.x -= v0.x; v1.y -= v0.y; v1.z -= v0.z;
9856 if (v1.x||v1.y||v1.z)
9857 v1 = HPvector_normal(v1);
9858 else
9859 v1.z = 1.0;
9860 HPVector v2 = make_HPvector(randf()-0.5, randf()-0.5, randf()-0.5); // random vector
9861 if (v2.x||v2.y||v2.z)
9862 v2 = HPvector_normal(v2);
9863 else
9864 v2.x = 1.0;
9865 HPVector v3 = HPcross_product(v1, v2); // random vector at 90 degrees to v1 and v2 (random Vector)
9866 if (v3.x||v3.y||v3.z)
9867 v3 = HPvector_normal(v3);
9868 else
9869 v3.y = 1.0;
9870 double radius = SUN_SKIM_RADIUS_FACTOR * the_sun->collision_radius - 250.0; // 250 m inside the skim radius
9871 v1.x *= radius; v1.y *= radius; v1.z *= radius;
9872 v1.x += v0.x; v1.y += v0.y; v1.z += v0.z;
9873 v1.x += 15000 * v3.x; v1.y += 15000 * v3.y; v1.z += 15000 * v3.z; // point 15000m at a tangent to sun from v1
9874 v1.x -= v0.x; v1.y -= v0.y; v1.z -= v0.z;
9875 if (v1.x||v1.y||v1.z)
9876 v1 = HPvector_normal(v1);
9877 else
9878 v1.z = 1.0;
9879 v1.x *= radius; v1.y *= radius; v1.z *= radius;
9880 v1.x += v0.x; v1.y += v0.y; v1.z += v0.z;
9881
9882 return v1;
9883}
9884
9885
9886- (NSArray *) listBeaconsWithCode:(NSString *)code
9887{
9888 NSMutableArray *result = [NSMutableArray array];
9889 Entity <OOBeaconEntity> *beacon = [self firstBeacon];
9890
9891 while (beacon != nil)
9892 {
9893 NSString *beaconCode = [beacon beaconCode];
9894 if ([beaconCode rangeOfString:code options: NSCaseInsensitiveSearch].location != NSNotFound)
9895 {
9896 [result addObject:beacon];
9897 }
9898 beacon = [beacon nextBeacon];
9899 }
9900
9901 return [result sortedArrayUsingSelector:@selector(compareBeaconCodeWith:)];
9902}
9903
9904
9905- (void) allShipsDoScriptEvent:(jsid)event andReactToAIMessage:(NSString *)message
9906{
9907 int i;
9908 int ent_count = n_entities;
9909 int ship_count = 0;
9910 ShipEntity* my_ships[ent_count];
9911 for (i = 0; i < ent_count; i++)
9912 {
9913 if (sortedEntities[i]->isShip)
9914 {
9915 my_ships[ship_count++] = [(ShipEntity *)sortedEntities[i] retain]; // retained
9916 }
9917 }
9918
9919 for (i = 0; i < ship_count; i++)
9920 {
9921 ShipEntity* se = my_ships[i];
9922 [se doScriptEvent:event];
9923 if (message != nil) [[se getAI] reactToMessage:message context:@"global message"];
9924 [se release]; // released
9925 }
9926}
9927
9929
9931{
9932 return gui;
9933}
9934
9935
9937{
9938 return comm_log_gui;
9939}
9940
9941
9943{
9944 return message_gui;
9945}
9946
9947
9949{
9950 [gui clear];
9953 [comm_log_gui printLongText:DESC(@"communications-log-string")
9954 align:GUI_ALIGN_CENTER color:[OOColor yellowColor] fadeTime:0 key:nil addToArray:nil];
9955}
9956
9957
9962
9963
9964- (void) setDisplayText:(BOOL) value
9965{
9966 displayGUI = !!value;
9967}
9968
9969
9971{
9972 return displayGUI;
9973}
9974
9975
9976- (void) setDisplayFPS:(BOOL) value
9977{
9978 displayFPS = !!value;
9979}
9980
9981
9983{
9984 return displayFPS;
9985}
9986
9987
9988- (void) setAutoSave:(BOOL) value
9989{
9990 autoSave = !!value;
9991 [[NSUserDefaults standardUserDefaults] setBool:autoSave forKey:@"autosave"];
9992}
9993
9994
9995- (BOOL) autoSave
9996{
9997 return autoSave;
9998}
9999
10000
10001- (void) setAutoSaveNow:(BOOL) value
10002{
10003 autoSaveNow = !!value;
10004}
10005
10006
10008{
10009 return autoSaveNow;
10010}
10011
10012
10013- (void) setWireframeGraphics:(BOOL) value
10014{
10015 wireframeGraphics = !!value;
10016 [[NSUserDefaults standardUserDefaults] setBool:wireframeGraphics forKey:@"wireframe-graphics"];
10017}
10018
10019
10021{
10022 return wireframeGraphics;
10023}
10024
10025
10027{
10029}
10030
10031
10032/* Only to be called directly at initialisation */
10033- (void) setDetailLevelDirectly:(OOGraphicsDetail)value
10034{
10035 if (value >= DETAIL_LEVEL_MAXIMUM)
10036 {
10037 value = DETAIL_LEVEL_MAXIMUM;
10038 }
10039 else if (value <= DETAIL_LEVEL_MINIMUM)
10040 {
10041 value = DETAIL_LEVEL_MINIMUM;
10042 }
10043 if (![[OOOpenGLExtensionManager sharedManager] shadersSupported])
10044 {
10045 value = DETAIL_LEVEL_MINIMUM;
10046 }
10047 detailLevel = value;
10048}
10049
10050
10051- (void) setDetailLevel:(OOGraphicsDetail)value
10052{
10054 [self setDetailLevelDirectly:value];
10055 [[NSUserDefaults standardUserDefaults] setInteger:detailLevel forKey:@"detailLevel"];
10056 // if changed then reset graphics state
10057 // (some items now require this even if shader on/off mode unchanged)
10058 if (old != detailLevel)
10059 {
10060 OOLog(@"rendering.detail-level", @"Detail level set to %@.", OOStringFromGraphicsDetail(detailLevel));
10062 }
10063
10064}
10065
10067{
10068 return detailLevel;
10069}
10070
10071
10073{
10075}
10076
10077
10078- (void) handleOoliteException:(NSException *)exception
10079{
10080 if (exception != nil)
10081 {
10082 if ([[exception name] isEqual:OOLITE_EXCEPTION_FATAL])
10083 {
10084 PlayerEntity *player = PLAYER;
10085 [player setStatus:STATUS_HANDLING_ERROR];
10086
10087 OOLog(kOOLogException, @"***** Handling Fatal : %@ : %@ *****",[exception name], [exception reason]);
10088 NSString* exception_msg = [NSString stringWithFormat:@"Exception : %@ : %@ Please take a screenshot and/or press esc or Q to quit.", [exception name], [exception reason]];
10089 [self addMessage:exception_msg forCount:30.0];
10090 [[self gameController] setGamePaused:YES];
10091 }
10092 else
10093 {
10094 OOLog(kOOLogException, @"***** Handling Non-fatal : %@ : %@ *****",[exception name], [exception reason]);
10095 }
10096 }
10097}
10098
10099
10101{
10102 return airResistanceFactor;
10103}
10104
10105
10106- (void) setAirResistanceFactor:(GLfloat)newFactor
10107{
10108 airResistanceFactor = OOClamp_0_1_f(newFactor);
10109}
10110
10111
10112// speech routines
10113#if OOLITE_MAC_OS_X
10114
10115- (void) startSpeakingString:(NSString *) text
10116{
10117 [speechSynthesizer startSpeakingString:[NSString stringWithFormat:@"[[volm %.3f]]%@", 0.3333333f * [OOSound masterVolume], text]];
10118}
10119
10120
10122{
10123 if ([speechSynthesizer respondsToSelector:@selector(stopSpeakingAtBoundary:)])
10124 {
10125 [speechSynthesizer stopSpeakingAtBoundary:NSSpeechWordBoundary];
10126 }
10127 else
10128 {
10129 [speechSynthesizer stopSpeaking];
10130 }
10131}
10132
10133
10135{
10136 return [speechSynthesizer isSpeaking];
10137}
10138
10139#elif OOLITE_ESPEAK
10140
10141- (void) startSpeakingString:(NSString *) text
10142{
10143 NSData *utf8 = [text dataUsingEncoding:NSUTF8StringEncoding];
10144
10145 if (utf8 != nil) // we have a valid UTF-8 string
10146 {
10147 const char *stringToSay = [text UTF8String];
10148 espeak_Synth(stringToSay, strlen(stringToSay) + 1 /* inc. NULL */, 0, POS_CHARACTER, 0, espeakCHARS_UTF8 | espeakPHONEMES | espeakENDPAUSE, NULL, NULL);
10149 }
10150}
10151
10152
10153- (void) stopSpeaking
10154{
10155 espeak_Cancel();
10156}
10157
10158
10159- (BOOL) isSpeaking
10160{
10161 return espeak_IsPlaying();
10162}
10163
10164
10165- (NSString *) voiceName:(unsigned int) index
10166{
10167 if (index >= espeak_voice_count)
10168 return @"-";
10169 return [NSString stringWithCString: espeak_voices[index]->name];
10170}
10171
10172
10173- (unsigned int) voiceNumber:(NSString *) name
10174{
10175 if (name == nil)
10176 return UINT_MAX;
10177
10178 const char *const label = [name UTF8String];
10179 if (!label)
10180 return UINT_MAX;
10181
10182 unsigned int index = -1;
10183 while (espeak_voices[++index] && strcmp (espeak_voices[index]->name, label))
10184 /**/;
10185 return (index < espeak_voice_count) ? index : UINT_MAX;
10186}
10187
10188
10189- (unsigned int) nextVoice:(unsigned int) index
10190{
10191 if (++index >= espeak_voice_count)
10192 index = 0;
10193 return index;
10194}
10195
10196
10197- (unsigned int) prevVoice:(unsigned int) index
10198{
10199 if (--index >= espeak_voice_count)
10200 index = espeak_voice_count - 1;
10201 return index;
10202}
10203
10204
10205- (unsigned int) setVoice:(unsigned int) index withGenderM:(BOOL) isMale
10206{
10207 if (index == UINT_MAX)
10208 index = [self voiceNumber:DESC(@"espeak-default-voice")];
10209
10210 if (index < espeak_voice_count)
10211 {
10212 espeak_VOICE voice = { espeak_voices[index]->name, NULL, NULL, isMale ? 1 : 2 };
10213 espeak_SetVoiceByProperties (&voice);
10214 }
10215
10216 return index;
10217}
10218
10219#else
10220
10221- (void) startSpeakingString:(NSString *) text {}
10222
10223- (void) stopSpeaking {}
10224
10225- (BOOL) isSpeaking
10226{
10227 return NO;
10228}
10229#endif
10230
10231
10233{
10234 return _pauseMessage;
10235}
10236
10237
10238- (void) setPauseMessageVisible:(BOOL)value
10239{
10240 _pauseMessage = value;
10241}
10242
10243
10245{
10246 return _permanentMessageLog;
10247}
10248
10249
10250- (void) setPermanentMessageLog:(BOOL)value
10251{
10252 _permanentMessageLog = value;
10253}
10254
10255
10257{
10258 return _autoMessageLogBg;
10259}
10260
10261
10262- (void) setAutoMessageLogBg:(BOOL)value
10263{
10264 _autoMessageLogBg = !!value;
10265}
10266
10267
10269{
10270 return _permanentCommLog;
10271}
10272
10273
10274- (void) setPermanentCommLog:(BOOL)value
10275{
10276 _permanentCommLog = value;
10277}
10278
10279
10280- (void) setAutoCommLog:(BOOL)value
10281{
10282 _autoCommLog = value;
10283}
10284
10285
10287{
10288 return gOOJSPlayerIfStale != nil;
10289}
10290
10291
10292- (void) setBlockJSPlayerShipProps:(BOOL)value
10293{
10294 if (value)
10295 {
10297 }
10298 else
10299 {
10301 }
10302}
10303
10304
10306{
10307 [self resetBeacons];
10308
10309 next_universal_id = 100; // start arbitrarily above zero
10310 memset(entity_for_uid, 0, sizeof entity_for_uid);
10311
10312 [self setMainLightPosition:kZeroVector];
10313
10314 [gui autorelease];
10315 gui = [[GuiDisplayGen alloc] init];
10316 [gui setTextColor:[OOColor colorWithDescription:[[gui userSettings] objectForKey:kGuiDefaultTextColor]]];
10317
10318 // message_gui and comm_log_gui defaults are set up inside [hud resetGuis:] ( via [player deferredInit], called from the code that calls this method).
10319 [message_gui autorelease];
10320 message_gui = [[GuiDisplayGen alloc]
10321 initWithPixelSize:NSMakeSize(480, 160)
10322 columns:1
10323 rows:9
10324 rowHeight:19
10325 rowStart:20
10326 title:nil];
10327
10328 [comm_log_gui autorelease];
10329 comm_log_gui = [[GuiDisplayGen alloc]
10330 initWithPixelSize:NSMakeSize(360, 120)
10331 columns:1
10332 rows:10
10333 rowHeight:12
10334 rowStart:12
10335 title:nil];
10336
10337 //
10338
10339 time_delta = 0.0;
10340#ifndef NDEBUG
10341 [self setTimeAccelerationFactor:TIME_ACCELERATION_FACTOR_DEFAULT];
10342#endif
10343 universal_time = 0.0;
10344 messageRepeatTime = 0.0;
10346
10347#if OOLITE_SPEECH_SYNTH
10348 [speechArray autorelease];
10349 speechArray = [[ResourceManager arrayFromFilesNamed:@"speech_pronunciation_guide.plist" inFolder:@"Config" andMerge:YES] retain];
10350#endif
10351
10352 [commodities autorelease];
10353 commodities = [[OOCommodities alloc] init];
10354
10355
10356 [self loadDescriptions];
10357
10358 [characters autorelease];
10359 characters = [[ResourceManager dictionaryFromFilesNamed:@"characters.plist" inFolder:@"Config" andMerge:YES] retain];
10360
10361 [customSounds autorelease];
10362 customSounds = [[ResourceManager dictionaryFromFilesNamed:@"customsounds.plist" inFolder:@"Config" andMerge:YES] retain];
10363
10364 [globalSettings autorelease];
10365 globalSettings = [[ResourceManager dictionaryFromFilesNamed:@"global-settings.plist" inFolder:@"Config" mergeMode:MERGE_SMART cache:YES] retain];
10366
10367
10368 [systemManager autorelease];
10370
10371 [screenBackgrounds autorelease];
10372 screenBackgrounds = [[ResourceManager dictionaryFromFilesNamed:@"screenbackgrounds.plist" inFolder:@"Config" andMerge:YES] retain];
10373
10374 // role-categories.plist and pirate-victim-roles.plist
10375 [roleCategories autorelease];
10377
10378 [autoAIMap autorelease];
10379 autoAIMap = [[ResourceManager dictionaryFromFilesNamed:@"autoAImap.plist" inFolder:@"Config" andMerge:YES] retain];
10380
10381 [equipmentData autorelease];
10382 [equipmentDataOutfitting autorelease];
10383 NSArray *equipmentTemp = [ResourceManager arrayFromFilesNamed:@"equipment.plist" inFolder:@"Config" andMerge:YES];
10384 equipmentData = [[equipmentTemp sortedArrayUsingFunction:equipmentSort context:NULL] retain];
10385 equipmentDataOutfitting = [[equipmentTemp sortedArrayUsingFunction:equipmentSortOutfitting context:NULL] retain];
10386
10388
10389 [explosionSettings autorelease];
10390 explosionSettings = [[ResourceManager dictionaryFromFilesNamed:@"explosions.plist" inFolder:@"Config" andMerge:YES] retain];
10391
10392}
10393
10394
10396{
10397 NSMutableDictionary *tmp = [[NSMutableDictionary alloc] initWithCapacity:[commodities count]];
10398 OOCommodityType type = nil;
10399 foreach (type, [commodities goods])
10400 {
10401 ShipEntity *container = [self newShipWithRole:@"oolite-template-cargopod"];
10402 [container setScanClass:CLASS_CARGO];
10404 [tmp setObject:container forKey:type];
10405 [container release];
10406 }
10407 [cargoPods release];
10408 cargoPods = [[NSDictionary alloc] initWithDictionary:tmp];
10409 [tmp release];
10410}
10411
10413{
10414#ifndef NDEBUG
10415 NSMutableArray *badEntities = nil;
10416 Entity *entity = nil;
10417
10418 unsigned i;
10419 for (i = 0; i < n_entities; i++)
10420 {
10421 entity = sortedEntities[i];
10422 if ([entity sessionID] != _sessionID)
10423 {
10424 OOLogERR(@"universe.sessionIDs.verify.failed", @"Invalid entity %@ (came from session %llu, current session is %llu).", [entity shortDescription], [entity sessionID], _sessionID);
10425 if (badEntities == nil) badEntities = [NSMutableArray array];
10426 [badEntities addObject:entity];
10427 }
10428 }
10429
10430 foreach (entity, badEntities)
10431 {
10432 [self removeEntity:entity];
10433 }
10434#endif
10435}
10436
10437
10438// FIXME: needs less redundancy?
10439- (BOOL) reinitAndShowDemo:(BOOL) showDemo
10440{
10441 no_update = YES;
10442 PlayerEntity* player = PLAYER;
10443 assert(player != nil);
10444
10445 if (JSResetFlags != 0) // JS reset failed, remember previous settings
10446 {
10447 showDemo = (JSResetFlags & 2) > 0; // binary 10, a.k.a. 1 << 1
10448 }
10449 else
10450 {
10451 JSResetFlags = (showDemo << 1);
10452 }
10453
10456
10457 _sessionID++; // Must be after removing old entities and before adding new ones.
10458
10459 [ResourceManager setUseAddOns:useAddOns]; // also logs the paths
10460 //[ResourceManager loadScripts]; // initialised inside [player setUp]!
10461
10462 // NOTE: Anything in the sharedCache is now trashed and must be
10463 // reloaded. Ideally anything using the sharedCache should
10464 // be aware of cache flushes so it can automatically
10465 // reinitialize itself - mwerle 20081107.
10467 [[self gameController] setGamePaused:NO];
10469 [PLAYER setSpeed:0.0];
10470
10471 [self loadDescriptions];
10472 [self loadScenarios];
10473
10474 [missiontext autorelease];
10475 missiontext = [[ResourceManager dictionaryFromFilesNamed:@"missiontext.plist" inFolder:@"Config" andMerge:YES] retain];
10476
10477
10478 if(showDemo)
10479 {
10480 [demo_ships release];
10482 demo_ship_index = 0;
10484 }
10485
10487
10488 cachedSun = nil;
10489 cachedPlanet = nil;
10491
10492 [self setUpSettings];
10493
10494 // reset these in case OXP set has changed
10495
10496 // set up cargopod templates
10497 [self setUpCargoPods];
10498
10499 if (![player setUpAndConfirmOK:YES])
10500 {
10501 // reinitAndShowDemo rescheduled inside setUpAndConfirmOK...
10502 return NO; // Abort!
10503 }
10504
10505 // we can forget the previous settings now.
10506 JSResetFlags = 0;
10507
10508 [self addEntity:player];
10509 demo_ship = nil;
10510 [[self gameController] setPlayerFileToLoad:nil]; // reset Quicksave
10511
10512 [self setUpInitialUniverse];
10513 autoSaveNow = NO; // don't autosave immediately after restarting a game
10514
10516
10517 if(showDemo)
10518 {
10519 [player setStatus:STATUS_START_GAME];
10520 // re-read keyconfig.plist just in case we've loaded a keyboard
10521 // configuration expansion
10523 }
10524 else
10525 {
10527 }
10528
10530 if(showDemo)
10531 {
10533 }
10534 else
10535 {
10536 // no need to do these if showing the demo as the only way out
10537 // now is to load a game
10538 [self populateNormalSpace];
10539
10541 }
10542
10543 if(!showDemo)
10544 {
10546 [player doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];
10547 }
10548
10550
10551 no_update = NO;
10552 return YES;
10553}
10554
10555
10557{
10558 PlayerEntity* player = PLAYER;
10559
10560 OO_DEBUG_PUSH_PROGRESS(@"%@", @"Wormhole and character reset");
10561 if (activeWormholes) [activeWormholes autorelease];
10562 activeWormholes = [[NSMutableArray arrayWithCapacity:16] retain];
10563 if (characterPool) [characterPool autorelease];
10564 characterPool = [[NSMutableArray arrayWithCapacity:256] retain];
10566
10567 OO_DEBUG_PUSH_PROGRESS(@"%@", @"Galaxy reset");
10569 systemID = [player systemID];
10571
10572 OO_DEBUG_PUSH_PROGRESS(@"%@", @"Player init: setUpShipFromDictionary");
10574 [player baseMass]; // bootstrap the base mass used in all fuel charge calculations.
10576
10577 // Player init above finishes initialising all standard player ship properties. Now that the base mass is set, we can run setUpSpace!
10578 [self setUpSpace];
10579
10581 [[self currentSystemData] oo_boolForKey:@"stations_require_docking_clearance" defaultValue:YES]];
10582
10585 [player setOrientation:kIdentityQuaternion];
10586}
10587
10588
10590{
10591 return SCANNER_MAX_RANGE * ((Ranrot() & 255) / 256.0 - 0.5);
10592}
10593
10594
10595- (Vector) randomPlaceWithinScannerFrom:(Vector)pos alongRoute:(Vector)route withOffset:(double)offset
10596{
10597 pos.x += offset * route.x + [self randomDistanceWithinScanner];
10598 pos.y += offset * route.y + [self randomDistanceWithinScanner];
10599 pos.z += offset * route.z + [self randomDistanceWithinScanner];
10600
10601 return pos;
10602}
10603
10604
10605- (HPVector) fractionalPositionFrom:(HPVector)point0 to:(HPVector)point1 withFraction:(double)routeFraction
10606{
10607 if (routeFraction == NSNotFound) routeFraction = randf();
10608
10609 point1 = OOHPVectorInterpolate(point0, point1, routeFraction);
10610
10611 point1.x += 2 * SCANNER_MAX_RANGE * (randf() - 0.5);
10612 point1.y += 2 * SCANNER_MAX_RANGE * (randf() - 0.5);
10613 point1.z += 2 * SCANNER_MAX_RANGE * (randf() - 0.5);
10614
10615 return point1;
10616}
10617
10618
10619- (BOOL)doRemoveEntity:(Entity *)entity
10620{
10621 // remove reference to entity in linked lists
10622 if ([entity canCollide]) // filter only collidables disappearing
10623 {
10624 doLinkedListMaintenanceThisUpdate = YES;
10625 }
10626
10628
10629 // moved forward ^^
10630 // remove from the reference dictionary
10631 int old_id = [entity universalID];
10632 entity_for_uid[old_id] = nil;
10633 [entity setUniversalID:NO_TARGET];
10635
10636 // maintain sorted lists
10637 int index = entity->zero_index;
10638
10639 int n = 1;
10640 if (index >= 0)
10641 {
10642 if (sortedEntities[index] != entity)
10643 {
10644 OOLog(kOOLogInconsistentState, @"DEBUG: Universe removeEntity:%@ ENTITY IS NOT IN THE RIGHT PLACE IN THE ZERO_DISTANCE SORTED LIST -- FIXING...", entity);
10645 unsigned i;
10646 index = -1;
10647 for (i = 0; (i < n_entities)&&(index == -1); i++)
10648 if (sortedEntities[i] == entity)
10649 index = i;
10650 if (index == -1)
10651 OOLog(kOOLogInconsistentState, @"DEBUG: Universe removeEntity:%@ ENTITY IS NOT IN THE ZERO_DISTANCE SORTED LIST -- CONTINUING...", entity);
10652 }
10653 if (index != -1)
10654 {
10655 while ((unsigned)index < n_entities)
10656 {
10657 while (((unsigned)index + n < n_entities)&&(sortedEntities[index + n] == entity))
10658 {
10659 n++; // ie there's a duplicate entry for this entity
10660 }
10661
10662 /*
10663 BUG: when n_entities == UNIVERSE_MAX_ENTITIES, this read
10664 off the end of the array and copied (Entity *)n_entities =
10665 0x800 into the list. The subsequent update of zero_index
10666 derferenced 0x800 and crashed.
10667 FIX: add an extra unused slot to sortedEntities, which is
10668 always nil.
10669 EFFICIENCY CONCERNS: this could have been an alignment
10670 issue since UNIVERSE_MAX_ENTITIES == 2048, but it isn't
10671 really. sortedEntities is part of the object, not malloced,
10672 it isn't aligned, and the end of it is only live in
10673 degenerate cases.
10674 -- Ahruman 2012-07-11
10675 */
10676 sortedEntities[index] = sortedEntities[index + n]; // copy entity[index + n] -> entity[index] (preserves sort order)
10677 if (sortedEntities[index])
10678 {
10679 sortedEntities[index]->zero_index = index; // give it its correct position
10680 }
10681 index++;
10682 }
10683 if (n > 1)
10684 OOLog(kOOLogInconsistentState, @"DEBUG: Universe removeEntity: REMOVED %d EXTRA COPIES OF %@ FROM THE ZERO_DISTANCE SORTED LIST", n - 1, entity);
10685 while (n--)
10686 {
10687 n_entities--;
10688 sortedEntities[n_entities] = nil;
10689 }
10690 }
10691 entity->zero_index = -1; // it's GONE!
10692 }
10693
10694 // remove from the definitive list
10695 if ([entities containsObject:entity])
10696 {
10697 // FIXME: better approach needed for core break patterns - CIM
10698 if ([entity isBreakPattern] && ![entity isVisualEffect])
10699 {
10700 breakPatternCounter--;
10701 }
10702
10703 if ([entity isShip])
10704 {
10705 ShipEntity *se = (ShipEntity*)entity;
10706 [self clearBeacon:se];
10707 }
10708 if ([entity isWaypoint])
10709 {
10710 OOWaypointEntity *wp = (OOWaypointEntity*)entity;
10711 [self clearBeacon:wp];
10712 }
10713 if ([entity isVisualEffect])
10714 {
10716 [self clearBeacon:ve];
10717 }
10718
10719 if ([entity isWormhole])
10720 {
10721 [activeWormholes removeObject:entity];
10722 }
10723 else if ([entity isPlanet])
10724 {
10725 [allPlanets removeObject:entity];
10726 }
10727
10728 [entities removeObject:entity];
10729 return YES;
10730 }
10731
10732 return NO;
10733}
10734
10735
10736static void PreloadOneSound(NSString *soundName)
10737{
10738 if (![soundName hasPrefix:@"["] && ![soundName hasSuffix:@"]"])
10739 {
10740 [ResourceManager ooSoundNamed:soundName inFolder:@"Sounds"];
10741 }
10742}
10743
10744
10746{
10747 // Preload sounds to avoid loading stutter.
10748 NSString *key = nil;
10750 {
10751 id object = [customSounds objectForKey:key];
10752 if([object isKindOfClass:[NSString class]])
10753 {
10754 PreloadOneSound(object);
10755 }
10756 else if([object isKindOfClass:[NSArray class]] && [object count] > 0)
10757 {
10758 NSString *soundName = nil;
10759 foreach (soundName, object)
10760 {
10761 if ([soundName isKindOfClass:[NSString class]])
10762 {
10763 PreloadOneSound(soundName);
10764 }
10765 }
10766 }
10767 }
10768
10769 // Afterburner sound doesn't go through customsounds.plist.
10770 PreloadOneSound(@"afterburner1.ogg");
10771}
10772
10773
10775{
10776 NSAutoreleasePool *pool = nil;
10777
10778 while ([activeWormholes count])
10779 {
10780 pool = [[NSAutoreleasePool alloc] init];
10781 @try
10782 {
10783 WormholeEntity* whole = [activeWormholes objectAtIndex:0];
10784 // If the wormhole has been scanned by the player then the
10785 // PlayerEntity will take care of it
10786 if (![whole isScanned] &&
10787 NSEqualPoints([PLAYER galaxy_coordinates], [whole destinationCoordinates]) )
10788 {
10789 // this is a wormhole to this system
10791 }
10792 [activeWormholes removeObjectAtIndex:0]; // empty it out
10793 }
10794 @catch (NSException *exception)
10795 {
10796 OOLog(kOOLogException, @"Squashing exception during wormhole unpickling (%@: %@).", [exception name], [exception reason]);
10797 }
10798 [pool release];
10799 }
10800}
10801
10802
10803- (NSString *)chooseStringForKey:(NSString *)key inDictionary:(NSDictionary *)dictionary
10804{
10805 id object = [dictionary objectForKey:key];
10806 if ([object isKindOfClass:[NSString class]]) return object;
10807 else if ([object isKindOfClass:[NSArray class]] && [object count] > 0) return [object oo_stringAtIndex:Ranrot() % [object count]];
10808 return nil;
10809}
10810
10811
10812#if OO_LOCALIZATION_TOOLS
10813
10814#if DEBUG_GRAPHVIZ
10815- (void) dumpDebugGraphViz
10816{
10817 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"universe-dump-debug-graphviz"])
10818 {
10819 [self dumpSystemDescriptionGraphViz];
10820 }
10821}
10822
10823
10824- (void) dumpSystemDescriptionGraphViz
10825{
10826 NSMutableString *graphViz = nil;
10827 NSArray *systemDescriptions = nil;
10828 NSArray *thisDesc = nil;
10829 NSUInteger i, count, j, subCount;
10830 NSString *descLine = nil;
10831 NSArray *curses = nil;
10832 NSString *label = nil;
10833 NSDictionary *keyMap = nil;
10834
10835 keyMap = [ResourceManager dictionaryFromFilesNamed:@"sysdesc_key_table.plist"
10836 inFolder:@"Config"
10837 andMerge:NO];
10838
10839 graphViz = [NSMutableString stringWithString:
10840 @"// System description grammar:\n\n"
10841 "digraph system_descriptions\n"
10842 "{\n"
10843 "\tgraph [charset=\"UTF-8\", label=\"System description grammar\", labelloc=t, labeljust=l rankdir=LR compound=true nodesep=0.02 ranksep=1.5 concentrate=true fontname=Helvetica]\n"
10844 "\tedge [arrowhead=dot]\n"
10845 "\tnode [shape=none height=0.2 width=3 fontname=Helvetica]\n\t\n"];
10846
10847 systemDescriptions = [[self descriptions] oo_arrayForKey:@"system_description"];
10848 count = [systemDescriptions count];
10849
10850 // Add system-description-string as special node (it's the one thing that ties [14] to everything else).
10851 descLine = DESC(@"system-description-string");
10852 label = OOStringifySystemDescriptionLine(descLine, keyMap, NO);
10853 [graphViz appendFormat:@"\tsystem_description_string [label=\"%@\" shape=ellipse]\n", EscapedGraphVizString(label)];
10854 [self addNumericRefsInString:descLine
10855 toGraphViz:graphViz
10856 fromNode:@"system_description_string"
10857 nodeCount:count];
10858 [graphViz appendString:@"\t\n"];
10859
10860 // Add special nodes for formatting codes
10861 [graphViz appendString:
10862 @"\tpercent_I [label=\"%I\\nInhabitants\" shape=diamond]\n"
10863 "\tpercent_H [label=\"%H\\nSystem name\" shape=diamond]\n"
10864 "\tpercent_RN [label=\"%R/%N\\nRandom name\" shape=diamond]\n"
10865 "\tpercent_J [label=\"%J\\nNumbered system name\" shape=diamond]\n"
10866 "\tpercent_G [label=\"%G\\nNumbered system name in chart number\" shape=diamond]\n\t\n"];
10867
10868 // Toss in the Thargoid curses, too
10869 [graphViz appendString:@"\tsubgraph cluster_thargoid_curses\n\t{\n\t\tlabel = \"Thargoid curses\"\n"];
10870 curses = [[self descriptions] oo_arrayForKey:@"thargoid_curses"];
10871 subCount = [curses count];
10872 for (j = 0; j < subCount; ++j)
10873 {
10874 label = OOStringifySystemDescriptionLine([curses oo_stringAtIndex:j], keyMap, NO);
10875 [graphViz appendFormat:@"\t\tthargoid_curse_%llu [label=\"%@\"]\n", j, EscapedGraphVizString(label)];
10876 }
10877 [graphViz appendString:@"\t}\n"];
10878 for (j = 0; j < subCount; ++j)
10879 {
10880 [self addNumericRefsInString:[curses oo_stringAtIndex:j]
10881 toGraphViz:graphViz
10882 fromNode:[NSString stringWithFormat:@"thargoid_curse_%llu", j]
10883 nodeCount:count];
10884 }
10885 [graphViz appendString:@"\t\n"];
10886
10887 // The main show: the bits of systemDescriptions itself.
10888 // Define the nodes
10889 for (i = 0; i < count; ++i)
10890 {
10891 // Build label, using sysdesc_key_table.plist if available
10892 label = [keyMap objectForKey:[NSString stringWithFormat:@"%llu", i]];
10893 if (label == nil) label = [NSString stringWithFormat:@"[%llu]", i];
10894 else label = [NSString stringWithFormat:@"[%llu] (%@)", i, label];
10895
10896 [graphViz appendFormat:@"\tsubgraph cluster_%llu\n\t{\n\t\tlabel=\"%@\"\n", i, EscapedGraphVizString(label)];
10897
10898 thisDesc = [systemDescriptions oo_arrayAtIndex:i];
10899 subCount = [thisDesc count];
10900 for (j = 0; j < subCount; ++j)
10901 {
10902 label = OOStringifySystemDescriptionLine([thisDesc oo_stringAtIndex:j], keyMap, NO);
10903 [graphViz appendFormat:@"\t\tn%llu_%llu [label=\"\\\"%@\\\"\"]\n", i, j, EscapedGraphVizString(label)];
10904 }
10905
10906 [graphViz appendString:@"\t}\n"];
10907 }
10908 [graphViz appendString:@"\t\n"];
10909
10910 // Define the edges
10911 for (i = 0; i != count; ++i)
10912 {
10913 thisDesc = [systemDescriptions oo_arrayAtIndex:i];
10914 subCount = [thisDesc count];
10915 for (j = 0; j != subCount; ++j)
10916 {
10917 descLine = [thisDesc oo_stringAtIndex:j];
10918 [self addNumericRefsInString:descLine
10919 toGraphViz:graphViz
10920 fromNode:[NSString stringWithFormat:@"n%llu_%llu", i, j]
10921 nodeCount:count];
10922 }
10923 }
10924
10925 // Write file
10926 [graphViz appendString:@"\t}\n"];
10927 [ResourceManager writeDiagnosticData:[graphViz dataUsingEncoding:NSUTF8StringEncoding] toFileNamed:@"SystemDescription.dot"];
10928}
10929#endif // DEBUG_GRAPHVIZ
10930
10931
10932- (void) addNumericRefsInString:(NSString *)string toGraphViz:(NSMutableString *)graphViz fromNode:(NSString *)fromNode nodeCount:(NSUInteger)nodeCount
10933{
10934 NSString *index = nil;
10935 NSInteger start, end;
10936 NSRange remaining, subRange;
10937 unsigned i;
10938
10939 remaining = NSMakeRange(0, [string length]);
10940
10941 for (;;)
10942 {
10943 subRange = [string rangeOfString:@"[" options:NSLiteralSearch range:remaining];
10944 if (subRange.location == NSNotFound) break;
10945 start = subRange.location + subRange.length;
10946 remaining.length -= start - remaining.location;
10947 remaining.location = start;
10948
10949 subRange = [string rangeOfString:@"]" options:NSLiteralSearch range:remaining];
10950 if (subRange.location == NSNotFound) break;
10951 end = subRange.location;
10952 remaining.length -= end - remaining.location;
10953 remaining.location = end;
10954
10955 index = [string substringWithRange:NSMakeRange(start, end - start)];
10956 i = [index intValue];
10957
10958 // Each node gets a colour for its incoming edges. The multiplication and mod shuffle them to avoid adjacent nodes having similar colours.
10959 [graphViz appendFormat:@"\t%@ -> n%u_0 [color=\"%f,0.75,0.8\" lhead=cluster_%u]\n", fromNode, i, ((float)(i * 511 % nodeCount)) / ((float)nodeCount), i];
10960 }
10961
10962 if ([string rangeOfString:@"%I"].location != NSNotFound)
10963 {
10964 [graphViz appendFormat:@"\t%@ -> percent_I [color=\"0,0,0.25\"]\n", fromNode];
10965 }
10966 if ([string rangeOfString:@"%H"].location != NSNotFound)
10967 {
10968 [graphViz appendFormat:@"\t%@ -> percent_H [color=\"0,0,0.45\"]\n", fromNode];
10969 }
10970 if ([string rangeOfString:@"%R"].location != NSNotFound || [string rangeOfString:@"%N"].location != NSNotFound)
10971 {
10972 [graphViz appendFormat:@"\t%@ -> percent_RN [color=\"0,0,0.65\"]\n", fromNode];
10973 }
10974
10975 // TODO: test graphViz output for @"%Jxxx" and @"%Gxxxxxx"
10976 if ([string rangeOfString:@"%J"].location != NSNotFound)
10977 {
10978 [graphViz appendFormat:@"\t%@ -> percent_J [color=\"0,0,0.75\"]\n", fromNode];
10979 }
10980
10981 if ([string rangeOfString:@"%G"].location != NSNotFound)
10982 {
10983 [graphViz appendFormat:@"\t%@ -> percent_G [color=\"0,0,0.85\"]\n", fromNode];
10984 }
10985}
10986
10988{
10989 // Handle command line options to transform system_description array for easier localization
10990
10991 NSArray *arguments = nil;
10992 NSEnumerator *argEnum = nil;
10993 NSString *arg = nil;
10994 BOOL compileSysDesc = NO, exportSysDesc = NO, xml = NO;
10995
10996 arguments = [[NSProcessInfo processInfo] arguments];
10997
10998 for (argEnum = [arguments objectEnumerator]; (arg = [argEnum nextObject]); )
10999 {
11000 if ([arg isEqual:@"--compile-sysdesc"]) compileSysDesc = YES;
11001 else if ([arg isEqual:@"--export-sysdesc"]) exportSysDesc = YES;
11002 else if ([arg isEqual:@"--xml"]) xml = YES;
11003 else if ([arg isEqual:@"--openstep"]) xml = NO;
11004 }
11005
11006 if (compileSysDesc) CompileSystemDescriptions(xml);
11007 if (exportSysDesc) ExportSystemDescriptions(xml);
11008}
11009#endif
11010
11011
11012#if NEW_PLANETS
11013// See notes at preloadPlanetTexturesForSystem:.
11014- (void) prunePreloadingPlanetMaterials
11015{
11017
11018 NSUInteger i = [_preloadingPlanetMaterials count];
11019 while (i--)
11020 {
11021 if ([[_preloadingPlanetMaterials objectAtIndex:i] isFinishedLoading])
11022 {
11023 [_preloadingPlanetMaterials removeObjectAtIndex:i];
11024 }
11025 }
11026}
11027#endif
11028
11029
11030
11032{
11033 [conditionScripts autorelease];
11034 conditionScripts = [[NSMutableDictionary alloc] init];
11035 // get list of names from cache manager
11036 [self addConditionScripts:[[[OOCacheManager sharedCache] objectForKey:@"equipment conditions" inCache:@"condition scripts"] objectEnumerator]];
11037
11038 [self addConditionScripts:[[[OOCacheManager sharedCache] objectForKey:@"ship conditions" inCache:@"condition scripts"] objectEnumerator]];
11039
11040 [self addConditionScripts:[[[OOCacheManager sharedCache] objectForKey:@"demoship conditions" inCache:@"condition scripts"] objectEnumerator]];
11041}
11042
11043
11044- (void) addConditionScripts:(NSEnumerator *)scripts
11045{
11046 NSString *scriptname = nil;
11047 while ((scriptname = [scripts nextObject]))
11048 {
11049 if ([conditionScripts objectForKey:scriptname] == nil)
11050 {
11051 OOJSScript *script = [OOScript jsScriptFromFileNamed:scriptname properties:nil];
11052 if (script != nil)
11053 {
11054 [conditionScripts setObject:script forKey:scriptname];
11055 }
11056 }
11057 }
11058}
11059
11060
11061- (OOJSScript*) getConditionScript:(NSString *)scriptname
11062{
11063 return [conditionScripts objectForKey:scriptname];
11064}
11065
11066@end
11067
11068
11069@implementation OOSound (OOCustomSounds)
11070
11071+ (id) soundWithCustomSoundKey:(NSString *)key
11072{
11073 NSString *fileName = [UNIVERSE soundNameForCustomSoundKey:key];
11074 if (fileName == nil) return nil;
11075 return [ResourceManager ooSoundNamed:fileName inFolder:@"Sounds"];
11076}
11077
11078
11079- (id) initWithCustomSoundKey:(NSString *)key
11080{
11081 [self release];
11082 return [[OOSound soundWithCustomSoundKey:key] retain];
11083}
11084
11085@end
11086
11087
11088@implementation OOSoundSource (OOCustomSounds)
11089
11090+ (id) sourceWithCustomSoundKey:(NSString *)key
11091{
11092 return [[[self alloc] initWithCustomSoundKey:key] autorelease];
11093}
11094
11095
11096- (id) initWithCustomSoundKey:(NSString *)key
11097{
11098 OOSound *theSound = [OOSound soundWithCustomSoundKey:key];
11099 if (theSound != nil)
11100 {
11101 self = [self initWithSound:theSound];
11102 }
11103 else
11104 {
11105 [self release];
11106 self = nil;
11107 }
11108 return self;
11109}
11110
11111
11112- (void) playCustomSoundWithKey:(NSString *)key
11113{
11114 OOSound *theSound = [OOSound soundWithCustomSoundKey:key];
11115 if (theSound != nil) [self playSound:theSound];
11116}
11117
11118@end
11119
11120NSComparisonResult populatorPrioritySort(id a, id b, void *context)
11121{
11122 NSDictionary *one = (NSDictionary *)a;
11123 NSDictionary *two = (NSDictionary *)b;
11124 int pri_one = [one oo_intForKey:@"priority" defaultValue:100];
11125 int pri_two = [two oo_intForKey:@"priority" defaultValue:100];
11126 if (pri_one < pri_two) return NSOrderedAscending;
11127 if (pri_one > pri_two) return NSOrderedDescending;
11128 return NSOrderedSame;
11129}
11130
11131
11132NSComparisonResult equipmentSort(id a, id b, void *context)
11133{
11134 NSArray *one = (NSArray *)a;
11135 NSArray *two = (NSArray *)b;
11136
11137 /* Sort by explicit sort_order, then tech level, then price */
11138
11139 OOCreditsQuantity comp1 = [[one oo_dictionaryAtIndex:EQUIPMENT_EXTRA_INFO_INDEX] oo_unsignedLongLongForKey:@"sort_order" defaultValue:1000];
11140 OOCreditsQuantity comp2 = [[two oo_dictionaryAtIndex:EQUIPMENT_EXTRA_INFO_INDEX] oo_unsignedLongLongForKey:@"sort_order" defaultValue:1000];
11141 if (comp1 < comp2) return NSOrderedAscending;
11142 if (comp1 > comp2) return NSOrderedDescending;
11143
11144 comp1 = [one oo_unsignedLongLongAtIndex:EQUIPMENT_TECH_LEVEL_INDEX];
11145 comp2 = [two oo_unsignedLongLongAtIndex:EQUIPMENT_TECH_LEVEL_INDEX];
11146 if (comp1 < comp2) return NSOrderedAscending;
11147 if (comp1 > comp2) return NSOrderedDescending;
11148
11149 comp1 = [one oo_unsignedLongLongAtIndex:EQUIPMENT_PRICE_INDEX];
11150 comp2 = [two oo_unsignedLongLongAtIndex:EQUIPMENT_PRICE_INDEX];
11151 if (comp1 < comp2) return NSOrderedAscending;
11152 if (comp1 > comp2) return NSOrderedDescending;
11153
11154 return NSOrderedSame;
11155}
11156
11157
11158NSComparisonResult equipmentSortOutfitting(id a, id b, void *context)
11159{
11160 NSArray *one = (NSArray *)a;
11161 NSArray *two = (NSArray *)b;
11162
11163 /* Sort by explicit sort_order, then tech level, then price */
11164
11165 OOCreditsQuantity comp1 = [[one oo_dictionaryAtIndex:EQUIPMENT_EXTRA_INFO_INDEX] oo_unsignedLongLongForKey:@"purchase_sort_order" defaultValue:[[one oo_dictionaryAtIndex:EQUIPMENT_EXTRA_INFO_INDEX] oo_unsignedLongLongForKey:@"sort_order" defaultValue:1000]];
11166 OOCreditsQuantity comp2 = [[two oo_dictionaryAtIndex:EQUIPMENT_EXTRA_INFO_INDEX] oo_unsignedLongLongForKey:@"purchase_sort_order" defaultValue:[[two oo_dictionaryAtIndex:EQUIPMENT_EXTRA_INFO_INDEX] oo_unsignedLongLongForKey:@"sort_order" defaultValue:1000]];
11167 if (comp1 < comp2) return NSOrderedAscending;
11168 if (comp1 > comp2) return NSOrderedDescending;
11169
11170 comp1 = [one oo_unsignedLongLongAtIndex:EQUIPMENT_TECH_LEVEL_INDEX];
11171 comp2 = [two oo_unsignedLongLongAtIndex:EQUIPMENT_TECH_LEVEL_INDEX];
11172 if (comp1 < comp2) return NSOrderedAscending;
11173 if (comp1 > comp2) return NSOrderedDescending;
11174
11175 comp1 = [one oo_unsignedLongLongAtIndex:EQUIPMENT_PRICE_INDEX];
11176 comp2 = [two oo_unsignedLongLongAtIndex:EQUIPMENT_PRICE_INDEX];
11177 if (comp1 < comp2) return NSOrderedAscending;
11178 if (comp1 > comp2) return NSOrderedDescending;
11179
11180 return NSOrderedSame;
11181}
11182
11183
11184NSString *OOLookUpDescriptionPRIV(NSString *key)
11185{
11186 NSString *result = [UNIVERSE descriptionForKey:key];
11187 if (result == nil) result = key;
11188 return result;
11189}
11190
11191
11192// There's a hint of gettext about this...
11193NSString *OOLookUpPluralDescriptionPRIV(NSString *key, NSInteger count)
11194{
11195 NSArray *conditions = [[UNIVERSE descriptions] oo_arrayForKey:@"plural-rules"];
11196
11197 // are we using an older descriptions.plist (1.72.x) ?
11198 NSString *tmp = [UNIVERSE descriptionForKey:key];
11199 if (tmp != nil)
11200 {
11201 static NSMutableSet *warned = nil;
11202
11203 if (![warned containsObject:tmp])
11204 {
11205 OOLogWARN(@"localization.plurals", @"'%@' found in descriptions.plist, should be '%@%%0'. Localization data needs updating.",key,key);
11206 if (warned == nil) warned = [[NSMutableSet alloc] init];
11207 [warned addObject:tmp];
11208 }
11209 }
11210
11211 if (conditions == nil)
11212 {
11213 if (tmp == nil) // this should mean that descriptions.plist is from 1.73 or above.
11214 return OOLookUpDescriptionPRIV([NSString stringWithFormat:@"%@%%%d", key, count != 1]);
11215 // still using an older descriptions.plist
11216 return tmp;
11217 }
11218 int unsigned i;
11219 long int index;
11220
11221 for (index = i = 0; i < [conditions count]; ++index, ++i)
11222 {
11223 const char *cond = [[conditions oo_stringAtIndex:i] UTF8String];
11224 if (!cond)
11225 break;
11226
11227 long int input = count;
11228 BOOL flag = NO; // we XOR test results with this
11229
11230 while (isspace (*cond))
11231 ++cond;
11232
11233 for (;;)
11234 {
11235 while (isspace (*cond))
11236 ++cond;
11237
11238 char command = *cond++;
11239
11240 switch (command)
11241 {
11242 case 0:
11243 goto passed; // end of string
11244
11245 case '~':
11246 flag = !flag;
11247 continue;
11248 }
11249
11250 long int param = strtol(cond, (char **)&cond, 10);
11251
11252 switch (command)
11253 {
11254 case '#':
11255 index = param;
11256 continue;
11257
11258 case '%':
11259 if (param < 2)
11260 break; // ouch - fail this!
11261 input %= param;
11262 continue;
11263
11264 case '=':
11265 if (flag ^ (input == param))
11266 continue;
11267 break;
11268 case '!':
11269 if (flag ^ (input != param))
11270 continue;
11271 break;
11272
11273 case '<':
11274 if (flag ^ (input < param))
11275 continue;
11276 break;
11277 case '>':
11278 if (flag ^ (input > param))
11279 continue;
11280 break;
11281 }
11282 // if we arrive here, we have an unknown test or a test has failed
11283 break;
11284 }
11285 }
11286
11287passed:
11288 return OOLookUpDescriptionPRIV([NSString stringWithFormat:@"%@%%%ld", key, index]);
11289}
#define MAX_CLEAR_DEPTH
#define INTERMEDIATE_CLEAR_DEPTH
#define MIN_FOV_DEG
OOEntityStatus
Definition Entity.h:60
OOScanClass
Definition Entity.h:71
#define SCANNER_MAX_RANGE
Definition Entity.h:51
#define SCANNER_MAX_RANGE2
Definition Entity.h:52
static NSString *const kOOLogEntityVerificationError
Definition Entity.m:55
#define OO_DEBUG_POP_PROGRESS()
#define OO_DEBUG_PROGRESS(...)
#define OO_DEBUG_PUSH_PROGRESS(...)
OOGUITabStop OOGUITabSettings[GUI_MAX_COLUMNS]
#define MAIN_GUI_PIXEL_WIDTH
#define MAIN_GUI_PIXEL_HEIGHT
NSInteger OOGUIRow
NSRect OORectFromString(NSString *text, GLfloat x, GLfloat y, NSSize siz)
void OODrawString(NSString *text, GLfloat x, GLfloat y, GLfloat z, NSSize siz)
#define BREAK_PATTERN_RING_SPEED
#define BREAK_PATTERN_RING_SPACING
@ kOOBreakPatternMaxSides
void OOCPUInfoInit(void)
Definition OOCPUInfo.m:64
NSInteger OOComparisonResult
Definition OOCocoa.h:327
#define DESTROY(x)
Definition OOCocoa.h:85
#define foreachkey(VAR, DICT)
Definition OOCocoa.h:378
double OODoubleFromObject(id object, double defaultValue)
OOINLINE jsval OOJSValueFromViewID(JSContext *context, OOViewID value)
NSString * OOStringFromGraphicsDetail(OOGraphicsDetail detail)
void CompileSystemDescriptions(BOOL asXML)
void ExportSystemDescriptions(BOOL asXML)
NSString * OOStringifySystemDescriptionLine(NSString *line, NSDictionary *indicesToKeys, BOOL useFallback)
void OOStandardsDeprecated(NSString *message)
BOOL OOEnforceStandards(void)
void OOInitDebugSupport(void)
BOOL IsShipPredicate(Entity *entity, void *parameter)
BOOL IsVisualEffectPredicate(Entity *entity, void *parameter)
BOOL YESPredicate(Entity *entity, void *parameter)
#define EXPECT_NOT(x)
#define OOINLINE
#define EXPECT(x)
HPVector OOHPVectorRandomRadial(OOHPScalar maxLength)
Definition OOHPVector.m:98
HPVector OOProjectHPVectorToPlane(HPVector point, HPVector plane, HPVector normal)
Definition OOHPVector.m:141
HPVector OORandomPositionInShell(HPVector centre, OOHPScalar inner, OOHPScalar outer)
Definition OOHPVector.m:130
const HPVector kZeroHPVector
Definition OOHPVector.m:28
HPVector OOHPVectorRandomSpatial(OOHPScalar maxLength)
Definition OOHPVector.m:82
const HPVector kBasisZHPVector
Definition OOHPVector.m:31
HPVector OORandomPositionInCylinder(HPVector centre1, OOHPScalar exclusion1, HPVector centre2, OOHPScalar exclusion2, OOHPScalar radius)
Definition OOHPVector.m:113
#define OOJS_PROFILE_EXIT
#define OOJS_PROFILE_ENTER
void OOJSPauseTimeLimiter(void)
OOINLINE jsval OOJSValueFromNativeObject(JSContext *context, id object)
OOINLINE JSContext * OOJSAcquireContext(void)
OOINLINE void OOJSRelinquishContext(JSContext *context)
void OOJSResumeTimeLimiter(void)
#define OOLogWARN(class, format,...)
Definition OOLogging.h:113
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
NSString *const kOOLogException
Definition OOLogging.m:651
NSString *const kOOLogInconsistentState
Definition OOLogging.m:650
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
Definition OOLogging.m:144
#define OOLogOutdentIf(class)
Definition OOLogging.h:102
void OOLogOutdent(void)
Definition OOLogging.m:376
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define OOExtraLog
Definition OOLogging.h:152
NSString *const kOOLogParameterError
Definition OOLogging.m:647
#define OOLogIndentIf(class)
Definition OOLogging.h:101
void OOLogIndent(void)
Definition OOLogging.m:366
#define ABS(A)
Definition OOMaths.h:117
double OOHPScalar
Definition OOMaths.h:69
#define M_SQRT1_2
Definition OOMaths.h:94
#define MAX(A, B)
Definition OOMaths.h:114
#define MIN(A, B)
Definition OOMaths.h:111
GLfloat OOScalar
Definition OOMaths.h:64
#define M_PI
Definition OOMaths.h:73
OOMatrix OOMatrixMultiply(OOMatrix a, OOMatrix b)
Definition OOMatrix.m:111
const OOMatrix kIdentityMatrix
Definition OOMatrix.m:31
Vector OOVectorMultiplyMatrix(Vector v, OOMatrix m)
Definition OOMatrix.m:129
@ MOUSE_MODE_UI_SCREEN_WITH_INTERACTION
@ kOOMusicOn
@ kOOMusicITunes
@ kOOMusicOff
void OOGLLoadModelView(OOMatrix matrix)
void OOGLLookAt(Vector eye, Vector center, Vector up)
OOMatrix OOGLGetModelView(void)
void OOGLPushModelView(void)
void OOGLResetModelView(void)
void OOGLTranslateModelView(Vector vector)
void OOGLFrustum(double left, double right, double bottom, double top, double near, double far)
void OOGLMultModelView(OOMatrix matrix)
OOMatrix OOGLGetModelViewProjection(void)
OOMatrix OOGLPopModelView(void)
void OOGLResetProjection(void)
OOShaderSetting
Definition OOOpenGL.h:35
@ OPENGL_STATE_OVERLAY
Definition OOOpenGL.h:126
@ OPENGL_STATE_OPAQUE
Definition OOOpenGL.h:123
#define OOVerifyOpenGLState()
Definition OOOpenGL.h:136
BOOL OOCheckOpenGLErrors(NSString *format,...)
Definition OOOpenGL.m:39
void GLScaledLineWidth(GLfloat width)
Definition OOOpenGL.m:218
#define OOSetOpenGLState(STATE)
Definition OOOpenGL.h:135
#define OOGL(statement)
Definition OOOpenGL.h:251
unsigned count
return nil
Vector vector_forward_from_quaternion(Quaternion quat)
void basis_vectors_from_quaternion(Quaternion quat, Vector *outRight, Vector *outUp, Vector *outForward)
void quaternion_set_random(Quaternion *quat)
const Quaternion kIdentityQuaternion
Quaternion quaternion_rotation_between(Vector v0, Vector v1)
void quaternion_rotate_about_y(Quaternion *quat, OOScalar angle)
void quaternion_rotate_about_axis(Quaternion *quat, Vector axis, OOScalar angle)
NSString * OOShipLibraryCategorySingular(NSString *category)
NSString * OOShipLibraryWitchspace(ShipEntity *demo_ship)
NSString * OOShipLibraryTurrets(ShipEntity *demo_ship)
NSString * OOShipLibraryShields(ShipEntity *demo_ship)
NSString * OOShipLibraryCargo(ShipEntity *demo_ship)
NSString * OOShipLibraryCategoryPlural(NSString *category)
static NSString *const kOODemoShipClass
NSString * OOShipLibraryGenerator(ShipEntity *demo_ship)
static NSString *const kOODemoShipShipData
NSString * OOShipLibrarySize(ShipEntity *demo_ship)
NSString * OOShipLibrarySpeed(ShipEntity *demo_ship)
static NSString *const kOODemoShipKey
NSString * OOShipLibraryWeapons(ShipEntity *demo_ship)
NSString * OOShipLibraryTurnRate(ShipEntity *demo_ship)
#define OOExpandKey(key,...)
#define OOExpand(string,...)
NSMutableArray * ScanTokensFromString(NSString *values)
@ OO_SYSTEMCONCEALMENT_NOTHING
@ OO_SYSTEMCONCEALMENT_NONAME
#define GL_CLAMP_TO_EDGE
@ MIN_ENTITY_UID
Definition OOTypes.h:195
@ MAX_ENTITY_UID
Definition OOTypes.h:196
@ UNIVERSE_MAX_ENTITIES
Definition OOTypes.h:193
@ NO_TARGET
Definition OOTypes.h:194
@ kOOMinimumSystemID
Definition OOTypes.h:218
uint8_t OOWeaponFacingSet
Definition OOTypes.h:237
NSString * OOCommodityType
Definition OOTypes.h:106
OORouteType
Definition OOTypes.h:33
@ OPTIMIZED_BY_TIME
Definition OOTypes.h:36
OOGraphicsDetail
Definition OOTypes.h:243
@ DETAIL_LEVEL_EXTRAS
Definition OOTypes.h:247
@ DETAIL_LEVEL_SHADERS
Definition OOTypes.h:246
@ DETAIL_LEVEL_MAXIMUM
Definition OOTypes.h:251
@ DETAIL_LEVEL_MINIMUM
Definition OOTypes.h:244
OOViewID
Definition OOTypes.h:43
uint64_t OOCreditsQuantity
Definition OOTypes.h:182
uint16_t OOUniversalID
Definition OOTypes.h:189
#define VALID_WEAPON_FACINGS
Definition OOTypes.h:239
NSUInteger OOTechLevelID
Definition OOTypes.h:204
int16_t OOSystemID
Definition OOTypes.h:211
@ CARGO_SCRIPTED_ITEM
Definition OOTypes.h:76
uint8_t OOGalaxyID
Definition OOTypes.h:210
uint32_t OOCargoQuantity
Definition OOTypes.h:176
OOMassUnit
Definition OOTypes.h:123
@ UNITS_TONS
Definition OOTypes.h:124
@ UNITS_GRAMS
Definition OOTypes.h:126
@ UNITS_KILOGRAMS
Definition OOTypes.h:125
double OOTimeDelta
Definition OOTypes.h:224
uint8_t OOGovernmentID
Definition OOTypes.h:206
double OOTimeAbsolute
Definition OOTypes.h:223
OOWeaponFacing
Definition OOTypes.h:228
@ WEAPON_FACING_FORWARD
Definition OOTypes.h:229
@ WEAPON_FACING_NONE
Definition OOTypes.h:234
@ WEAPON_FACING_AFT
Definition OOTypes.h:230
@ WEAPON_FACING_PORT
Definition OOTypes.h:231
@ WEAPON_FACING_STARBOARD
Definition OOTypes.h:232
uint8_t OOEconomyID
Definition OOTypes.h:207
const Vector kZeroVector
Definition OOVector.m:28
const Vector kBasisYVector
Definition OOVector.m:30
const Vector kBasisZVector
Definition OOVector.m:31
const Vector kBasisXVector
Definition OOVector.m:29
@ OOSPEECHSETTINGS_ALL
@ OOSPEECHSETTINGS_OFF
@ OOSPEECHSETTINGS_COMMS
#define PLAYER
OOEquipmentType * OOWeaponType
Definition ShipEntity.h:168
BOOL isWeaponNone(OOWeaponType weapon)
#define MAX_JUMP_RANGE
Definition ShipEntity.h:107
#define ENTITY_PERSONALITY_MAX
Definition ShipEntity.h:110
NSString * OOEquipmentIdentifierFromWeaponType(OOWeaponType weapon) CONST_FUNC
OOWeaponType OOWeaponTypeFromEquipmentIdentifierSloppy(NSString *string) PURE_FUNC
#define ShipScriptEvent(context, ship, event,...)
#define SUN_SKIM_RADIUS_FACTOR
Definition Universe.h:117
@ OO_POSTFX_ENDOFLIST
Definition Universe.h:103
@ OO_POSTFX_COLORBLINDNESS_TRITAN
Definition Universe.h:97
@ OO_POSTFX_NONE
Definition Universe.h:94
@ OO_POSTFX_CRTBADSIGNAL
Definition Universe.h:102
@ OO_POSTFX_CLOAK
Definition Universe.h:98
#define TIME_ACCELERATION_FACTOR_DEFAULT
Definition Universe.h:170
#define UNIVERSE
Definition Universe.h:844
#define PASSENGER_BERTH_SPACE
Definition Universe.h:156
#define BILLBOARD_DEPTH
Definition Universe.h:167
#define KEY_CHANCE
Definition Universe.h:137
#define MIN_DISTANCE_TO_BUOY
Definition Universe.h:175
#define DESC(key)
Definition Universe.h:850
#define DEMO_LIGHT_POSITION
Definition Universe.h:173
#define DESC_PLURAL(key, count)
Definition Universe.h:851
#define OOLITE_EXCEPTION_FATAL
Definition Universe.h:163
#define TIME_ACCELERATION_FACTOR_MAX
Definition Universe.h:171
NSString * OOLookUpDescriptionPRIV(NSString *key)
Definition Universe.m:11184
#define SAFE_ADDITION_FACTOR2
Definition Universe.h:115
NSString * OOLookUpPluralDescriptionPRIV(NSString *key, NSInteger count)
Definition Universe.m:11193
NSComparisonResult populatorPrioritySort(id a, id b, void *context)
Definition Universe.m:11120
NSComparisonResult equipmentSortOutfitting(id a, id b, void *context)
Definition Universe.m:11158
#define KEY_TECHLEVEL
Definition Universe.h:120
NSComparisonResult equipmentSort(id a, id b, void *context)
Definition Universe.m:11132
#define KEY_RADIUS
Definition Universe.h:128
#define KEY_NAME
Definition Universe.h:129
#define SYSTEM_REPOPULATION_INTERVAL
Definition Universe.h:180
#define OOLITE_EXCEPTION_DATA_NOT_FOUND
Definition Universe.h:162
BOOL(* EntityFilterPredicate)(Entity *entity, void *parameter)
Definition Universe.h:56
#define DOCKED_ILLUM_LEVEL
Definition Universe.m:267
const GLfloat framebufferQuadVertices[]
Definition Universe.m:125
static const OOMatrix fwd_matrix
Definition Universe.m:4629
#define SKY_AMBIENT_ADJUSTMENT
Definition Universe.m:276
static GLfloat docked_light_specular[4]
Definition Universe.m:270
static NSString *const kOOLogUniversePopulateWitchspace
Definition Universe.m:119
static const OOMatrix port_matrix
Definition Universe.m:4643
Universe * gSharedUniverse
Definition Universe.m:138
static const OOMatrix starboard_matrix
Definition Universe.m:4650
#define SUN_AMBIENT_INFLUENCE
Definition Universe.m:274
static int JSResetFlags
Definition Universe.m:257
static BOOL MaintainLinkedLists(Universe *uni)
static OOComparisonResult compareName(id dict1, id dict2, void *context)
static BOOL demo_light_on
Definition Universe.m:262
static const OOMatrix aft_matrix
Definition Universe.m:4636
static NSString *const kOOLogEntityVerificationRebuild
Definition Universe.m:121
static BOOL object_light_on
Definition Universe.m:261
Entity * gOOJSPlayerIfStale
Definition Universe.m:141
static GLfloat docked_light_ambient[4]
Definition Universe.m:268
#define DEMO2_FLY_IN_STAGE_TIME
Definition Universe.m:110
static GLfloat sun_off[4]
Definition Universe.m:263
#define LANE_WIDTH
Definition Universe.m:116
@ DEMO_FLY_IN
Definition Universe.m:104
@ DEMO_FLY_OUT
Definition Universe.m:106
@ DEMO_SHOW_THING
Definition Universe.m:105
static NSString *const kOOLogUniversePopulateError
Definition Universe.m:118
#define DEMO2_VANISHING_DISTANCE
Definition Universe.m:109
const GLuint framebufferQuadIndices[]
Definition Universe.m:132
static GLfloat docked_light_diffuse[4]
Definition Universe.m:269
static GLfloat demo_light_position[4]
Definition Universe.m:264
#define DOCKED_AMBIENT_LEVEL
Definition Universe.m:266
OOINLINE BOOL EntityInRange(HPVector p1, Entity *e2, float range)
static OOComparisonResult comparePrice(id dict1, id dict2, void *context)
NSDictionary * demoShipData()
Definition Universe.m:3307
void drawTargetTextureIntoDefaultFramebuffer()
Definition Universe.m:591
void setLibraryTextForDemoShip()
Definition Universe.m:3313
void prepareToRenderIntoDefaultFramebuffer()
Definition Universe.m:5284
float randomDistanceWithinScanner()
Definition Universe.m:10589
void populateSpaceFromActiveWormholes()
Definition Universe.m:10774
void setGuiToIntroFirstGo:(BOOL justCobra)
Definition AI.h:38
void setNextThinkTime:(OOTimeAbsolute ntt)
Definition AI.m:658
void setOwner:(ShipEntity *ship)
Definition AI.m:197
OOTimeDelta thinkTimeInterval
Definition AI.m:679
void setState:(NSString *stateName)
Definition AI.m:334
OOTimeAbsolute nextThinkTime
Definition AI.m:664
void think()
Definition AI.m:575
void reactToMessage:context:(NSString *message,[context] NSString *debugContext)
Definition AI.m:404
NSString * collisionDescription()
BOOL checkEntity:(Entity *ent)
void setDustColor:(OOColor *color)
Definition DustEntity.m:136
unsigned isImmuneToBreakPatternHide
Definition Entity.m:1139
void setAtmosphereFogging:(OOColor *fogging)
Definition Entity.m:1075
void removeFromLinkedLists()
Definition Entity.m:286
GLfloat collision_radius
Definition Entity.h:111
void drawImmediate:translucent:(bool immediate,[translucent] bool translucent)
Definition Entity.m:984
void setUniversalID:(OOUniversalID uid)
Definition Entity.m:546
OOUniversalID universalID
Definition Entity.m:552
Entity * z_next
Definition Entity.h:122
void setThrowSparks:(BOOL value)
Definition Entity.m:564
void setVelocity:(Vector vel)
Definition Entity.m:757
Entity * z_previous
Definition Entity.h:122
Quaternion orientation
Definition Entity.m:732
void updateCameraRelativePosition()
Definition Entity.m:663
int zero_index
Definition Entity.h:117
void setOrientation:(Quaternion quat)
Definition Entity.m:725
void update:(OOTimeDelta delta_t)
Definition Entity.m:929
GLfloat zero_distance
Definition Entity.h:108
unsigned collisionTestFilter
Definition Entity.h:100
GLfloat collisionRadius()
Definition Entity.m:905
Entity * x_previous
Definition Entity.h:120
void wasRemovedFromUniverse()
Definition Entity.m:520
void setScanClass:(OOScanClass sClass)
Definition Entity.m:799
Vector cameraRelativePosition
Definition Entity.m:617
OOEntityStatus status()
Definition Entity.m:793
void setPositionX:y:z:(OOHPScalar x,[y] OOHPScalar y,[z] OOHPScalar z)
Definition Entity.m:654
void updateLinkedLists()
Definition Entity.m:413
BoundingBox boundingBox
Definition Entity.m:713
unsigned isStation
Definition Entity.m:143
unsigned isPlayer
Definition Entity.m:155
HPVector position
Definition Entity.m:612
BOOL canCollide()
Definition Entity.m:899
HPVector absolutePositionForSubentityOffset:(HPVector offset)
Definition Entity.m:675
void setEnergy:(GLfloat amount)
Definition Entity.m:811
Entity * x_next
Definition Entity.h:120
Quaternion normalOrientation()
Definition Entity.m:738
BOOL isStellarObject()
Definition Entity.m:179
ShipEntity * parentEntity()
Definition Entity.m:589
unsigned isExplicitlyNotMainStation
Definition Entity.h:103
void addToLinkedLists()
Definition Entity.m:228
Entity * collision_chain
Definition Entity.h:124
void setStatus:(OOEntityStatus stat)
Definition Entity.m:787
Entity * y_next
Definition Entity.h:121
Entity * y_previous
Definition Entity.h:121
GLfloat mass
Definition Entity.m:719
void setPosition:(HPVector posn)
Definition Entity.m:647
void setMouseInteractionModeForUIWithMouseInteraction:(BOOL interaction)
GameController * sharedController()
void setGamePaused:(BOOL value)
void logProgress:(NSString *message)
void setMouseInteractionModeForFlight()
void setPlayerFileToLoad:(NSString *filename)
void setTextColor:(OOColor *color)
void printLineNoScroll:align:color:fadeTime:key:addToArray:(NSString *str,[align] OOGUIAlignment alignment,[color] OOColor *text_color,[fadeTime] float text_fade,[key] NSString *text_key,[addToArray] NSMutableArray *text_array)
OOColor * textColor
NSDictionary * userSettings()
void setBackgroundColor:(OOColor *color)
void printLongText:align:color:fadeTime:key:addToArray:(NSString *str,[align] OOGUIAlignment alignment,[color] OOColor *text_color,[fadeTime] float text_fade,[key] NSString *text_key,[addToArray] NSMutableArray *text_array)
int drawGUI:drawCursor:(GLfloat alpha,[drawCursor] BOOL drawCursor)
void setAlpha:(GLfloat an_alpha)
OOColor * textCommsColor
void fadeOutFromTime:overDuration:(OOTimeAbsolute now_time,[overDuration] OOTimeDelta duration)
void setLineWidth:(GLfloat value)
NSString * deferredHudName
void setOverallAlpha:(GLfloat newAlphaValue)
GLfloat overallAlpha
void setFov:fromFraction:(float value,[fromFraction] BOOL fromFraction)
void setGammaValue:(float value)
float fov:(BOOL inFraction)
void setMsaa:(BOOL newMsaa)
OOSDRToneMapper sdrToneMapper()
GameController * gameController
OOAsyncWorkManager * sharedAsyncWorkManager()
void setInnerColor:outerColor:(OOColor *color1,[outerColor] OOColor *color2)
void setLifetime:(double lifetime)
instancetype breakPatternWithPolygonSides:startAngle:aspectRatio:(NSUInteger sides,[startAngle] float startAngleDegrees,[aspectRatio] float aspectRatio)
void setObject:forKey:inCache:(id inElement,[forKey] NSString *inKey,[inCache] NSString *inCacheKey)
id objectForKey:inCache:(NSString *inKey,[inCache] NSString *inCacheKey)
OOCacheManager * sharedCache()
OOCharacter * randomCharacterWithRole:andOriginalSystem:(NSString *c_role,[andOriginalSystem] OOSystemID s)
OOColor * colorWithRed:green:blue:alpha:(float red,[green] float green,[blue] float blue,[alpha] float alpha)
Definition OOColor.m:95
OOColor * brightColorWithDescription:(id description)
Definition OOColor.m:205
OOColor * colorWithDescription:(id description)
Definition OOColor.m:127
OOColor * greenColor()
Definition OOColor.m:274
void getRed:green:blue:alpha:(float *red,[green] float *green,[blue] float *blue,[alpha] float *alpha)
Definition OOColor.m:368
OOColor * whiteColor()
Definition OOColor.m:256
OOColor * colorWithHue:saturation:brightness:alpha:(float hue,[saturation] float saturation,[brightness] float brightness,[alpha] float alpha)
Definition OOColor.m:87
OOColor * yellowColor()
Definition OOColor.m:292
OOColor * blendedColorWithFraction:ofColor:(float fraction,[ofColor] OOColor *color)
Definition OOColor.m:328
NSString * getRandomCommodity()
OOCommodityMarket * generateMarketForSystemWithEconomy:andScript:(OOEconomyID economy,[andScript] NSString *scriptName)
OOMassUnit massUnitForGood:(NSString *good)
OOMassUnit massUnitForGood:(OOCommodityType good)
OOCargoQuantity quantityForGood:(OOCommodityType good)
NSDictionary * definitionForGood:(OOCommodityType good)
NSString * nameForGood:(OOCommodityType good)
NSString * conditionScript()
OOTechLevelID techLevel()
OOEquipmentType * equipmentTypeWithIdentifier:(NSString *identifier)
OOCreditsQuantity price()
instancetype explosionCloudFromEntity:withSettings:(Entity *entity,[withSettings] NSDictionary *settings)
instancetype laserFlashWithPosition:velocity:color:(HPVector position,[velocity] Vector vel,[color] OOColor *color)
OOGraphicsResetManager * sharedManager()
void runCallback:(HPVector location)
BOOL callMethod:inContext:withArguments:count:result:(jsid methodID,[inContext] JSContext *context,[withArguments] jsval *argv,[count] intN argc,[result] jsval *outResult)
Definition OOJSScript.m:395
OOJavaScriptEngine * sharedEngine()
void garbageCollectionOpportunity:(BOOL force)
void setUp()
Definition OOMaterial.m:38
OOOpenGLExtensionManager * sharedManager()
instancetype shrinkingRingFromEntity:(Entity *sourceEntity)
instancetype ringFromEntity:(Entity *sourceEntity)
id jsScriptFromFileNamed:properties:(NSString *fileName,[properties] NSDictionary *properties)
Definition OOScript.m:192
instancetype groupWithName:(NSString *name)
NSDictionary * shipyardInfoForKey:(NSString *key)
NSArray * playerShipKeys()
OOProbabilitySet * probabilitySetForRole:(NSString *role)
NSString * randomShipKeyForRole:(NSString *role)
OOShipRegistry * sharedRegistry()
NSDictionary * shipInfoForKey:(NSString *key)
NSArray * demoShipKeys()
NSDictionary * effectInfoForKey:(NSString *key)
id initWithSound:(OOSound *inSound)
void playSound:(OOSound *inSound)
float masterVolume()
Definition OOALSound.m:80
id soundWithCustomSoundKey:(NSString *key)
Definition Universe.m:11071
void setRadius:andCorona:(GLfloat rad,[andCorona] GLfloat corona)
void drawDirectVisionSunGlare()
double radius()
BOOL changeSunProperty:withDictionary:(NSString *key,[withDictionary] NSDictionary *dict)
void setPosition:(HPVector posn)
void getSpecularComponents:(GLfloat[4] components)
void getDiffuseComponents:(GLfloat[4] components)
void drawStarGlare()
BOOL setSunColor:(OOColor *sun_color)
Definition OOSunEntity.m:61
NSDictionary * getPropertiesForSystemKey:(NSString *key)
NSPoint getCoordinatesForSystem:inGalaxy:(OOSystemID s,[inGalaxy] OOGalaxyID g)
id getProperty:forSystem:inGalaxy:(NSString *property,[forSystem] OOSystemID s,[inGalaxy] OOGalaxyID g)
void setProperty:forSystemKey:andLayer:toValue:fromManifest:(NSString *property,[forSystemKey] NSString *key,[andLayer] OOSystemLayer layer,[toValue] id value,[fromManifest] NSString *manifest)
NSArray * getNeighbourIDsForSystem:inGalaxy:(OOSystemID s,[inGalaxy] OOGalaxyID g)
NSDictionary * getPropertiesForSystem:inGalaxy:(OOSystemID s,[inGalaxy] OOGalaxyID g)
void clearCache()
Definition OOTexture.m:357
instancetype waypointWithDictionary:(NSDictionary *info)
void setBounty:withReason:(OOCreditsQuantity amount, [withReason] OOLegalStatusReason reason)
void setSystemID:(OOSystemID sid)
void setGuiToStatusScreen()
void setRandom_factor:(int rf)
Vector weaponViewOffset()
OOGalaxyID galaxyNumber()
void setWormhole:(WormholeEntity *newWormhole)
BOOL setUpShipFromDictionary:(NSDictionary *shipDict)
StationEntity * dockedStation()
void setShowDemoShips:(BOOL value)
Quaternion normalOrientation()
BOOL switchHudTo:(NSString *hudFileName)
void addScannedWormhole:(WormholeEntity *wormhole)
void addToAdjustTime:(double seconds)
NSPoint galaxy_coordinates
NSMutableArray * commLog
unsigned showDemoShips
void setGalaxyCoordinates:(NSPoint newPosition)
HeadUpDisplay * hud
void runUnsanitizedScriptActions:allowingAIMethods:withContextName:forTarget:(NSArray *unsanitizedActions,[allowingAIMethods] BOOL allowAIMethods,[withContextName] NSString *contextName,[forTarget] ShipEntity *target)
void setJumpCause:(NSString *value)
void setDockedAtMainStation()
void setPreviousSystemID:(OOSystemID sid)
OOMatrix customViewMatrix
Vector customViewUpVector
Vector customViewForwardVector
Vector viewpointOffset()
OOSystemID systemID()
GLfloat baseMass()
NSString * jumpCause()
NSString * dial_clock_adjusted()
BOOL doWorldEventUntilMissionScreen:(jsid message)
PlayerEntity * sharedPlayer()
OOSystemID targetSystemID()
NSString * builtInPath()
OOSound * ooSoundNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)
NSDictionary * dictionaryFromFilesNamed:inFolder:andMerge:(NSString *fileName,[inFolder] NSString *folderName,[andMerge] BOOL mergeFiles)
NSArray * arrayFromFilesNamed:inFolder:andMerge:(NSString *fileName,[inFolder] NSString *folderName,[andMerge] BOOL mergeFiles)
BOOL writeDiagnosticData:toFileNamed:(NSData *data,[toFileNamed] NSString *name)
OOSystemDescriptionManager * systemDescriptionManager()
void setUseAddOns:(NSString *useAddOns)
NSDictionary * roleCategoriesDictionary()
NSDictionary * dictionaryFromFilesNamed:inFolder:mergeMode:cache:(NSString *fileName,[inFolder] NSString *folderName,[mergeMode] OOResourceMergeMode mergeMode,[cache] BOOL useCache)
OOSystemID parent()
Definition Universe.m:187
OOSystemID _location
Definition Universe.m:156
double distance()
Definition Universe.m:190
OOSystemID _parent
Definition Universe.m:156
double _time
Definition Universe.m:157
double _cost
Definition Universe.m:157
OOSystemID location()
Definition Universe.m:188
instancetype elementWithLocation:parent:cost:distance:time:jumps:(OOSystemID location,[parent] OOSystemID parent,[cost] double cost,[distance] double distance,[time] double time,[jumps] int jumps)
Definition Universe.m:173
double cost()
Definition Universe.m:189
double _distance
Definition Universe.m:157
double time()
Definition Universe.m:191
void setDemoStartTime:(OOTimeAbsolute time)
void setIsWreckage:(BOOL isw)
void setBounty:withReason:(OOCreditsQuantity amount,[withReason] OOLegalStatusReason reason)
NSDictionary * shipInfoDictionary()
void removeEquipmentItem:(NSString *equipmentKey)
void setFuel:(OOFuelQuantity amount)
void rescaleBy:writeToCache:(GLfloat factor, [writeToCache] BOOL writeToCache)
void setStatus:(OOEntityStatus stat)
GLfloat weaponRange
void setCargoFlag:(OOCargoFlag flag)
BOOL witchspaceLeavingEffects()
void setRoll:(double amount)
void performTumble()
void setDestination:(HPVector dest)
NSString * shipDataKey()
void setGroup:(OOShipGroup *group)
NSString * primaryRole
void setSubEntityTakingDamage:(ShipEntity *sub)
void doScriptEvent:(jsid message)
NSArray * portWeaponOffset
void setPrimaryRole:(NSString *role)
NSArray * starboardWeaponOffset
void setHeatInsulation:(GLfloat value)
OOScanClass scanClass()
void leaveWitchspace()
void setPitch:(double amount)
BOOL isHostileTo:(Entity *entity)
void setAITo:(NSString *aiString)
void enterTargetWormhole()
NSArray * aftWeaponOffset
NSArray * forwardWeaponOffset
void setPendingEscortCount:(uint8_t count)
HPVector destination()
Vector velocity()
uint8_t pendingEscortCount()
void setTemperature:(GLfloat value)
void setCrew:(NSArray *crewArray)
void setDemoShip:(OOScalar demoRate)
void doScriptEvent:withArgument:(jsid message,[withArgument] id argument)
void switchAITo:(NSString *aiString)
void setCommodity:andAmount:(OOCommodityType co_type,[andAmount] OOCargoQuantity co_amount)
GLfloat maxFlightSpeed
OOCommodityType commodityType()
BOOL changeProperty:withDictionary:(NSString *key,[withDictionary] NSDictionary *dict)
Definition SkyEntity.m:164
OOColor * skyColor
Definition SkyEntity.m:158
OOCommodityMarket * localMarket
void setAllowsFastDocking:(BOOL newValue)
OOCommodityMarket * initialiseLocalMarket()
void setRequiresDockingClearance:(BOOL newValue)
void setEquivalentTechLevel:(OOTechLevelID value)
void setLocalMarket:(NSArray *market)
unsigned interstellarUndockingAllowed
void setPlanet:(OOPlanetEntity *planet)
void setLocalShipyard:(NSArray *market)
void setAllegiance:(NSString *newAllegiance)
BOOL role:isInCategory:(NSString *role,[isInCategory] NSString *category)
Definition Universe.m:3014
OOSystemID findSystemNumberAtCoords:withGalaxy:includingHidden:(NSPoint coords,[withGalaxy] OOGalaxyID gal,[includingHidden] BOOL hidden)
Definition Universe.m:8584
NSArray * speechArray
Definition Universe.h:343
NSSpeechSynthesizer * speechSynthesizer
Definition Universe.h:338
OOGraphicsDetail detailLevel
Definition Universe.m:10066
Random_Seed marketSeed()
Definition Universe.m:8997
HPVector legacyPositionFrom:asCoordinateSystem:(HPVector pos,[asCoordinateSystem] NSString *system)
Definition Universe.m:2318
unsigned n_entities
Definition Universe.h:196
void setTimeAccelerationFactor:(double newTimeAccelerationFactor)
Definition Universe.m:7392
NSDictionary * commodityDataForType:(OOCommodityType type)
Definition Universe.m:4425
void populateNormalSpace()
Definition Universe.m:1701
OOWeakReference * _firstBeacon
Definition Universe.h:225
Entity< OOBeaconEntity > * lastBeacon()
Definition Universe.m:3776
void setDetailLevelDirectly:(OOGraphicsDetail value)
Definition Universe.m:10033
NSMutableDictionary * conditionScripts
Definition Universe.h:353
Entity * firstEntityTargetedByPlayerPrecisely()
Definition Universe.m:6315
BOOL useShaders()
Definition Universe.m:10072
OOGalaxyID galaxyID
Definition Universe.h:288
OOSunEntity * sun()
Definition Universe.m:3693
NSArray * demo_ships
Definition Universe.h:261
static BOOL MaintainLinkedLists(Universe *uni)
Definition Universe.m:5384
void pauseGame()
Definition Universe.m:1017
OOTimeDelta getTimeDelta()
Definition Universe.m:6658
static OOComparisonResult compareName(id dict1, id dict2, void *context)
Definition Universe.m:9495
BOOL _autoMessageLogBg
Definition Universe.h:358
unsigned countShipsMatchingPredicate:parameter:inRange:ofEntity:(EntityFilterPredicate predicate,[parameter] void *parameter,[inRange] double range,[ofEntity] Entity *entity)
Definition Universe.m:6439
OOSystemID systemID
Definition Universe.h:289
ShipEntity * newShipWithName:usePlayerProxy:isSubentity:(NSString *shipKey,[usePlayerProxy] BOOL usePlayerProxy,[isSubentity] BOOL OO_RETURNS_RETAINED)
Definition Universe.m:4090
void deleteOpenGLObjects()
Definition Universe.m:528
void setUpUniverseFromWitchspace()
Definition Universe.m:1199
void resetCommsLogColor()
Definition Universe.m:9958
void allShipsDoScriptEvent:andReactToAIMessage:(jsid event,[andReactToAIMessage] NSString *message)
Definition Universe.m:9905
BOOL * systemsFound()
Definition Universe.m:8672
BOOL reducedDetail()
Definition Universe.m:10026
NSString * collisionDescription()
Definition Universe.m:6685
BOOL autoSaveNow
Definition Universe.m:10007
BOOL _doingStartUp
Definition Universe.h:362
OOINLINE BOOL EntityInRange(HPVector p1, Entity *e2, float range)
Definition Universe.m:6467
BOOL autoMessageLogBg()
Definition Universe.m:10256
void setDockingClearanceProtocolActive:(BOOL newValue)
Definition Universe.m:3168
NSString * system_repopulator
Definition Universe.h:309
OOTimeAbsolute demo_start_time
Definition Universe.h:256
OOSystemID targetSystemID
Definition Universe.h:290
void setLighting()
Definition Universe.m:2030
BOOL ECMVisualFXEnabled
Definition Universe.m:7413
NSString * system_names[256]
Definition Universe.h:292
void setUpSpace()
Definition Universe.m:1381
NSString * useAddOns
Definition Universe.m:954
unsigned countShipsWithRole:inRange:ofEntity:(NSString *role,[inRange] double range,[ofEntity] Entity *entity)
Definition Universe.m:6364
NSString * defaultAIForRole:(NSString *role)
Definition Universe.m:4221
GLfloat sun_diffuse[4]
Definition Universe.h:209
id nearestEntityMatchingPredicate:parameter:relativeToEntity:(EntityFilterPredicate predicate,[parameter] void *parameter,[relativeToEntity] Entity *entity)
Definition Universe.m:6595
void selectIntro2Previous()
Definition Universe.m:3593
GuiDisplayGen * messageGUI()
Definition Universe.m:9942
void forceWitchspaceEntries()
Definition Universe.m:3026
NSUInteger demo_ship_index
Definition Universe.h:259
BOOL dockingClearanceProtocolActive()
Definition Universe.m:3162
Entity * y_list_start
Definition Universe.h:201
NSDictionary * customSounds
Definition Universe.h:272
BOOL doProcedurallyTexturedPlanets
Definition Universe.m:941
void setPauseMessageVisible:(BOOL value)
Definition Universe.m:10238
OOCreditsQuantity getEquipmentPriceForKey:(NSString *eq_key)
Definition Universe.m:4235
static void VerifyDescArray(NSString *key, NSArray *desc)
Definition Universe.m:8025
NSDictionary * currentSystemData()
Definition Universe.m:8181
NSDictionary * descriptions()
Definition Universe.m:7996
OOCommodities * commodities
Definition Universe.m:4251
void getActiveViewMatrix:forwardVector:upVector:(OOMatrix *outMatrix, [forwardVector] Vector *outForward, [upVector] Vector *outUp)
Definition Universe.m:4659
void showCommsLog:(OOTimeDelta how_long)
Definition Universe.m:7060
NSArray * stations()
Definition Universe.m:3709
NSMutableSet * allStations
Definition Universe.h:303
NSDictionary * roleCategories
Definition Universe.h:281
GuiDisplayGen * comm_log_gui
Definition Universe.h:237
void setAirResistanceFactor:(GLfloat newFactor)
Definition Universe.m:10106
GLfloat sun_specular[4]
Definition Universe.h:210
void removeAllEntitiesExceptPlayer()
Definition Universe.m:5720
void setWitchspaceBreakPattern:(BOOL newValue)
Definition Universe.m:3156
OOTimeAbsolute getTime()
Definition Universe.m:6652
NSUInteger demo_ship_subindex
Definition Universe.h:260
NSDictionary * gameSettings()
Definition Universe.m:4505
BOOL doRemoveEntity:(Entity *entity)
Definition Universe.m:10619
void handleOoliteException:(NSException *ooliteException)
Definition Universe.m:10078
BOOL breakPatternHide()
Definition Universe.m:3896
GLuint targetFramebufferID
Definition Universe.h:371
NSDictionary * _descriptions
Definition Universe.h:271
void drawWatermarkString:(NSString *watermarkString)
Definition Universe.m:5344
void speakWithSubstitutions:(NSString *text)
Definition Universe.m:6950
ShipEntity * addShipAt:withRole:withinRadius:(HPVector pos,[withRole] NSString *role,[withinRadius] GLfloat radius)
Definition Universe.m:2789
void enterGUIViewModeWithMouseInteraction:(BOOL mouseInteraction)
Definition Universe.m:6777
ShipEntity * addShipWithRole:launchPos:rfactor:(NSString *desc, [launchPos] HPVector launchPos, [rfactor] GLfloat rfactor)
Definition Universe.m:2122
ShipEntity * newShipWithName:(NSString *OO_RETURNS_RETAINED)
Definition Universe.m:4189
NSArray * equipmentData
Definition Universe.m:8898
ShipEntity * newShipWithRole:(NSString *OO_RETURNS_RETAINED)
Definition Universe.m:4003
void drawTargetTextureIntoDefaultFramebuffer()
Definition Universe.m:591
int cursor_row
Definition Universe.h:198
void resetFramesDoneThisUpdate()
Definition Universe.m:5306
void setGalaxyTo:andReinit:(OOGalaxyID g,[andReinit] BOOL forced)
Definition Universe.m:7944
HPVector coordinatesFromCoordinateSystemString:(NSString *system_x_y_z)
Definition Universe.m:2419
int demo_stage
Definition Universe.h:258
static void VerifyDescString(NSString *key, NSString *desc)
Definition Universe.m:8015
BOOL doLinkedListMaintenanceThisUpdate
Definition Universe.h:331
NSArray * getStationMarkets()
Definition Universe.m:9047
NSDictionary * characters
Definition Universe.m:8109
void setMainLightPosition:(Vector sunPos)
Definition Universe.m:2113
void setUpSettings()
Definition Universe.m:10305
void setDisplayText:(BOOL value)
Definition Universe.m:9964
NSMutableDictionary * waypoints
Definition Universe.h:227
void setECMVisualFXEnabled:(BOOL isEnabled)
Definition Universe.m:7419
int next_universal_id
Definition Universe.h:220
BOOL wasDisplayGUI
Definition Universe.h:240
OOTimeAbsolute demo_stage_time
Definition Universe.h:255
BOOL _pauseMessage
Definition Universe.h:355
BOOL no_update
Definition Universe.h:316
OOViewID viewDirection
Definition Universe.m:6698
void addMessage:forCount:forceDisplay:(NSString *text,[forCount] OOTimeDelta count,[forceDisplay] BOOL forceDisplay)
Definition Universe.m:7001
void populateSystemFromDictionariesWithSun:andPlanet:(OOSunEntity *sun,[andPlanet] OOPlanetEntity *planet)
Definition Universe.m:1799
NSUInteger sessionID()
Definition Universe.m:929
void addMessage:forCount:(NSString *text,[forCount] OOTimeDelta count)
Definition Universe.m:6944
BOOL deterministic_population
Definition Universe.h:310
BOOL _autoCommLog
Definition Universe.h:356
OOMatrix viewMatrix
Definition Universe.m:5312
void setUpUniverseFromStation()
Definition Universe.m:1115
void selectIntro2Next()
Definition Universe.m:3620
void verifyEntitySessionIDs()
Definition Universe.m:10412
NSMutableDictionary * populatorSettings
Definition Universe.h:307
NSDictionary * screenBackgrounds
Definition Universe.h:283
void unMagicMainStation()
Definition Universe.m:3721
unsigned countEntitiesMatchingPredicate:parameter:inRange:ofEntity:(EntityFilterPredicate predicate,[parameter] void *parameter,[inRange] double range,[ofEntity] Entity *entity)
Definition Universe.m:6403
void setUpWitchspaceBetweenSystem:andSystem:(OOSystemID s1,[andSystem] OOSystemID s2)
Definition Universe.m:1276
NSDictionary * globalSettings
Definition Universe.m:8892
GuiDisplayGen * message_gui
Definition Universe.h:236
BOOL addShips:withRole:intoBoundingBox:(int howMany,[withRole] NSString *desc,[intoBoundingBox] BoundingBox bbox)
Definition Universe.m:2562
OOJSScript * getConditionScript:(NSString *scriptname)
Definition Universe.m:11061
OOSunEntity * cachedSun
Definition Universe.h:301
static BOOL IsCandidateMainStationPredicate(Entity *entity, void *parameter)
Definition Universe.m:3627
void lightForEntity:(BOOL isLit)
Definition Universe.m:4588
void dumpCollisions()
Definition Universe.m:6692
HPVector getWitchspaceExitPosition()
Definition Universe.m:9785
NSUInteger _sessionID
Definition Universe.h:206
BOOL system_found[256]
Definition Universe.h:293
NSMutableArray * findEntitiesMatchingPredicate:parameter:inRange:ofEntity:(EntityFilterPredicate predicate,[parameter] void *parameter,[inRange] double range,[ofEntity] Entity *entity)
Definition Universe.m:6477
NSString * currentMessage
Definition Universe.h:231
CollisionRegion * universeRegion
Definition Universe.h:328
NSDictionary * generateSystemData:useCache:(OOSystemID s,[useCache] BOOL useCache)
Definition Universe.m:8166
NSArray * wormholes()
Definition Universe.m:3715
void setUpUniverseFromMisjump()
Definition Universe.m:1235
void drawMessage()
Definition Universe.m:5318
NSDictionary * explosionSettings
Definition Universe.h:284
void clearSystemPopulator()
Definition Universe.m:1767
OOPlanetEntity * cachedPlanet
Definition Universe.h:300
OOMatrix activeViewMatrix()
Definition Universe.m:4705
NSSize targetFramebufferSize
Definition Universe.h:367
NSDictionary * getPopulatorSettings()
Definition Universe.m:1774
NSUInteger entityCount()
Definition Universe.m:980
NSMutableArray * activeWormholes
Definition Universe.h:324
void setGalaxyTo:(OOGalaxyID g)
Definition Universe.m:7938
ShipEntity * demo_ship
Definition Universe.h:297
void clearPreviousMessage()
Definition Universe.m:6885
NSMutableArray * entities
Definition Universe.h:223
NSMutableArray * findShipsMatchingPredicate:parameter:inRange:ofEntity:(EntityFilterPredicate predicate,[parameter] void *parameter,[inRange] double range,[ofEntity] Entity *entity)
Definition Universe.m:6539
void setSystemTo:(OOSystemID s)
Definition Universe.m:7969
void setUpInitialUniverse()
Definition Universe.m:10556
void populateSpaceFromActiveWormholes()
Definition Universe.m:10774
void defineFrustum()
Definition Universe.m:4720
BOOL deterministicPopulation()
Definition Universe.m:1793
void prepareToRenderIntoDefaultFramebuffer()
Definition Universe.m:5284
void loadDescriptions()
Definition Universe.m:8082
BOOL reinitAndShowDemo:(BOOL showDemo)
Definition Universe.m:10439
OOPlanetEntity * setUpPlanet()
Definition Universe.m:1343
Entity * firstEntityTargetedByPlayer()
Definition Universe.m:6229
GLfloat skyClearColor[4]
Definition Universe.m:3874
NSString * getSystemName:(OOSystemID sys)
Definition Universe.m:8393
OOCommodityMarket * commodityMarket
Definition Universe.m:8910
OOPlanetEntity * planet()
Definition Universe.m:3683
void loadScenarios()
Definition Universe.m:8102
OOSystemDescriptionManager * systemManager
Definition Universe.m:8141
NSPoint findSystemCoordinatesWithPrefix:exactMatch:(NSString *p_fix,[exactMatch] BOOL exactMatch)
Definition Universe.m:8640
BOOL _permanentMessageLog
Definition Universe.h:359
BOOL inInterstellarSpace()
Definition Universe.m:8218
BOOL _witchspaceBreakPattern
Definition Universe.h:360
NSArray * closeSystems
Definition Universe.h:312
void loadConditionScripts()
Definition Universe.m:11031
static void PreloadOneSound(NSString *soundName)
Definition Universe.m:10736
NSArray * _scenarios
Definition Universe.h:274
void setViewDirection:(OOViewID vd)
Definition Universe.m:6704
ShipEntity * newShipWithName:usePlayerProxy:(NSString *shipKey,[usePlayerProxy] BOOL OO_RETURNS_RETAINED)
Definition Universe.m:4085
OOTimeDelta next_repopulation
Definition Universe.h:308
void setSystemDataForGalaxy:planet:key:value:fromManifest:forLayer:(OOGalaxyID gnum,[planet] OOSystemID pnum,[key] NSString *key,[value] id object,[fromManifest] NSString *manifest,[forLayer] OOSystemLayer layer)
Definition Universe.m:8234
ShipEntity * addWreckageFrom:withRole:at:scale:lifetime:(ShipEntity *ship,[withRole] NSString *wreckRole,[at] HPVector rpos,[scale] GLfloat scale,[lifetime] GLfloat lifetime)
Definition Universe.m:6061
GLuint msaaFramebufferID
Definition Universe.h:368
BOOL _bloom
Definition Universe.h:380
BOOL removeEntity:(Entity *entity)
Definition Universe.m:5687
double timeAccelerationFactor
Definition Universe.m:7386
OOTimeDelta time_delta
Definition Universe.h:253
void setCurrentPostFX:(int newCurrentPostFX)
Definition Universe.m:293
void removeDemoShips()
Definition Universe.m:5772
NSString * getSystemInhabitants:plural:(OOSystemID sys,[plural] BOOL plural)
Definition Universe.m:8417
BOOL dumpCollisionInfo
Definition Universe.h:265
Entity * sortedEntities[UNIVERSE_MAX_ENTITIES+1]
Definition Universe.h:195
GLfloat main_light_position[4]
Definition Universe.h:263
NSArray * addShipsAt:withRole:quantity:withinRadius:asGroup:(HPVector pos,[withRole] NSString *role,[quantity] unsigned count,[withinRadius] GLfloat radius,[asGroup] BOOL isGroup)
Definition Universe.m:2896
void setSkyColorRed:green:blue:alpha:(GLfloat red,[green] GLfloat green,[blue] GLfloat blue,[alpha] GLfloat alpha)
Definition Universe.m:3880
void showGUIMessage:withScroll:andColor:overDuration:(NSString *text,[withScroll] BOOL scroll,[andColor] OOColor *selectedColor,[overDuration] OOTimeDelta how_long)
Definition Universe.m:7067
BOOL _dockingClearanceProtocolActive
Definition Universe.h:361
void handleGameOver()
Definition Universe.m:3192
BOOL displayFPS
Definition Universe.m:9982
BOOL setUseAddOns:fromSaveGame:forceReinit:(NSString *newUse,[fromSaveGame] BOOL saveGame,[forceReinit] BOOL force)
Definition Universe.m:966
BOOL autoSave
Definition Universe.m:9995
static BOOL IsFriendlyStationPredicate(Entity *entity, void *parameter)
Definition Universe.m:3633
ShipEntity * firstShipHitByLaserFromShip:inDirection:offset:gettingRangeFound:(ShipEntity *srcEntity,[inDirection] OOWeaponFacing direction,[offset] Vector offset,[gettingRangeFound] GLfloat *range_ptr)
Definition Universe.m:6122
int breakPatternCounter
Definition Universe.h:295
ShipEntity * newShipWithName:usePlayerProxy:isSubentity:andScaleFactor:(NSString *shipKey,[usePlayerProxy] BOOL usePlayerProxy,[isSubentity] BOOL isSubentity,[andScaleFactor] float OO_RETURNS_RETAINED)
Definition Universe.m:4095
NSPoint coordinatesForSystem:(OOSystemID s)
Definition Universe.m:8435
void initTargetFramebufferWithViewSize:(NSSize viewSize)
Definition Universe.m:338
NSArray * neighboursToSystem:(OOSystemID system_number)
Definition Universe.m:8813
OOTimeAbsolute countdown_messageRepeatTime
Definition Universe.h:233
void setUpCargoPods()
Definition Universe.m:10395
void setGameView:(MyOpenGLView *view)
Definition Universe.m:4486
BOOL _permanentCommLog
Definition Universe.h:357
NSMutableSet * entitiesDeadThisUpdate
Definition Universe.h:333
NSArray * shipsForSaleForSystem:withTL:atTime:(OOSystemID s,[withTL] OOTechLevelID specialTL,[atTime] OOTimeAbsolute current_time)
Definition Universe.m:9077
HPVector locationByCode:withSun:andPlanet:(NSString *code,[withSun] OOSunEntity *sun,[andPlanet] OOPlanetEntity *planet)
Definition Universe.m:1889
int currentPostFX()
Definition Universe.m:288
NSString * getRandomCommodity()
Definition Universe.m:4396
GLfloat stars_ambient[4]
Definition Universe.h:203
void fillCargopodWithRandomCargo:(ShipEntity *cargopod)
Definition Universe.m:4383
void verifyDescriptions()
Definition Universe.m:8057
MyOpenGLView * gameView
Definition Universe.m:4493
BOOL doingStartUp()
Definition Universe.m:935
void startSpeakingString:(NSString *text)
Definition Universe.m:10115
GLfloat airResistanceFactor
Definition Universe.m:10100
NSString * randomShipKeyForRoleRespectingConditions:(NSString *role)
Definition Universe.m:3952
void repopulateSystem()
Definition Universe.m:7081
void addConditionScripts:(NSEnumerator *scripts)
Definition Universe.m:11044
NSDictionary * autoAIMap
Definition Universe.h:282
BOOL addEntity:(Entity *entity)
Definition Universe.m:5523
id findOneEntityMatchingPredicate:parameter:(EntityFilterPredicate predicate,[parameter] void *parameter)
Definition Universe.m:6517
NSDictionary * currentWaypoints()
Definition Universe.m:3837
NSString * keyForPlanetOverridesForSystem:inGalaxy:(OOSystemID s,[inGalaxy] OOGalaxyID g)
Definition Universe.m:8147
OOTimeAbsolute messageRepeatTime
Definition Universe.h:232
NSMutableArray * allPlanets
Definition Universe.h:302
void stopSpeaking()
Definition Universe.m:10121
GLfloat demo_start_z
Definition Universe.h:257
Class shipClassForShipDictionary:(NSDictionary *dict)
Definition Universe.m:4195
NSString * chooseStringForKey:inDictionary:(NSString *key, [inDictionary] NSDictionary *dictionary)
Definition Universe.m:10803
NSString * keyForInterstellarOverridesForSystems:s1:inGalaxy:(OOSystemID,[s1] OOSystemID s2,[inGalaxy] OOGalaxyID g)
Definition Universe.m:8153
GameController * gameController()
Definition Universe.m:4499
NSDictionary * generateSystemData:(OOSystemID s)
Definition Universe.m:8159
void setUpWitchspace()
Definition Universe.m:1270
void dealloc()
Definition Universe.m:865
GLint defaultDrawFBO
Definition Universe.h:377
NSArray * scenarios()
Definition Universe.m:8096
void setLastBeacon:(Entity< OOBeaconEntity > *beacon)
Definition Universe.m:3782
StationEntity * station()
Definition Universe.m:3639
Entity * z_list_start
Definition Universe.h:201
Entity< OOBeaconEntity > * firstBeacon()
Definition Universe.m:3757
GLfloat safeWitchspaceExitDistance()
Definition Universe.m:3059
void addCommsMessage:forCount:andShowComms:logOnly:(NSString *text,[forCount] OOTimeDelta count,[andShowComms] BOOL showComms,[logOnly] BOOL logOnly)
Definition Universe.m:7027
void forceLightSwitch()
Definition Universe.m:2107
Quaternion getWitchspaceExitRotation()
Definition Universe.m:9791
void selectIntro2NextCategory()
Definition Universe.m:3611
void drawUniverse()
Definition Universe.m:4822
BOOL displayGUI
Definition Universe.m:9970
GLfloat frustum[6][4]
Definition Universe.h:351
int framesDoneThisUpdate
Definition Universe.m:5300
void filterSortedLists()
Definition Universe.m:7425
void clearGUIs()
Definition Universe.m:9948
BOOL blockJSPlayerShipProps()
Definition Universe.m:10286
BOOL permanentMessageLog()
Definition Universe.m:10244
void findCollisionsAndShadows()
Definition Universe.m:6664
Entity * entity_for_uid[MAX_ENTITY_UID]
Definition Universe.h:221
BOOL wireframeGraphics
Definition Universe.m:10020
BOOL pauseMessageVisible()
Definition Universe.m:10232
static void VerifyDesc(NSString *key, id desc)
Definition Universe.m:8035
BOOL breakPatternOver()
Definition Universe.m:3890
float randomDistanceWithinScanner()
Definition Universe.m:10589
OOTimeAbsolute universal_time
Definition Universe.h:252
GuiDisplayGen * commLogGUI()
Definition Universe.m:9936
void setLibraryTextForDemoShip()
Definition Universe.m:3313
void setFirstBeacon:(Entity< OOBeaconEntity > *beacon)
Definition Universe.m:3763
StationEntity * cachedStation
Definition Universe.h:299
void clearBeacon:(Entity< OOBeaconEntity > *beaconShip)
Definition Universe.m:3809
NSString * getSystemName:forGalaxy:(OOSystemID sys,[forGalaxy] OOGalaxyID gnum)
Definition Universe.m:8399
OOWeakReference * _lastBeacon
Definition Universe.h:226
NSDictionary * generateSystemDataForGalaxy:planet:(OOGalaxyID gnum, [planet] OOSystemID pnum)
Definition Universe.m:8373
void resetBeacons()
Definition Universe.m:3741
Entity * x_list_start
Definition Universe.h:201
int _currentPostFX
Definition Universe.h:381
OOSystemID currentSystemID()
Definition Universe.m:7990
OOVisualEffectEntity * newVisualEffectWithName:(NSString *OO_RETURNS_RETAINED)
Definition Universe.m:4050
HPVector fractionalPositionFrom:to:withFraction:(HPVector point0, [to] HPVector point1, [withFraction] double routeFraction)
Definition Universe.m:10605
BOOL isSpeaking()
Definition Universe.m:10134
HPVector coordinatesForPosition:withCoordinateSystem:returningScalar:(HPVector pos,[withCoordinateSystem] NSString *system,[returningScalar] GLfloat *my_scalar)
Definition Universe.m:2175
void resizeTargetFramebufferWithViewSize:(NSSize viewSize)
Definition Universe.m:549
void preloadSounds()
Definition Universe.m:10745
BOOL witchspaceBreakPattern()
Definition Universe.m:3150
void makeSunSkimmer:andSetAI:(ShipEntity *ship,[andSetAI] BOOL setAI)
Definition Universe.m:8987
void selectIntro2PreviousCategory()
Definition Universe.m:3602
void debugDumpEntities()
Definition Universe.m:987
BOOL permanentCommLog()
Definition Universe.m:10268
NSDictionary * missiontext
Definition Universe.m:8115
float ambientLightLevel
Definition Universe.m:2024
BOOL bloom()
Definition Universe.m:278
int _colorblindMode
Definition Universe.h:382
static OOComparisonResult comparePrice(id dict1, id dict2, void *context)
Definition Universe.m:9510
NSDictionary * demoShipData()
Definition Universe.m:3307
unsigned countShipsWithPrimaryRole:inRange:ofEntity:(NSString *role,[inRange] double range,[ofEntity] Entity *entity)
Definition Universe.m:6379
NSArray * planets()
Definition Universe.m:3703
void setNextBeacon:(Entity< OOBeaconEntity > *beaconShip)
Definition Universe.m:3795
int colorblindMode()
Definition Universe.m:333
NSArray * entityList()
Definition Universe.m:1010
NSArray * equipmentDataOutfitting
Definition Universe.m:8904
OOCargoQuantity getRandomAmountOfCommodity:(OOCommodityType co_type)
Definition Universe.m:4402
GuiDisplayGen * gui
Definition Universe.m:9930
OOSystemID destination
NSPoint destinationCoordinates()
OOSystemID origin
voidpf uLong offset
Definition ioapi.h:140
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
const char int mode
Definition ioapi.h:133
RANROTSeed RanrotSeedFromRandomSeed(Random_Seed seed)
float randf(void)
RANROTSeed RANROTGetFullSeed(void)
void ranrot_srand(uint32_t seed)
void OOInitReallyRandom(uint64_t seed)
void RANROTSetFullSeed(RANROTSeed seed)
unsigned RanrotWithSeed(RANROTSeed *ioSeed)
int gen_rnd_number(void)
double cunningFee(double value, double precision)
void seed_for_planet_description(Random_Seed s_seed)
RANROTSeed MakeRanrotSeed(uint32_t seed)
unsigned Ranrot(void)
void rotate_seed(Random_Seed *seed_ptr)
OOINLINE double distanceBetweenPlanetPositions(int x1, int y1, int x2, int y2) INLINE_CONST_FUNC