163 LandPixels[y div 2, x div 2]:= c |
163 LandPixels[y div 2, x div 2]:= c |
164 end; |
164 end; |
165 end |
165 end |
166 end; |
166 end; |
167 |
167 |
|
168 procedure ColorizeLandFast(mapsurf: PSDL_Surface); |
|
169 var ltexsurf: PSDL_Surface; |
|
170 i: LongInt; |
|
171 ltlnp, srcp, dstp, stopp: Pointer; |
|
172 c: SizeInt; |
|
173 begin |
|
174 ltexsurf:= LoadDataImage(ptCurrTheme, 'LandTex', ifCritical or ifIgnoreCaps); |
|
175 |
|
176 // pointer to current line of ltexsurf pixels. will be moved from line to line |
|
177 ltlnp:= ltexsurf^.pixels; |
|
178 // pointer to mapsurf pixels. will jump forward after every move() |
|
179 dstp:= mapsurf^.pixels; |
|
180 |
|
181 // time to get serious |
|
182 SDL_LockSurface(mapsurf); |
|
183 SDL_LockSurface(ltexsurf); |
|
184 |
|
185 // for now only fill a row with the height of landtex. do vertical copies within mapsurf after |
|
186 |
|
187 // do this loop for each line of ltexsurf (unless we run out of map height first) |
|
188 for i:= 1 to min(ltexsurf^.h, mapsurf^.h) do |
|
189 begin |
|
190 // amount of pixels to write in first move() |
|
191 c:= ltexsurf^.pitch; |
|
192 |
|
193 // protect from odd cases where landtex wider than map |
|
194 if c > mapsurf^.pitch then |
|
195 c:= mapsurf^.pitch; |
|
196 |
|
197 // write line of landtex to mapsurf |
|
198 move(ltlnp^, dstp^, c); |
|
199 |
|
200 // fill the rest of the line by copying left-to-right until full |
|
201 |
|
202 // new src is start of line that we've just written to |
|
203 srcp:= dstp; |
|
204 // set stop pointer to start of next pixel line of mapsurf |
|
205 stopp:= dstp + mapsurf^.pitch; |
|
206 // move dst pointer to after what we've just written |
|
207 inc(dstp, c); |
|
208 |
|
209 // loop until dstp went past end of line |
|
210 while dstp < stopp do |
|
211 begin |
|
212 // copy all from left of dstp to right of it (or just fill the gap if smaller) |
|
213 c:= min(dstp-srcp, stopp-dstp); |
|
214 move(srcp^, dstp^, c); |
|
215 inc(dstp, c); |
|
216 end; |
|
217 |
|
218 // move to next line in ltexsurf |
|
219 inc(ltlnp, ltexsurf^.pitch); |
|
220 end; |
|
221 |
|
222 // we don't need ltexsurf itself anymore -> cleanup |
|
223 ltlnp:= nil; |
|
224 SDL_UnlockSurface(ltexsurf); |
|
225 SDL_FreeSurface(ltexsurf); |
|
226 ltexsurf:= nil; |
|
227 |
|
228 // from now on only copy pixels within mapsurf |
|
229 |
|
230 // copy all the already written lines at once for that get number of written bytes so far |
|
231 // already written pixels are between start and current dstp |
|
232 srcp:= mapsurf^.pixels; |
|
233 |
|
234 // first byte after end of pixels |
|
235 stopp:= srcp + (mapsurf^.pitch * mapsurf^.h); |
|
236 |
|
237 while dstp < stopp do |
|
238 begin |
|
239 // copy all from before dstp to after (or just fill the gap if smaller) |
|
240 c:= min(dstp-srcp, stopp-dstp); |
|
241 // worried about size of c with humongous maps? don't be: |
|
242 // the OS wouldn't have allowed allocation of object with size > max of SizeInt anyway |
|
243 move(srcp^, dstp^, c); |
|
244 inc(dstp, c); |
|
245 end; |
|
246 |
|
247 // cleanup |
|
248 srcp:= nil; |
|
249 dstp:= nil; |
|
250 stopp:= nil; |
|
251 SDL_UnlockSurface(mapsurf); |
|
252 |
|
253 // freed in freeModule() below |
|
254 LandBackSurface:= LoadDataImage(ptCurrTheme, 'LandBackTex', ifIgnoreCaps or ifColorKey); |
|
255 if (LandBackSurface <> nil) and GrayScale then Surface2GrayScale(LandBackSurface); |
|
256 end; |
|
257 |
168 procedure ColorizeLand(Surface: PSDL_Surface); |
258 procedure ColorizeLand(Surface: PSDL_Surface); |
169 var tmpsurf: PSDL_Surface; |
259 var tmpsurf: PSDL_Surface; |
170 r: TSDL_Rect; |
260 r: TSDL_Rect; |
171 y: LongInt; // stupid SDL 1.2 uses stupid SmallInt for y which limits us to 32767. But is even worse if LandTex is large, can overflow on 32767 map. |
261 y: LongInt; // stupid SDL 1.2 uses stupid SmallInt for y which limits us to 32767. But is even worse if LandTex is large, can overflow on 32767 map. |
172 begin |
262 begin |
275 AddProgress(); |
365 AddProgress(); |
276 |
366 |
277 tmpsurf:= SDL_CreateRGBSurface(SDL_SWSURFACE, LAND_WIDTH, LAND_HEIGHT, 32, RMask, GMask, BMask, AMask); |
367 tmpsurf:= SDL_CreateRGBSurface(SDL_SWSURFACE, LAND_WIDTH, LAND_HEIGHT, 32, RMask, GMask, BMask, AMask); |
278 |
368 |
279 if checkFails(tmpsurf <> nil, 'Error creating pre-land surface', true) then exit; |
369 if checkFails(tmpsurf <> nil, 'Error creating pre-land surface', true) then exit; |
280 ColorizeLand(tmpsurf); |
370 ColorizeLandFast(tmpsurf); |
281 if gameFlags and gfShoppaBorder = 0 then DrawBorderFromImage(tmpsurf); |
371 if gameFlags and gfShoppaBorder = 0 then DrawBorderFromImage(tmpsurf); |
282 AddOnLandObjects(tmpsurf); |
372 AddOnLandObjects(tmpsurf); |
283 |
373 |
284 LandSurface2LandPixels(tmpsurf); |
374 LandSurface2LandPixels(tmpsurf); |
285 SDL_FreeSurface(tmpsurf); |
375 SDL_FreeSurface(tmpsurf); |
452 mirror:= (i <> 0) and (mirror or (i = ClansCount - 1)); |
542 mirror:= (i <> 0) and (mirror or (i = ClansCount - 1)); |
453 |
543 |
454 if mirror then |
544 if mirror then |
455 begin |
545 begin |
456 // not critical because if no R we can fallback to mirrored L |
546 // not critical because if no R we can fallback to mirrored L |
457 tmpsurf:= LoadDataImage(ptForts, SpawnClansArray[i]^.Teams[0]^.FortName + 'R', ifAlpha or ifTransparent or ifIgnoreCaps); |
547 tmpsurf:= LoadDataImage(ptForts, SpawnClansArray[i]^.Teams[0]^.FortName + 'R', ifAlpha or ifColorKey or ifIgnoreCaps); |
458 // fallback |
548 // fallback |
459 if tmpsurf = nil then |
549 if tmpsurf = nil then |
460 begin |
550 begin |
461 tmpsurf:= LoadDataImage(ptForts, SpawnClansArray[i]^.Teams[0]^.FortName + 'L', ifAlpha or ifCritical or ifTransparent or ifIgnoreCaps); |
551 tmpsurf:= LoadDataImage(ptForts, SpawnClansArray[i]^.Teams[0]^.FortName + 'L', ifAlpha or ifCritical or ifColorKey or ifIgnoreCaps); |
462 BlitImageAndGenerateCollisionInfo(leftX + sectionWidth * i + ((sectionWidth - tmpsurf^.w) div 2), LAND_HEIGHT - tmpsurf^.h, tmpsurf^.w, tmpsurf, 0, true); |
552 BlitImageAndGenerateCollisionInfo(leftX + sectionWidth * i + ((sectionWidth - tmpsurf^.w) div 2), LAND_HEIGHT - tmpsurf^.h, tmpsurf^.w, tmpsurf, 0, true); |
463 end |
553 end |
464 else |
554 else |
465 BlitImageAndGenerateCollisionInfo(leftX + sectionWidth * i + ((sectionWidth - tmpsurf^.w) div 2), LAND_HEIGHT - tmpsurf^.h, tmpsurf^.w, tmpsurf); |
555 BlitImageAndGenerateCollisionInfo(leftX + sectionWidth * i + ((sectionWidth - tmpsurf^.w) div 2), LAND_HEIGHT - tmpsurf^.h, tmpsurf^.w, tmpsurf); |
466 SDL_FreeSurface(tmpsurf); |
556 SDL_FreeSurface(tmpsurf); |
467 end |
557 end |
468 else |
558 else |
469 begin |
559 begin |
470 tmpsurf:= LoadDataImage(ptForts, SpawnClansArray[i]^.Teams[0]^.FortName + 'L', ifAlpha or ifCritical or ifTransparent or ifIgnoreCaps); |
560 tmpsurf:= LoadDataImage(ptForts, SpawnClansArray[i]^.Teams[0]^.FortName + 'L', ifAlpha or ifCritical or ifColorKey or ifIgnoreCaps); |
471 BlitImageAndGenerateCollisionInfo(leftX + sectionWidth * i + ((sectionWidth - tmpsurf^.w) div 2), LAND_HEIGHT - tmpsurf^.h, tmpsurf^.w, tmpsurf); |
561 BlitImageAndGenerateCollisionInfo(leftX + sectionWidth * i + ((sectionWidth - tmpsurf^.w) div 2), LAND_HEIGHT - tmpsurf^.h, tmpsurf^.w, tmpsurf); |
472 SDL_FreeSurface(tmpsurf); |
562 SDL_FreeSurface(tmpsurf); |
473 end; |
563 end; |
474 |
564 |
475 end; |
565 end; |
505 var tmpsurf: PSDL_Surface; |
595 var tmpsurf: PSDL_Surface; |
506 p: PLongwordArray; |
596 p: PLongwordArray; |
507 x, y, cpX, cpY: Longword; |
597 x, y, cpX, cpY: Longword; |
508 mapName: shortstring; |
598 mapName: shortstring; |
509 begin |
599 begin |
510 tmpsurf:= LoadDataImage(ptMapCurrent, 'mask', ifAlpha or ifTransparent or ifIgnoreCaps); |
600 tmpsurf:= LoadDataImage(ptMapCurrent, 'mask', ifAlpha or ifColorKey or ifIgnoreCaps); |
511 if tmpsurf = nil then |
601 if tmpsurf = nil then |
512 begin |
602 begin |
513 mapName:= ExtractFileName(cPathz[ptMapCurrent]); |
603 mapName:= ExtractFileName(cPathz[ptMapCurrent]); |
514 tmpsurf:= LoadDataImage(ptMissionMaps, mapName + '/mask', ifAlpha or ifTransparent or ifIgnoreCaps); |
604 tmpsurf:= LoadDataImage(ptMissionMaps, mapName + '/mask', ifAlpha or ifColorKey or ifIgnoreCaps); |
515 end; |
605 end; |
516 |
606 |
517 |
607 |
518 if (tmpsurf <> nil) and (tmpsurf^.format^.BytesPerPixel = 4) then |
608 if (tmpsurf <> nil) and (tmpsurf^.format^.BytesPerPixel = 4) then |
519 begin |
609 begin |
547 if SDL_MustLock(tmpsurf) then |
637 if SDL_MustLock(tmpsurf) then |
548 SDL_UnlockSurface(tmpsurf); |
638 SDL_UnlockSurface(tmpsurf); |
549 if not disableLandBack then |
639 if not disableLandBack then |
550 begin |
640 begin |
551 // freed in freeModule() below |
641 // freed in freeModule() below |
552 LandBackSurface:= LoadDataImage(ptCurrTheme, 'LandBackTex', ifIgnoreCaps or ifTransparent); |
642 LandBackSurface:= LoadDataImage(ptCurrTheme, 'LandBackTex', ifIgnoreCaps or ifColorKey); |
553 if (LandBackSurface <> nil) and GrayScale then |
643 if (LandBackSurface <> nil) and GrayScale then |
554 Surface2GrayScale(LandBackSurface) |
644 Surface2GrayScale(LandBackSurface) |
555 end; |
645 end; |
556 end; |
646 end; |
557 end; |
647 end; |
564 var tmpsurf: PSDL_Surface; |
654 var tmpsurf: PSDL_Surface; |
565 mapName: shortstring = ''; |
655 mapName: shortstring = ''; |
566 begin |
656 begin |
567 WriteLnToConsole('Loading land from file...'); |
657 WriteLnToConsole('Loading land from file...'); |
568 AddProgress; |
658 AddProgress; |
569 tmpsurf:= LoadDataImage(ptMapCurrent, 'map', ifAlpha or ifTransparent or ifIgnoreCaps); |
659 tmpsurf:= LoadDataImage(ptMapCurrent, 'map', ifAlpha or ifColorKey or ifIgnoreCaps); |
570 if tmpsurf = nil then |
660 if tmpsurf = nil then |
571 begin |
661 begin |
572 mapName:= ExtractFileName(cPathz[ptMapCurrent]); |
662 mapName:= ExtractFileName(cPathz[ptMapCurrent]); |
573 tmpsurf:= LoadDataImage(ptMissionMaps, mapName + '/map', ifAlpha or ifCritical or ifTransparent or ifIgnoreCaps); |
663 tmpsurf:= LoadDataImage(ptMissionMaps, mapName + '/map', ifAlpha or ifCritical or ifColorKey or ifIgnoreCaps); |
574 if not allOK then exit; |
664 if not allOK then exit; |
575 end; |
665 end; |
576 // (bare) Sanity check. Considering possible LongInt comparisons as well as just how much system memoery it would take |
666 // (bare) Sanity check. Considering possible LongInt comparisons as well as just how much system memoery it would take |
577 if checkFails((tmpsurf^.w < $40000000) and (tmpsurf^.h < $40000000) and (QWord(tmpsurf^.w) * tmpsurf^.h < 6*1024*1024*1024), 'Map dimensions too big!', true) |
667 if checkFails((tmpsurf^.w < $40000000) and (tmpsurf^.h < $40000000) and (QWord(tmpsurf^.w) * tmpsurf^.h < 6*1024*1024*1024), 'Map dimensions too big!', true) |
578 then exit; |
668 then exit; |
597 |
687 |
598 if allOK then LoadMask; |
688 if allOK then LoadMask; |
599 end; |
689 end; |
600 |
690 |
601 procedure DrawBottomBorder; // broken out from other borders for doing a floor-only map, or possibly updating bottom during SD |
691 procedure DrawBottomBorder; // broken out from other borders for doing a floor-only map, or possibly updating bottom during SD |
602 var x, w, c: Longword; |
692 var x, w, c, y: Longword; |
603 begin |
693 begin |
604 for w:= 0 to 23 do |
694 for w:= 0 to 23 do |
605 for x:= leftX to rightX do |
695 for x:= leftX to rightX do |
606 begin |
696 begin |
607 Land[Longword(cWaterLine) - 1 - w, x]:= lfIndestructible; |
697 y:= Longword(cWaterLine) - 1 - w; |
608 if (x + w) mod 32 < 16 then |
698 Land[y, x]:= lfIndestructible; |
|
699 if (x + y) mod 32 < 16 then |
609 c:= AMask |
700 c:= AMask |
610 else |
701 else |
611 c:= AMask or RMask or GMask; // FF00FFFF |
702 c:= AMask or RMask or GMask; // FF00FFFF |
612 |
703 |
613 if (cReducedQuality and rqBlurryLand) = 0 then |
704 if (cReducedQuality and rqBlurryLand) = 0 then |
614 LandPixels[Longword(cWaterLine) - 1 - w, x]:= c |
705 LandPixels[y, x]:= c |
615 else |
706 else |
616 LandPixels[(Longword(cWaterLine) - 1 - w) div 2, x div 2]:= c |
707 LandPixels[y div 2, x div 2]:= c |
617 end |
708 end |
618 end; |
709 end; |
619 |
710 |
620 procedure GenMap; |
711 procedure GenMap; |
621 var x, y, w, c: Longword; |
712 var x, y, w, c, c2: Longword; |
622 map, mask: shortstring; |
713 map, mask: shortstring; |
623 begin |
714 begin |
624 hasBorder:= false; |
715 hasBorder:= false; |
625 maskOnly:= false; |
716 maskOnly:= false; |
626 |
717 |
628 |
719 |
629 // is this not needed any more? lets hope setlength sets also 0s |
720 // is this not needed any more? lets hope setlength sets also 0s |
630 //if ((GameFlags and gfForts) <> 0) or (Pathz[ptMapCurrent] <> '') then |
721 //if ((GameFlags and gfForts) <> 0) or (Pathz[ptMapCurrent] <> '') then |
631 // FillChar(Land,SizeOf(TCollisionArray),0);*) |
722 // FillChar(Land,SizeOf(TCollisionArray),0);*) |
632 |
723 |
633 if (GameFlags and gfForts) = 0 then |
724 if cPathz[ptMapCurrent] <> '' then |
634 if cPathz[ptMapCurrent] <> '' then |
725 begin |
635 begin |
726 map:= cPathz[ptMapCurrent] + '/map.png'; |
636 map:= cPathz[ptMapCurrent] + '/map.png'; |
727 mask:= cPathz[ptMapCurrent] + '/mask.png'; |
637 mask:= cPathz[ptMapCurrent] + '/mask.png'; |
728 if (not(pfsExists(map)) and pfsExists(mask)) then |
638 if (not(pfsExists(map)) and pfsExists(mask)) then |
729 begin |
639 begin |
730 maskOnly:= true; |
640 maskOnly:= true; |
731 LoadMask; |
641 LoadMask; |
732 GenLandSurface |
642 GenLandSurface |
|
643 end |
|
644 else LoadMap; |
|
645 end |
733 end |
|
734 else LoadMap; |
|
735 end |
|
736 else |
|
737 begin |
|
738 WriteLnToConsole('Generating land...'); |
|
739 case cMapGen of |
|
740 mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]); |
|
741 mgMaze : begin ResizeLand(4096,2048); GenMaze; end; |
|
742 mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end; |
|
743 mgDrawn : GenDrawnMap; |
|
744 mgForts : begin GameFlags:= (GameFlags or gfForts or gfDivideTeams); MakeFortsMap(); end; |
646 else |
745 else |
647 begin |
746 OutError('Unknown mapgen', true); |
648 WriteLnToConsole('Generating land...'); |
747 end; |
649 case cMapGen of |
748 if cMapGen <> mgForts then |
650 mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]); |
749 GenLandSurface |
651 mgMaze : begin ResizeLand(4096,2048); GenMaze; end; |
750 end; |
652 mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end; |
|
653 mgDrawn : GenDrawnMap; |
|
654 mgForts : begin GameFlags:= (GameFlags or gfForts or gfDivideTeams); MakeFortsMap(); end; |
|
655 else |
|
656 OutError('Unknown mapgen', true); |
|
657 end; |
|
658 if cMapGen <> mgForts then |
|
659 GenLandSurface |
|
660 end |
|
661 else |
|
662 MakeFortsMap; |
|
663 |
751 |
664 AddProgress; |
752 AddProgress; |
665 |
753 |
666 // check for land near top |
754 // check for land near top |
667 c:= 0; |
755 c:= 0; |
702 if (WorldEdge <> weBounce) and (WorldEdge <> weWrap) then |
790 if (WorldEdge <> weBounce) and (WorldEdge <> weWrap) then |
703 for y:= topY to LAND_HEIGHT - 1 do |
791 for y:= topY to LAND_HEIGHT - 1 do |
704 begin |
792 begin |
705 Land[y, leftX + w]:= lfIndestructible; |
793 Land[y, leftX + w]:= lfIndestructible; |
706 Land[y, rightX - w]:= lfIndestructible; |
794 Land[y, rightX - w]:= lfIndestructible; |
707 if (y + w) mod 32 < 16 then |
795 if (y + leftX + w) mod 32 < 16 then |
708 c:= AMask |
796 c:= AMask |
709 else |
797 else |
710 c:= AMask or RMask or GMask; // FF00FFFF |
798 c:= AMask or RMask or GMask; // FF00FFFF |
|
799 if (y + rightX - w) mod 32 < 16 then |
|
800 c2:= AMask |
|
801 else |
|
802 c2:= AMask or RMask or GMask; // FF00FFFF |
711 |
803 |
712 if (cReducedQuality and rqBlurryLand) = 0 then |
804 if (cReducedQuality and rqBlurryLand) = 0 then |
713 begin |
805 begin |
714 LandPixels[y, leftX + w]:= c; |
806 LandPixels[y, leftX + w]:= c; |
715 LandPixels[y, rightX - w]:= c; |
807 LandPixels[y, rightX - w]:= c2; |
716 end |
808 end |
717 else |
809 else |
718 begin |
810 begin |
719 LandPixels[y div 2, (leftX + w) div 2]:= c; |
811 LandPixels[y div 2, (leftX + w) div 2]:= c; |
720 LandPixels[y div 2, (rightX - w) div 2]:= c; |
812 LandPixels[y div 2, (rightX - w) div 2]:= c2; |
721 end; |
813 end; |
722 end; |
814 end; |
723 |
815 |
724 for x:= leftX to rightX do |
816 for x:= leftX to rightX do |
725 begin |
817 begin |
884 begin |
976 begin |
885 AddFileLog('CheckLandDigest: ' + s + ' digest : ' + digest); |
977 AddFileLog('CheckLandDigest: ' + s + ' digest : ' + digest); |
886 if digest = '' then |
978 if digest = '' then |
887 digest:= s |
979 digest:= s |
888 else |
980 else |
889 checkFails(s = digest, 'Different maps generated, sorry', true); |
981 checkFails(s = digest, 'Different map or critical resources loaded, sorry', true); |
890 end; |
982 end; |
891 |
983 |
892 procedure chSendLandDigest(var s: shortstring); |
984 procedure chSendLandDigest(var s: shortstring); |
893 var adler, i: LongInt; |
985 var i: LongInt; |
894 begin |
986 begin |
895 adler:= 1; |
|
896 for i:= 0 to LAND_HEIGHT-1 do |
987 for i:= 0 to LAND_HEIGHT-1 do |
897 adler:= Adler32Update(adler, @Land[i,0], LAND_WIDTH); |
988 syncedPixelDigest:= Adler32Update(syncedPixelDigest, @Land[i,0], LAND_WIDTH*2); |
898 s:= 'M' + IntToStr(adler) + cScriptName; |
989 s:= 'M' + IntToStr(syncedPixelDigest); // + cScriptName; script name is no longer needed. scripts are hashed |
899 |
990 |
900 ScriptSetString('LandDigest', s); |
991 ScriptSetString('LandDigest', s); |
901 |
992 |
902 chLandCheck(s); |
993 chLandCheck(s); |
903 if allOK then SendIPCRaw(@s[0], Length(s) + 1) |
994 if allOK then SendIPCRaw(@s[0], Length(s) + 1) |