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