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