21 |
21 |
22 #import "MapConfigViewController.h" |
22 #import "MapConfigViewController.h" |
23 #import "PascalImports.h" |
23 #import "PascalImports.h" |
24 #import "CommodityFunctions.h" |
24 #import "CommodityFunctions.h" |
25 #import "UIImageExtra.h" |
25 #import "UIImageExtra.h" |
26 #import "SDL_net.h" |
26 |
27 #import <pthread.h> |
|
28 |
|
29 #define INDICATOR_TAG 7654 |
|
30 |
27 |
31 @implementation MapConfigViewController |
28 @implementation MapConfigViewController |
32 @synthesize previewButton, maxHogs, seedCommand, templateFilterCommand, mapGenCommand, mazeSizeCommand, themeCommand, staticMapCommand, |
29 @synthesize previewButton, maxHogs, seedCommand, templateFilterCommand, mapGenCommand, mazeSizeCommand, themeCommand, staticMapCommand, |
33 tableView, maxLabel, sizeLabel, segmentedControl, slider, lastIndexPath, themeArray, mapArray, busy, delegate; |
30 tableView, maxLabel, sizeLabel, segmentedControl, slider, lastIndexPath, themeArray, mapArray, busy, delegate; |
34 |
31 |
35 |
32 |
36 -(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { |
33 -(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { |
37 return rotationManager(interfaceOrientation); |
34 return rotationManager(interfaceOrientation); |
38 } |
|
39 |
|
40 #pragma mark - |
|
41 #pragma mark Preview Handling |
|
42 -(int) sendToEngine: (NSString *)string { |
|
43 unsigned char length = [string length]; |
|
44 |
|
45 SDLNet_TCP_Send(csd, &length , 1); |
|
46 return SDLNet_TCP_Send(csd, [string UTF8String], length); |
|
47 } |
|
48 |
|
49 -(const uint8_t *)engineProtocol:(NSInteger) port { |
|
50 IPaddress ip; |
|
51 BOOL serverQuit = NO; |
|
52 static uint8_t map[128*32]; |
|
53 |
|
54 if (SDLNet_Init() < 0) { |
|
55 DLog(@"SDLNet_Init: %s", SDLNet_GetError()); |
|
56 serverQuit = YES; |
|
57 } |
|
58 |
|
59 // Resolving the host using NULL make network interface to listen |
|
60 if (SDLNet_ResolveHost(&ip, NULL, port) < 0) { |
|
61 DLog(@"SDLNet_ResolveHost: %s\n", SDLNet_GetError()); |
|
62 serverQuit = YES; |
|
63 } |
|
64 |
|
65 // Open a connection with the IP provided (listen on the host's port) |
|
66 if (!(sd = SDLNet_TCP_Open(&ip))) { |
|
67 DLog(@"SDLNet_TCP_Open: %s %\n", SDLNet_GetError(), port); |
|
68 serverQuit = YES; |
|
69 } |
|
70 |
|
71 // launch the preview here so that we're sure the tcp channel is open |
|
72 pthread_t thread_id; |
|
73 pthread_create(&thread_id, NULL, (void *)GenLandPreview, (void *)port); |
|
74 pthread_detach(thread_id); |
|
75 |
|
76 DLog(@"Waiting for a client on port %d", port); |
|
77 while (!serverQuit) { |
|
78 /* This check the sd if there is a pending connection. |
|
79 * If there is one, accept that, and open a new socket for communicating */ |
|
80 csd = SDLNet_TCP_Accept(sd); |
|
81 if (NULL != csd) { |
|
82 DLog(@"Client found"); |
|
83 |
|
84 [self sendToEngine:self.seedCommand]; |
|
85 [self sendToEngine:self.templateFilterCommand]; |
|
86 [self sendToEngine:self.mapGenCommand]; |
|
87 [self sendToEngine:self.mazeSizeCommand]; |
|
88 [self sendToEngine:@"!"]; |
|
89 |
|
90 memset(map, 0, 128*32); |
|
91 SDLNet_TCP_Recv(csd, map, 128*32); |
|
92 SDLNet_TCP_Recv(csd, &maxHogs, sizeof(uint8_t)); |
|
93 |
|
94 SDLNet_TCP_Close(csd); |
|
95 serverQuit = YES; |
|
96 } |
|
97 } |
|
98 |
|
99 SDLNet_TCP_Close(sd); |
|
100 SDLNet_Quit(); |
|
101 return map; |
|
102 } |
|
103 |
|
104 -(void) drawingThread { |
|
105 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
106 |
|
107 // select the port for IPC and launch the preview generation through engineProtocol: |
|
108 int port = randomPort(); |
|
109 const uint8_t *map = [self engineProtocol:port]; |
|
110 uint8_t mapExp[128*32*8]; |
|
111 |
|
112 // draw the buffer (1 pixel per component, 0= transparent 1= color) |
|
113 int k = 0; |
|
114 for (int i = 0; i < 32*128; i++) { |
|
115 unsigned char byte = map[i]; |
|
116 for (int j = 0; j < 8; j++) { |
|
117 // select the color based on the leftmost bit |
|
118 if ((byte & 0x80) != 0) |
|
119 mapExp[k] = 100; |
|
120 else |
|
121 mapExp[k] = 255; |
|
122 // shift to next bit |
|
123 byte <<= 1; |
|
124 k++; |
|
125 } |
|
126 } |
|
127 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); |
|
128 CGContextRef bitmapImage = CGBitmapContextCreate(mapExp, 256, 128, 8, 256, colorspace, kCGImageAlphaNone); |
|
129 CGColorSpaceRelease(colorspace); |
|
130 |
|
131 CGImageRef previewCGImage = CGBitmapContextCreateImage(bitmapImage); |
|
132 CGContextRelease(bitmapImage); |
|
133 UIImage *previewImage = [[UIImage alloc] initWithCGImage:previewCGImage]; |
|
134 CGImageRelease(previewCGImage); |
|
135 previewCGImage = nil; |
|
136 |
|
137 // set the preview image (autoreleased) in the button and the maxhog label on the main thread to prevent a leak |
|
138 [self performSelectorOnMainThread:@selector(setButtonImage:) withObject:[previewImage makeRoundCornersOfSize:CGSizeMake(12, 12)] waitUntilDone:NO]; |
|
139 [previewImage release]; |
|
140 [self performSelectorOnMainThread:@selector(setLabelText:) withObject:[NSString stringWithFormat:@"%d", maxHogs] waitUntilDone:NO]; |
|
141 |
|
142 // restore functionality of button and remove the spinning wheel on the main thread to prevent a leak |
|
143 [self performSelectorOnMainThread:@selector(turnOnWidgets) withObject:nil waitUntilDone:NO]; |
|
144 |
|
145 [pool release]; |
|
146 //Invoking this method should be avoided as it does not give your thread a chance to clean up any resources it allocated during its execution. |
|
147 //[NSThread exit]; |
|
148 |
|
149 /* |
|
150 // http://developer.apple.com/mac/library/qa/qa2001/qa1037.html |
|
151 UIGraphicsBeginImageContext(CGSizeMake(256,128)); |
|
152 CGContextRef context = UIGraphicsGetCurrentContext(); |
|
153 UIGraphicsPushContext(context); |
|
154 |
|
155 CGContextSetRGBFillColor(context, 0.5, 0.5, 0.7, 1.0); |
|
156 CGContextFillRect(context,CGRectMake(xc,yc,1,1)); |
|
157 |
|
158 UIGraphicsPopContext(); |
|
159 UIImage *previewImage = UIGraphicsGetImageFromCurrentImageContext(); |
|
160 UIGraphicsEndImageContext(); |
|
161 */ |
|
162 } |
35 } |
163 |
36 |
164 -(IBAction) mapButtonPressed { |
37 -(IBAction) mapButtonPressed { |
165 playSound(@"clickSound"); |
38 playSound(@"clickSound"); |
166 [self updatePreview]; |
39 [self updatePreview]; |
174 // generate a seed |
47 // generate a seed |
175 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); |
48 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); |
176 NSString *seed = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid); |
49 NSString *seed = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid); |
177 CFRelease(uuid); |
50 CFRelease(uuid); |
178 NSString *seedCmd = [[NSString alloc] initWithFormat:@"eseed {%@}", seed]; |
51 NSString *seedCmd = [[NSString alloc] initWithFormat:@"eseed {%@}", seed]; |
179 [seed release]; |
|
180 self.seedCommand = seedCmd; |
52 self.seedCommand = seedCmd; |
181 [seedCmd release]; |
53 [seedCmd release]; |
182 |
54 |
183 NSIndexPath *theIndex; |
55 NSIndexPath *theIndex; |
184 if (segmentedControl.selectedSegmentIndex != 1) { |
56 if (segmentedControl.selectedSegmentIndex != 1) { |
185 // remove the current preview and title |
57 // prevent other events and add an activity while the preview is beign generated |
186 [self.previewButton setImage:nil forState:UIControlStateNormal]; |
58 [self turnOffWidgets]; |
187 [self.previewButton setTitle:nil forState:UIControlStateNormal]; |
59 [self.previewButton updatePreviewWithSeed:seed]; |
188 |
|
189 // don't display preview on slower device, too slow and memory hog |
|
190 NSString *modelId = modelType(); |
|
191 if ([modelId hasPrefix:@"iPhone1"] || [modelId hasPrefix:@"iPod1,1"] || [modelId hasPrefix:@"iPod2,1"]) { |
|
192 busy = NO; |
|
193 [self.previewButton setTitle:NSLocalizedString(@"Preview not available",@"") forState:UIControlStateNormal]; |
|
194 } else { |
|
195 // prevent other events and add an activity while the preview is beign generated |
|
196 [self turnOffWidgets]; |
|
197 |
|
198 // add a very nice spinning wheel |
|
199 UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] |
|
200 initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; |
|
201 indicator.center = CGPointMake(previewButton.bounds.size.width / 2, previewButton.bounds.size.height / 2); |
|
202 indicator.tag = INDICATOR_TAG; |
|
203 [indicator startAnimating]; |
|
204 [self.previewButton addSubview:indicator]; |
|
205 [indicator release]; |
|
206 |
|
207 // let's draw in a separate thread so the gui can work; at the end it restore other widgets |
|
208 [NSThread detachNewThreadSelector:@selector(drawingThread) toTarget:self withObject:nil]; |
|
209 } |
|
210 |
|
211 theIndex = [NSIndexPath indexPathForRow:(random()%[self.themeArray count]) inSection:0]; |
60 theIndex = [NSIndexPath indexPathForRow:(random()%[self.themeArray count]) inSection:0]; |
212 } else { |
61 } else { |
213 theIndex = [NSIndexPath indexPathForRow:(random()%[self.mapArray count]) inSection:0]; |
62 theIndex = [NSIndexPath indexPathForRow:(random()%[self.mapArray count]) inSection:0]; |
214 } |
63 // the preview for static maps is loaded in didSelectRowAtIndexPath |
215 [self.tableView reloadData]; |
64 } |
|
65 [seed release]; |
|
66 |
|
67 // perform as if user clicked on an entry |
216 [self tableView:self.tableView didSelectRowAtIndexPath:theIndex]; |
68 [self tableView:self.tableView didSelectRowAtIndexPath:theIndex]; |
217 [self.tableView scrollToRowAtIndexPath:theIndex atScrollPosition:UITableViewScrollPositionNone animated:YES]; |
69 [self.tableView scrollToRowAtIndexPath:theIndex atScrollPosition:UITableViewScrollPositionNone animated:YES]; |
218 } |
|
219 |
|
220 // instead of drawing a random map we load an image; this function is called by didSelectRowAtIndexPath only |
|
221 -(void) updatePreviewWithMap:(NSInteger) index { |
|
222 // change the preview button |
|
223 NSString *fileImage = [[NSString alloc] initWithFormat:@"%@/%@/preview.png", MAPS_DIRECTORY(),[self.mapArray objectAtIndex:index]]; |
|
224 UIImage *image = [[UIImage alloc] initWithContentsOfFile:fileImage]; |
|
225 [fileImage release]; |
|
226 [self.previewButton setImage:[image makeRoundCornersOfSize:CGSizeMake(12, 12)] forState:UIControlStateNormal]; |
|
227 [image release]; |
|
228 |
|
229 // update label |
|
230 maxHogs = 18; |
|
231 NSString *fileCfg = [[NSString alloc] initWithFormat:@"%@/%@/map.cfg", MAPS_DIRECTORY(),[self.mapArray objectAtIndex:index]]; |
|
232 NSString *contents = [[NSString alloc] initWithContentsOfFile:fileCfg encoding:NSUTF8StringEncoding error:NULL]; |
|
233 [fileCfg release]; |
|
234 NSArray *split = [contents componentsSeparatedByString:@"\n"]; |
|
235 [contents release]; |
|
236 |
|
237 // set the theme and map here |
|
238 self.themeCommand = [NSString stringWithFormat:@"etheme %@", [split objectAtIndex:0]]; |
|
239 self.staticMapCommand = [NSString stringWithFormat:@"emap %@", [self.mapArray objectAtIndex:index]]; |
|
240 |
|
241 // if the number is not set we keep 18 standard; |
|
242 // sometimes it's not set but there are trailing characters, we get around them with the second equation |
|
243 if ([split count] > 1 && [[split objectAtIndex:1] intValue] > 0) |
|
244 maxHogs = [[split objectAtIndex:1] intValue]; |
|
245 NSString *max = [[NSString alloc] initWithFormat:@"%d",maxHogs]; |
|
246 self.maxLabel.text = max; |
|
247 [max release]; |
|
248 } |
70 } |
249 |
71 |
250 -(void) turnOffWidgets { |
72 -(void) turnOffWidgets { |
251 busy = YES; |
73 busy = YES; |
252 self.previewButton.alpha = 0.5f; |
74 self.previewButton.alpha = 0.5f; |
260 self.previewButton.alpha = 1.0f; |
82 self.previewButton.alpha = 1.0f; |
261 self.previewButton.enabled = YES; |
83 self.previewButton.enabled = YES; |
262 self.segmentedControl.enabled = YES; |
84 self.segmentedControl.enabled = YES; |
263 self.slider.enabled = YES; |
85 self.slider.enabled = YES; |
264 busy = NO; |
86 busy = NO; |
265 |
|
266 UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)[self.previewButton viewWithTag:INDICATOR_TAG]; |
|
267 if (indicator) { |
|
268 [indicator stopAnimating]; |
|
269 [indicator removeFromSuperview]; |
|
270 } |
|
271 } |
87 } |
272 |
88 |
273 -(void) setLabelText:(NSString *)str { |
89 -(void) setLabelText:(NSString *)str { |
|
90 self.maxHogs = [str intValue]; |
274 self.maxLabel.text = str; |
91 self.maxLabel.text = str; |
275 } |
92 } |
276 |
93 |
277 -(void) setButtonImage:(UIImage *)img { |
94 -(NSDictionary *)getDataForEngine { |
278 [self.previewButton setBackgroundImage:img forState:UIControlStateNormal]; |
95 NSDictionary *dictForEngine = [NSDictionary dictionaryWithObjectsAndKeys: |
279 } |
96 self.seedCommand,@"seedCommand", |
280 |
97 self.templateFilterCommand,@"templateFilterCommand", |
281 -(void) restoreBackgroundImage { |
98 self.mapGenCommand,@"mapGenCommand", |
282 // white rounded rectangle as background image for previewButton |
99 self.mazeSizeCommand,@"mazeSizeCommand", |
283 UIGraphicsBeginImageContext(CGSizeMake(256,128)); |
100 nil]; |
284 CGContextRef context = UIGraphicsGetCurrentContext(); |
101 return dictForEngine; |
285 UIGraphicsPushContext(context); |
|
286 |
|
287 CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); |
|
288 CGContextFillRect(context,CGRectMake(0,0,256,128)); |
|
289 |
|
290 UIGraphicsPopContext(); |
|
291 UIImage *bkgImg = UIGraphicsGetImageFromCurrentImageContext(); |
|
292 UIGraphicsEndImageContext(); |
|
293 [self.previewButton setBackgroundImage:[bkgImg makeRoundCornersOfSize:CGSizeMake(12, 12)] forState:UIControlStateNormal]; |
|
294 } |
102 } |
295 |
103 |
296 #pragma mark - |
104 #pragma mark - |
297 #pragma mark Table view data source |
105 #pragma mark Table view data source |
298 -(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { |
106 -(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { |
338 |
146 |
339 cell.backgroundColor = [UIColor blackColor]; |
147 cell.backgroundColor = [UIColor blackColor]; |
340 return cell; |
148 return cell; |
341 } |
149 } |
342 |
150 |
|
151 // this set details for a static map (called by didSelectRowAtIndexPath) |
|
152 -(void) setDetailsForStaticMap:(NSInteger) index { |
|
153 // update label |
|
154 maxHogs = 18; |
|
155 NSString *fileCfg = [[NSString alloc] initWithFormat:@"%@/%@/map.cfg", MAPS_DIRECTORY(),[self.mapArray objectAtIndex:index]]; |
|
156 NSString *contents = [[NSString alloc] initWithContentsOfFile:fileCfg encoding:NSUTF8StringEncoding error:NULL]; |
|
157 [fileCfg release]; |
|
158 NSArray *split = [contents componentsSeparatedByString:@"\n"]; |
|
159 [contents release]; |
|
160 |
|
161 // set the theme and map here |
|
162 self.themeCommand = [NSString stringWithFormat:@"etheme %@", [split objectAtIndex:0]]; |
|
163 self.staticMapCommand = [NSString stringWithFormat:@"emap %@", [self.mapArray objectAtIndex:index]]; |
|
164 |
|
165 // if the number is not set we keep 18 standard; |
|
166 // sometimes it's not set but there are trailing characters, we get around them with the second equation |
|
167 if ([split count] > 1 && [[split objectAtIndex:1] intValue] > 0) |
|
168 maxHogs = [[split objectAtIndex:1] intValue]; |
|
169 NSString *max = [[NSString alloc] initWithFormat:@"%d",maxHogs]; |
|
170 self.maxLabel.text = max; |
|
171 [max release]; |
|
172 } |
343 |
173 |
344 #pragma mark - |
174 #pragma mark - |
345 #pragma mark Table view delegate |
175 #pragma mark Table view delegate |
346 -(void) tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { |
176 -(void) tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { |
347 int newRow = [indexPath row]; |
177 int newRow = [indexPath row]; |
348 int oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1; |
178 int oldRow = (lastIndexPath != nil) ? [lastIndexPath row] : -1; |
349 |
179 |
350 if (newRow != oldRow) { |
180 if (newRow != oldRow) { |
|
181 [self.tableView reloadData]; |
351 if (self.segmentedControl.selectedSegmentIndex != 1) { |
182 if (self.segmentedControl.selectedSegmentIndex != 1) { |
352 NSString *theme = [self.themeArray objectAtIndex:newRow]; |
183 // just change the theme, don't update preview |
353 self.themeCommand = [NSString stringWithFormat:@"etheme %@", theme]; |
184 self.themeCommand = [NSString stringWithFormat:@"etheme %@", [self.themeArray objectAtIndex:newRow]]; |
354 } else { |
185 } else { |
355 // theme and map are set in the function below |
186 NSString *fileImage = [NSString stringWithFormat:@"%@/%@/preview.png",MAPS_DIRECTORY(),[self.mapArray objectAtIndex:newRow]]; |
356 [self updatePreviewWithMap:newRow]; |
187 [self.previewButton updatePreviewWithFile:fileImage]; |
|
188 [self setDetailsForStaticMap:newRow]; |
357 } |
189 } |
358 |
190 |
359 UITableViewCell *newCell = [aTableView cellForRowAtIndexPath:indexPath]; |
191 UITableViewCell *newCell = [aTableView cellForRowAtIndexPath:indexPath]; |
360 UIImageView *checkbox = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:@"checkbox.png"]]; |
192 UIImageView *checkbox = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:@"checkbox.png"]]; |
361 newCell.accessoryView = checkbox; |
193 newCell.accessoryView = checkbox; |