/ComHilfe.pas |
---|
34,7 → 34,7 |
DOC: Variant; |
begin |
if not Assigned(WebBrowser1.Document) then |
WebBrowser1.Navigate('about:blank'); |
WebBrowser1.Navigate('about:blank'); // do not localize |
DOC := WebBrowser1.Document; |
DOC.Clear; |
53,7 → 53,7 |
slCss := TStringList.Create(); |
try |
slHtml.LoadFromFile(AMarkDownFile); |
cssFile := IncludeTrailingPathDelimiter(FDirectory) + 'Style.css'; |
cssFile := IncludeTrailingPathDelimiter(FDirectory) + 'Style.css'; // do not localize |
if FileExists(cssFile) then |
slCss.LoadFromFile(cssFile); |
md := TMarkdownProcessor.CreateDialect(mdCommonMark); |
60,15 → 60,15 |
try |
//md.AllowUnsafe := true; |
ShowHTMLHelp( |
'<html>'+ |
'<head>'+ |
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'+ |
'<style>'+slCss.Text+'</style>'+ |
'</head>'+ |
'<body>'+ |
'<html>'+ // do not localize |
'<head>'+ // do not localize |
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'+ // do not localize |
'<style>'+slCss.Text+'</style>'+ // do not localize |
'</head>'+ // do not localize |
'<body>'+ // do not localize |
md.process(UTF8ToString(RawByteString(slHtml.Text)))+ |
'</body>'+ |
'</html>'); |
'</body>'+ // do not localize |
'</html>'); // do not localize |
finally |
FreeAndNil(md); |
end; |
82,17 → 82,17 |
const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData, |
Headers: OleVariant; var Cancel: WordBool); |
begin |
if SameText(Copy(URL,1,7),'http://') or |
SameText(Copy(URL,1,8),'https://') or |
SameText(Copy(URL,1,7),'mailto:') then |
if SameText(Copy(URL,1,7),'http://') or // do not localize |
SameText(Copy(URL,1,8),'https://') or // do not localize |
SameText(Copy(URL,1,7),'mailto:') then // do not localize |
begin |
// Links in default Browser anzeigen |
ShellExecute(handle, 'open', PChar(string(URL)), '', '', SW_NORMAL); |
ShellExecute(handle, 'open', PChar(string(URL)), '', '', SW_NORMAL); // do not localize |
Cancel := true; |
end |
else if SameText(ExtractFileExt(URL), '.md') then |
else if SameText(ExtractFileExt(URL), '.md') then // do not localize |
begin |
if SameText(Copy(URL,1,6), 'about:') then |
if SameText(Copy(URL,1,6), 'about:') then // do not localize |
ShowMarkDownHelp(IncludeTrailingPathDelimiter(FDirectory) + Copy(URL,7,Length(URL))) |
else |
ShowMarkDownHelp(URL); |
/ComInfo.pas |
---|
42,18 → 42,20 |
end; |
procedure TInfoForm.FormCreate(Sender: TObject); |
resourcestring |
SVersion = 'Version %s'; |
begin |
VersionLbl.caption := 'Version ' + ProgramVersion; |
VersionLbl.caption := Format(SVersion, [ProgramVersion]); |
end; |
procedure TInfoForm.WebsiteClick(Sender: TObject); |
begin |
ShellExecute(application.Handle, 'open', pchar('https://'+url2.caption+'/'), nil, nil, SW_SHOW); |
ShellExecute(application.Handle, 'open', pchar('https://'+url2.caption+'/'), nil, nil, SW_SHOW); // do not localize |
end; |
procedure TInfoForm.EMailClick(Sender: TObject); |
begin |
ShellExecute(application.Handle, 'open', pchar('mailto:'+url1.Caption+'?subject=SpaceMission ' + ProgramVersion), nil, nil, SW_SHOW); |
ShellExecute(application.Handle, 'open', pchar('mailto:'+url1.Caption+'?subject=SpaceMission ' + ProgramVersion), nil, nil, SW_SHOW); // do not localize |
end; |
end. |
/ComLevelReader.pas |
---|
11,7 → 11,7 |
// - GamMain.pas : TMainForm.SceneMain |
// - LevMain.pas : * GUI |
// * TMainForm.SelectedEnemyType |
// * TEnemy.Create |
// * TEnemyOrItem.Create |
// * TMainForm.DXDrawMouseMove |
// * TMainForm.DXDrawMouseDown |
TEnemyType = ( |
96,14 → 96,19 |
const |
// { iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) 37476 products(2) spacemission(8) file-format(1) lev-sav-v12(1) } |
// https://hosted.oidplus.com/viathinksoft/?goto=oid%3A1.3.6.1.4.1.37476.2.8.1.1 |
OID_LEVSAV_VER12 = '1.3.6.1.4.1.37476.2.8.1.1'; |
OID_LEVSAV_VER12 = '1.3.6.1.4.1.37476.2.8.1.1'; // do not localize |
resourcestring |
SLevelFileFolder = 'Levels'; |
SLevelFileSubFolder = 'SpaceMission'; |
SExtraContentAfterLine = 'Zeile %d ist ungültig (Zusatzinfo am Ende)'; |
function GetLevelFileName(lev: integer; forceuserdir: boolean): TLevelFile; |
function _GetLevelVerzeichnisSystem: string; |
begin |
// Für die Auslieferungs-Levels |
result := OwnDirectory + 'Levels'; |
result := OwnDirectory + SLevelFileFolder; |
end; |
function _GetLevelVerzeichnisUser: string; |
116,12 → 121,12 |
if result = '' then |
begin |
// Pre Vista |
result := OwnDirectory + 'Levels'; |
result := OwnDirectory + SLevelFileFolder; |
end |
else |
begin |
result := IncludeTrailingPathDelimiter(result); |
result := result + 'SpaceMission'; |
result := result + SLevelFileSubFolder; |
end; |
result := IncludeTrailingPathDelimiter(result); |
ForceDirectories(result); |
131,8 → 136,8 |
var |
old, new: string; |
begin |
new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files |
old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files |
new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files // do not localize |
old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files // do not localize |
if fileexists(new) then exit(new); |
if fileexists(old) then exit(old); |
exit(new); |
142,8 → 147,8 |
var |
old, new: string; |
begin |
new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files |
old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files |
new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files // do not localize |
old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files // do not localize |
if fileexists(new) then exit(new); |
if fileexists(old) then exit(old); |
exit(new); |
282,6 → 287,9 |
end; |
procedure TLevelData.AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer); |
resourcestring |
SInvalidXCoord = 'X-Koordinate muss ohne Rest durch %d teilbar sein'; |
SInvalidYCoord = 'Y-Koordinate muss ohne Rest durch %d teilbar sein'; |
begin |
SetLength(EnemyAdventTable, Length(EnemyAdventTable)+1); |
288,8 → 296,8 |
if enemyType = etEnemyMeteor then lifes := 0; |
if RasterErzwingen then |
begin |
if x mod RasterW <> 0 then raise Exception.CreateFmt('X-Koordinate muss ohne Rest durch %d teilbar sein', [RasterW]); |
if y mod RasterH <> 0 then raise Exception.CreateFmt('Y-Koordinate muss ohne Rest durch %d teilbar sein', [RasterH]); |
if x mod LevEditRasterW <> 0 then raise Exception.CreateFmt(SInvalidXCoord, [LevEditRasterW]); |
if y mod LevEditRasterH <> 0 then raise Exception.CreateFmt(SInvalidYCoord, [LevEditRasterH]); |
end; |
if lifes > MaxPossibleEnemyLives then lifes := MaxPossibleEnemyLives; |
319,6 → 327,9 |
end; |
procedure TLevelData.LoadFromStrings(sl: TStrings); |
resourcestring |
SInvalidLevelFile = 'Level-Format nicht unterstützt oder Datei ist beschädigt'; |
SEnemyTypeNotImplemented = 'Enemy Type %d not implemented'; |
var |
curline: integer; |
z, act: integer; |
328,6 → 339,7 |
ergebnis: string; |
ary: TStringDynArray; |
sLine: string; |
iEnemy: Integer; |
begin |
Clear; |
357,8 → 369,8 |
if z > 2 then inc(act); |
if act = 5 then act := 1; |
ergebnis := sl.Strings[z-1]; |
if ergebnis = '; SpaceMission 0.4' then |
sl2.Add('; SpaceMission 1.0') |
if ergebnis = '; SpaceMission 0.4' then // do not localize |
sl2.Add('; SpaceMission 1.0') // do not localize |
else |
begin |
if (ergebnis = '30000') and (z = 3) then |
391,7 → 403,10 |
curline := 3; |
while curline < sl.Count do |
begin |
tmpEnemy := TEnemyType(strtoint(sl.Strings[curline])); |
iEnemy := strtoint(sl.Strings[curline]); |
if TEnemyType(iEnemy) = etUnknown then // <-- for some reason, etUnknown will also be set if iEnemy is too large. This is actually good! |
raise Exception.CreateFmt(SEnemyTypeNotImplemented, [iEnemy]); |
tmpEnemy := TEnemyType(iEnemy); |
Inc(curline); |
tmpX := strtoint(sl.Strings[curline]); |
Inc(curline); |
415,7 → 430,7 |
begin |
LevelEditorLength := StrToInt(ary[1]); |
if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then |
raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]); |
raise Exception.CreateFmt(SExtraContentAfterLine, [curline+1]); |
end |
else if SameText(ary[0], 'Name') then // do not localize |
begin |
427,12 → 442,15 |
end |
else if SameText(ary[0], 'Enemy') then // do not localize |
begin |
tmpEnemy := TEnemyType(strtoint(ary[1])); |
iEnemy := strtoint(ary[1]); |
if TEnemyType(iEnemy) = etUnknown then // <-- for some reason, etUnknown will also be set if iEnemy is too large. This is actually good! |
raise Exception.CreateFmt(SEnemyTypeNotImplemented, [iEnemy]); |
tmpEnemy := TEnemyType(iEnemy); |
tmpX := strtoint(ary[2]); |
tmpY := strtoint(ary[3]); |
tmpLifes := strtoint(ary[4]); |
if (Length(ary) > 5) and (Copy(ary[5], 1, 1) <> ';') then |
raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]); |
raise Exception.CreateFmt(SExtraContentAfterLine, [curline+1]); |
AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes); |
end; |
end; |
440,7 → 458,7 |
end |
else |
begin |
raise Exception.Create('Level-Format nicht unterstützt oder Datei ist beschädigt'); |
raise Exception.Create(SInvalidLevelFile); |
end; |
SortEnemies; // Sortierung nach X-Koordinate ist sehr wichtig für das Spiel! |
522,6 → 540,8 |
end; |
procedure TLevelData.SaveToStrings(sl: TStrings); |
resourcestring |
SLevelEnemyLineComment = '; Type XCoord YCoord Lives'; |
var |
i: integer; |
begin |
531,7 → 551,7 |
if LevelAuthor <> '' then sl.Add('Author ' + LevelAuthor); // do not localize |
sl.Add('Width ' + IntToStr(LevelEditorLength)); // do not localize |
SortEnemies; |
sl.Add('; Type XCoord YCoord Lives'); |
sl.Add(SLevelEnemyLineComment); |
for i := 0 to Length(EnemyAdventTable)-1 do |
begin |
sl.Add(Trim( |
645,6 → 665,8 |
end; |
procedure TSaveData.LoadFromStrings(sl: TStrings); |
resourcestring |
SInvalidFile = 'Spielstand-Format nicht unterstützt oder Datei beschädigt'; |
var |
curline: Integer; |
ary: TStringDynArray; |
674,25 → 696,25 |
begin |
Score := StrToInt(ary[1]); |
if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then |
raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]); |
raise Exception.CreateFmt(SExtraContentAfterLine, [curline+1]); |
end |
else if SameText(ary[0], 'Lives') then // do not localize |
begin |
Life := StrToInt(ary[1]); |
if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then |
raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]); |
raise Exception.CreateFmt(SExtraContentAfterLine, [curline+1]); |
end |
else if SameText(ary[0], 'Level') then // do not localize |
begin |
Level := StrToInt(ary[1]); |
if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then |
raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]); |
raise Exception.CreateFmt(SExtraContentAfterLine, [curline+1]); |
end |
else if SameText(ary[0], 'Mode') then // do not localize |
begin |
GameMode := TGameMode(StrToInt(ary[1])); |
if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then |
raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]); |
raise Exception.CreateFmt(SExtraContentAfterLine, [curline+1]); |
end; |
end; |
if Assigned(LevelData) then FreeAndNil(LevelData); |
702,7 → 724,7 |
end |
else |
begin |
raise Exception.Create('Spielstand-Format nicht unterstützt oder Datei beschädigt'); |
raise Exception.Create(SInvalidFile); |
end; |
end; |
/GamCheat.dfm |
---|
35,6 → 35,7 |
Font.Name = 'MS Sans Serif' |
Font.Style = [fsUnderline] |
ParentFont = False |
Visible = False |
OnClick = Label3Click |
end |
object CheatEdit: TEdit |
/GamCheat.pas |
---|
40,6 → 40,8 |
const |
// Cheat1 = 'Kmkjk'+#39+'Khyc'; {Johnny Cash} |
Cheat1 = #75+#109+#107+#106+#107+#127+#39+#75+#104+#121+#99; |
resourcestring |
Cheat1Text = 'Unendlich Leben!'; |
procedure TCheatForm.AbbBtnClick(Sender: TObject); |
54,6 → 56,10 |
end; |
procedure TCheatForm.OKBtnClick(Sender: TObject); |
resourcestring |
SCheatUnlocked = 'Dieser Cheat wurde freigeschaltet!'; |
SCheckAlreadyUnlocked = 'Dieser Cheat wurde bereits freigeschaltet!'; |
SNoCheat = 'Dies ist kein offizieller Cheat!'; |
var |
temp: string; |
i, j: integer; |
68,10 → 74,10 |
if lowercase(temp) = lowercase(Cheat1) then |
begin |
if mainform.FCheat then |
showmessage('Dieser Cheat wurde bereits freigeschaltet!') |
showmessage(SCheckAlreadyUnlocked) |
else |
begin |
showmessage('Dieser Cheat wurde freigeschaltet!'); |
showmessage(SCheatUnlocked); |
mainform.FCheat := true; |
SearchCheats; |
end; |
79,7 → 85,7 |
end |
else |
begin |
showmessage('Dies ist kein offizieller Cheat!'); |
showmessage(SNoCheat); |
CheatEdit.text := ''; |
CheatEdit.setfocus; |
end; |
108,15 → 114,18 |
end; |
procedure TCheatForm.Label2Click(Sender: TObject); |
resourcestring |
SDisableCheat = 'Diesen Cheat wirklich deaktivieren?'; |
SCheatDisabled = 'Dieser Cheat wurde deakiviert!'; |
begin |
if not CheatBox.items.IndexOf(Cheat1Text) = -1 then |
begin |
if CheatBox.Selected[CheatBox.items.IndexOf(Cheat1Text)] then |
begin |
if MessageDlg('Diesen Cheat wirklich deaktivieren?', mtConfirmation, mbYesNoCancel, 0) = mrYes then |
if MessageDlg(SDisableCheat, mtConfirmation, mbYesNoCancel, 0) = mrYes then |
begin |
mainform.FCheat := false; |
showmessage('Dieser Cheat wurde deakiviert!'); |
showmessage(SCheatDisabled); |
SearchCheats; |
end; |
end; |
125,7 → 134,7 |
procedure TCheatForm.Label3Click(Sender: TObject); |
begin |
shellexecute(handle, 'open', pchar('mailto:daniel-marschall@viathinksoft.de?subject=Cheats für SpaceMission '+ProgramVersion), '', '', 1); |
shellexecute(handle, 'open', pchar('mailto:daniel-marschall@viathinksoft.de?subject=Cheats für SpaceMission '+ProgramVersion), '', '', 1); // do not localize |
end; |
end. |
/GamMain.pas |
---|
1230,6 → 1230,8 |
{ TMainForm } |
procedure TMainForm.FormCreate(Sender: TObject); |
resourcestring |
SAppTitle = 'SpaceMission %s'; |
begin |
Randomize; |
1292,7 → 1294,7 |
{ Ende VCL-Ersatz } |
Application.Title := 'SpaceMission '+ProgramVersion; |
Application.Title := Format(SAppTitle, [ProgramVersion]); |
LoadOptions; |
DXInit; |
SoundInit; |
1351,9 → 1353,11 |
end; |
procedure TMainForm.DXInit; |
const |
DxgFile = 'DirectX\Graphics.dxg'; // do not localize |
begin |
try |
Imagelist.Items.LoadFromFile(OwnDirectory+'DirectX\Graphics.dxg'); |
Imagelist.Items.LoadFromFile(OwnDirectory+DxgFile); |
ImageList.Items.MakeColorTable; |
DXDraw.ColorTable := ImageList.Items.ColorTable; |
DXDraw.DefColorTable := ImageList.Items.ColorTable; |
1370,7 → 1374,7 |
procedure TMainForm.CheckUpdatesClick(Sender: TObject); |
begin |
CheckForUpdates('spacemission', ProgramVersion); |
CheckForUpdates('spacemission', ProgramVersion); // do not localize |
end; |
procedure TMainForm.BeendenClick(Sender: TObject); |
1386,8 → 1390,10 |
end; |
procedure TMainForm.SoundInit; |
const |
DxwFile = 'DirectX\Sound.dxw'; // do not localize |
begin |
if (WaveOutGetNumDevs < 1) or not FileExists(OwnDirectory+'DirectX\Sound.dxw') then |
if (WaveOutGetNumDevs < 1) or not FileExists(OwnDirectory+DxwFile) then |
begin |
OptionSound.Checked := false; |
OptionSound.Enabled := False; |
1400,7 → 1406,7 |
begin |
try |
DXSound.Initialize; |
WaveList.Items.LoadFromFile(OwnDirectory+'DirectX\Sound.dxw'); |
WaveList.Items.LoadFromFile(OwnDirectory+DxwFile); |
except |
OptionSound.enabled := False; |
WaveList.items.clear; |
1411,10 → 1417,12 |
end; |
procedure TMainForm.MusicInit; |
const |
DxmFile = 'DirectX\Music.dxm'; // do not localize |
var |
i: integer; |
begin |
if (WaveOutGetNumDevs < 1) or not FileExists(OwnDirectory+'DirectX\Music.dxm') then |
if (WaveOutGetNumDevs < 1) or not FileExists(OwnDirectory+DxmFile) then |
begin |
optionmusic.Checked := false; |
optionmusic.Enabled := False; |
1422,7 → 1430,7 |
end; |
try |
dxmusic.Midis.LoadFromFile(OwnDirectory+'DirectX\Music.dxm'); |
dxmusic.Midis.LoadFromFile(OwnDirectory+DxmFile); |
for i := 0 to dxmusic.Midis.Count-1 do |
begin |
if not dxmusic.Midis.Items[i].IsInitialized then |
1476,9 → 1484,11 |
end; |
procedure TMainForm.DXTimerDeactivate(Sender: TObject); |
resourcestring |
SPauseTitle = '%s [Pause]'; |
begin |
TDxTimer(Sender).Tag := TDxTimer(Sender).Tag + 1; |
Caption := Application.Title + ' [Pause]'; |
Caption := Format(SPauseTitle, [Application.Title]); |
PauseMusic(FMusic); |
end; |
1531,9 → 1541,9 |
Reg.RootKey := HKEY_CURRENT_USER; |
if Reg.OpenKey(RegistrySettingsKey, true) then |
begin |
Reg.WriteBool('Music', OptionMusic.checked); |
Reg.WriteBool('Sound', OptionSound.checked); |
Reg.WriteInteger('Speed', Ord(FInterval)); |
Reg.WriteBool(MusicSettingKey, OptionMusic.checked); |
Reg.WriteBool(SoundSettingKey, OptionSound.checked); |
Reg.WriteInteger(SpeedSettingKey, Ord(FInterval)); // TODO : maybe it would be better to let the user change the real speed, so they can set their own custom one! |
Reg.CloseKey; |
end; |
finally |
1550,18 → 1560,18 |
Reg.RootKey := HKEY_CURRENT_USER; |
if Reg.OpenKey(RegistrySettingsKey, true) then |
begin |
if Reg.ValueExists('Music') then |
optionmusic.checked := Reg.ReadBool('Music') |
if Reg.ValueExists(MusicSettingKey) then |
optionmusic.checked := Reg.ReadBool(MusicSettingKey) |
else |
optionmusic.checked := true; // default |
if Reg.ValueExists('Sound') then |
optionsound.checked := Reg.ReadBool('Sound') |
if Reg.ValueExists(SoundSettingKey) then |
optionsound.checked := Reg.ReadBool(SoundSettingKey) |
else |
optionsound.checked := true; // default |
if Reg.ValueExists('Speed') then |
FInterval := TGameInterval(Reg.ReadInteger('Speed')) |
if Reg.ValueExists(SpeedSettingKey) then |
FInterval := TGameInterval(Reg.ReadInteger(SpeedSettingKey)) |
else |
FInterval := giMittel; // default |
1846,7 → 1856,7 |
procedure TMainForm.NewLevel(lev: integer); |
resourcestring |
LNG_LEVEL_INVALID = 'Das Level Nr. %d ist ungültig!'+#13#10+'Das Programm wird beendet.'; |
SLevelInvalid = 'Das Level Nr. %d ist ungültig!'+#13#10+'Das Programm wird beendet.'; |
const |
RandomLevelMaxEnemyLives = 10; |
var |
1973,7 → 1983,7 |
LevelData.RasterErzwingen := false; |
LevelData.LoadFromFile(levFile.fileLocation); |
except |
showmessage(Format(LNG_LEVEL_INVALID, [lev])); |
showmessage(Format(SLevelInvalid, [lev])); |
ResetLevelData; |
end; |
end; |
2098,7 → 2108,7 |
FLevel := 1; |
if FGameMode = gmEditor then |
begin |
ShellExecute(0, 'open', PChar(OwnDirectory+'LevEdit.exe'), '', PChar(OwnDirectory), SW_NORMAL); |
ShellExecute(0, 'open', PChar(OwnDirectory+LevEditExe), '', PChar(OwnDirectory), SW_NORMAL); // do not localize |
Close; |
exit; |
end; |
2116,6 → 2126,15 |
end; |
procedure TMainForm.SceneMain; |
resourcestring |
SMissionSucsessful = 'Mission erfolgreich!'; |
SMissionFailed = 'Mission gescheitert!'; |
SPunkte = 'Punkte: %s'; // TODO: Is %f or FloatToStrF() better for internationalization? |
SLevel = 'Level: %d'; |
SLifes = 'Leben: %d'; |
SInfLifes = ''; |
SBossLifes = 'Boss: %d'; |
SEinheiten = 'Einheiten: %d'; |
var |
Enemy: TSprite; |
spriteClass: TClass; |
2178,16 → 2197,16 |
begin |
{$REGION 'Anzeige Punkte'} |
DXDraw.Surface.Canvas.Font.Color := clOlive; |
DXDraw.Surface.Canvas.Textout(9, 9, 'Punkte: ' + FloatToStrF(FScore,ffNumber,14,0)); |
DXDraw.Surface.Canvas.Textout(9, 9, Format(SPunkte, [FloatToStrF(FScore,ffNumber,14,0)])); |
DXDraw.Surface.Canvas.Font.Color := clYellow; |
DXDraw.Surface.Canvas.Textout(10, 10, 'Punkte: ' + FloatToStrF(FScore,ffNumber,14,0)); |
DXDraw.Surface.Canvas.Textout(10, 10, Format(SPunkte, [FloatToStrF(FScore,ffNumber,14,0)])); |
{$ENDREGION} |
{$REGION 'Anzeige Level'} |
DXDraw.Surface.Canvas.Font.Color := clMaroon; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-141, 9, 'Level: ' + IntToStr(flevel)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-141, 9, Format(SLevel, [flevel])); |
DXDraw.Surface.Canvas.Font.Color := clRed; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-140, 10, 'Level: ' + IntToStr(flevel)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-140, 10, Format(SLevel, [flevel])); |
{$ENDREGION} |
{$REGION 'Lebensanzeige'} |
2195,9 → 2214,9 |
if FCheat then |
begin |
DXDraw.Surface.Canvas.Font.Color := clPurple; |
DXDraw.Surface.Canvas.Textout(9, dxdraw.surfaceheight-41, 'Leben: ?'); |
DXDraw.Surface.Canvas.Textout(9, dxdraw.surfaceheight-41, SInfLifes); |
DXDraw.Surface.Canvas.Font.Color := clFuchsia; |
DXDraw.Surface.Canvas.Textout(10, dxdraw.surfaceheight-40, 'Leben: ?'); |
DXDraw.Surface.Canvas.Textout(10, dxdraw.surfaceheight-40, SInfLifes); |
end |
else |
begin |
2204,9 → 2223,9 |
if ((Flife = 1) and ((FBlink div 300) mod 2=0)) or (Flife <> 1) then |
begin |
DXDraw.Surface.Canvas.Font.Color := clPurple; |
DXDraw.Surface.Canvas.Textout(9, dxdraw.surfaceheight-41, 'Leben: ' + IntToStr(flife)); |
DXDraw.Surface.Canvas.Textout(9, dxdraw.surfaceheight-41, Format(SLifes, [flife])); |
DXDraw.Surface.Canvas.Font.Color := clFuchsia; |
DXDraw.Surface.Canvas.Textout(10, dxdraw.surfaceheight-40, 'Leben: ' + IntToStr(flife)); |
DXDraw.Surface.Canvas.Textout(10, dxdraw.surfaceheight-40, Format(SLifes, [flife])); |
end; |
if Flife = 1 then BlinkUpdate; |
end; |
2222,26 → 2241,26 |
if (tmpEnemyAnzeige>0) then |
begin |
DXDraw.Surface.Canvas.Font.Color := clGreen; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-81, 'Boss: ' + IntToStr(FBossLife)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-41, 'Einheiten: ' + IntToStr(tmpEnemyAnzeige)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-81, Format(SBossLifes, [FBossLife])); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-41, Format(SEinheiten, [tmpEnemyAnzeige])); |
DXDraw.Surface.Canvas.Font.Color := clLime; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-80, 'Boss: ' + IntToStr(FBossLife)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-40, 'Einheiten: ' + IntToStr(tmpEnemyAnzeige)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-80, Format(SBossLifes, [FBossLife])); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-40, Format(SEinheiten, [tmpEnemyAnzeige])); |
end |
else |
begin |
DXDraw.Surface.Canvas.Font.Color := clGreen; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-41, 'Boss: ' + IntToStr(FBossLife)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-41, Format(SBossLifes, [FBossLife])); |
DXDraw.Surface.Canvas.Font.Color := clLime; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-40, 'Boss: ' + IntToStr(FBossLife)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-40, Format(SBossLifes, [FBossLife])); |
end; |
end |
else if (FBossLife<=0) and (tmpEnemyAnzeige>0) then |
begin |
DXDraw.Surface.Canvas.Font.Color := clGreen; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-41, 'Einheiten: ' + IntToStr(tmpEnemyAnzeige)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-191, dxdraw.surfaceheight-41, Format(SEinheiten, [tmpEnemyAnzeige])); |
DXDraw.Surface.Canvas.Font.Color := clLime; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-40, 'Einheiten: ' + IntToStr(tmpEnemyAnzeige)); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-190, dxdraw.surfaceheight-40, Format(SEinheiten, [tmpEnemyAnzeige])); |
end; |
{$ENDREGION} |
2249,9 → 2268,9 |
if (EnemyCounter=0) and (FRestEnemies=0) and ((FBossImLevel and (FBossLife=0)) or not FBossImLevel) then |
begin |
DXDraw.Surface.Canvas.Font.Color := clGreen; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-251, dxdraw.surfaceheight-41, 'Mission erfolgreich!'); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-251, dxdraw.surfaceheight-41, SMissionSucsessful); |
DXDraw.Surface.Canvas.Font.Color := clLime; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-250, dxdraw.surfaceheight-40, 'Mission erfolgreich!'); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-250, dxdraw.surfaceheight-40, SMissionSucsessful); |
DXDraw.Surface.Canvas.Release; |
Sleep(1); |
inc(FCounter); |
2262,9 → 2281,9 |
else |
begin |
DXDraw.Surface.Canvas.Font.Color := clMaroon; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-251, dxdraw.surfaceheight-41, 'Mission gescheitert!'); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-251, dxdraw.surfaceheight-41, SMissionFailed); |
DXDraw.Surface.Canvas.Font.Color := clRed; |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-250, dxdraw.surfaceheight-40, 'Mission gescheitert!'); |
DXDraw.Surface.Canvas.Textout(dxdraw.surfacewidth-250, dxdraw.surfaceheight-40, SMissionFailed); |
end; |
DXDraw.Surface.Canvas.Release; |
end; |
/GamSpeicherung.pas |
---|
58,6 → 58,12 |
{$R *.DFM} |
resourcestring |
SSaveGameFileNotFound = 'Spielstandsdatei nicht gefunden'; |
SNa = 'n/a'; |
SSaveGameFolder = 'Spielstände'; |
SSaveGameSubFolder = 'SpaceMission'; |
{ TSpeicherungForm } |
procedure TSpeicherungForm.SearchSaves; |
76,10 → 82,10 |
li4b.visible := false; |
liu.visible := false; |
liw.visible := true; |
li1.caption := 'n/a'; |
li2b.caption := 'n/a'; |
li3b.caption := 'n/a'; |
li4b.caption := 'n/a'; |
li1.caption := SNa; |
li2b.caption := SNa; |
li3b.caption := SNa; |
li4b.caption := SNa; |
LadenBtn.enabled := false; |
LoeschenBtn.enabled := false; |
res := FindFirst(IncludeTrailingPathDelimiter(GetSpielstandVerzeichnisSystem)+'*.sav', 0, sr); |
109,12 → 115,14 |
end; |
procedure TSpeicherungForm.LoeschenBtnClick(Sender: TObject); |
resourcestring |
SDeleteSaveGame = 'Diesen Spielstand wirklich löschen?'; |
var |
fil: string; |
begin |
if LevelListBox.ItemIndex = -1 then exit; |
if MessageDlg('Diesen Spielstand wirklich löschen?', mtConfirmation, mbYesNoCancel, 0) = mrYes then |
if MessageDlg(SDeleteSaveGame, mtConfirmation, mbYesNoCancel, 0) = mrYes then |
begin |
li1.visible := false; |
li2a.visible := false; |
125,14 → 133,14 |
li4b.visible := false; |
liu.visible := false; |
liw.visible := false; |
li1.caption := 'n/a'; |
li2b.caption := 'n/a'; |
li3b.caption := 'n/a'; |
li4b.caption := 'n/a'; |
li1.caption := SNa; |
li2b.caption := SNa; |
li3b.caption := SNa; |
li4b.caption := SNa; |
LadenBtn.enabled := false; |
LoeschenBtn.enabled := false; |
fil := GetSaveGameFileName(LevelListBox.Items.strings[LevelListBox.itemindex], false); |
if not fileexists(fil) then raise Exception.Create('Spielstandsdatei nicht gefunden'); |
if not fileexists(fil) then raise Exception.Create(SSaveGameFileNotFound); |
deletefile(fil); |
searchsaves; |
end; |
142,8 → 150,8 |
var |
usr, sys: string; |
begin |
usr := IncludeTrailingPathDelimiter(GetSpielstandVerzeichnisUser) + SpielstandName + '.sav'; // ab SpaceMission 1.2+ |
sys := IncludeTrailingPathDelimiter(GetSpielstandVerzeichnisSystem) + SpielstandName + '.sav'; // alte Versionen von SpaceMission <1.2 |
usr := IncludeTrailingPathDelimiter(GetSpielstandVerzeichnisUser) + SpielstandName + '.sav'; // ab SpaceMission 1.2+ // do not localize |
sys := IncludeTrailingPathDelimiter(GetSpielstandVerzeichnisSystem) + SpielstandName + '.sav'; // alte Versionen von SpaceMission <1.2 // do not localize |
if fileexists(usr) or forceuserdir then exit(usr); |
if fileexists(sys) then exit(sys); |
exit(usr); |
167,10 → 175,10 |
li4b.visible := false; |
liu.visible := false; |
liw.visible := false; |
li1.caption := 'n/a'; |
li2b.caption := 'n/a'; |
li3b.caption := 'n/a'; |
li4b.caption := 'n/a'; |
li1.caption := SNa; |
li2b.caption := SNa; |
li3b.caption := SNa; |
li4b.caption := SNa; |
LadenBtn.enabled := false; |
LoeschenBtn.enabled := false; |
end; |
179,7 → 187,7 |
SavGame := TSaveData.Create; |
try |
fil := GetSaveGameFileName(LevelListBox.Items.strings[LevelListBox.itemindex], false); |
if not fileexists(fil) then raise Exception.Create('Spielstandsdatei nicht gefunden'); |
if not fileexists(fil) then raise Exception.Create(SSaveGameFileNotFound); |
SavGame.LoadFromFile(fil); |
mainform.FScore := SavGame.Score; |
mainform.FLife := SavGame.Life; |
200,6 → 208,10 |
end; |
procedure TSpeicherungForm.SpeichernBtnClick(Sender: TObject); |
resourcestring |
SNoValidSaveGameName = 'Dies ist kein gültiger Spielstandname!'; |
SEmptySaveGameName = 'Bitte geben Sie einen Namen für den Spielstand ein'; |
SReplaceSaveGame = 'Spielstand ist bereits vorhanden. Ersetzen?'; |
var |
SavGame: TSaveData; |
i: integer; |
206,7 → 218,7 |
begin |
if Levelname.text = '' then |
begin |
MessageDlg('Dies ist kein gültiger Spielstandname!', mtError, [mbOK], 0); |
MessageDlg(SEmptySaveGameName, mtError, [mbOK], 0); |
LevelName.setfocus; |
exit; |
end; |
222,7 → 234,7 |
(copy(LevelName.text, i, 1) = '>') or |
(copy(LevelName.text, i, 1) = '|') then |
begin |
MessageDlg('Dies ist kein gültiger Spielstandname!', mtError, [mbOK], 0); |
MessageDlg(SNoValidSaveGameName, mtError, [mbOK], 0); |
LevelName.setfocus; |
exit; |
end; |
229,7 → 241,7 |
end; |
if LevelListBox.items.IndexOf(LevelName.text) > -1 then |
begin |
if MessageDlg('Spielstand ist bereits vorhanden. Ersetzen?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then |
if MessageDlg(SReplaceSaveGame, mtConfirmation, mbYesNoCancel, 0) <> mrYes then |
exit; |
end; |
250,6 → 262,11 |
end; |
procedure TSpeicherungForm.LevelListBoxClick(Sender: TObject); |
resourcestring |
SNoNA = 'n/a'; |
SIsNormalLevel = 'Das Level ist ein norm. Level'; |
SIsRandomLevel = 'Das Level ist ein Zufallslevel'; |
SHasAttachedLevel = '%s mit Karte'; |
var |
SavGame: TSaveData; |
Punkte, Leben, Level: integer; |
268,10 → 285,10 |
li4b.visible := false; |
liu.visible := false; |
liw.visible := false; |
li1.caption := 'n/a'; |
li2b.caption := 'n/a'; |
li3b.caption := 'n/a'; |
li4b.caption := 'n/a'; |
li1.caption := SNoNA; |
li2b.caption := SNoNA; |
li3b.caption := SNoNA; |
li4b.caption := SNoNA; |
if (LevelListBox.items.count=0) or (LevelListBox.itemindex = -1) then |
begin |
ladenbtn.enabled := false; |
285,7 → 302,7 |
try |
try |
fil := GetSaveGameFileName(LevelListBox.Items.strings[LevelListBox.itemindex], false); |
if not fileexists(fil) then raise Exception.Create('Spielstandsdatei nicht gefunden'); |
if not fileexists(fil) then raise Exception.Create(SSaveGameFileNotFound); |
SavGame.LoadFromFile(fil); |
Punkte := SavGame.Score; |
Leben := SavGame.Life; |
308,11 → 325,11 |
li4a.visible := true; |
li4b.visible := true; |
if Art = gmLevels then |
li1.caption := 'Das Level ist ein norm. Level' |
li1.caption := SIsNormalLevel |
else |
li1.caption := 'Das Level ist ein Zufallslevel'; |
li1.caption := SIsRandomLevel; |
if BeinhaltetLevelDaten then |
li1.Caption := li1.Caption + ' mit Karte'; |
li1.Caption := Format(SHasAttachedLevel, [li1.Caption]); |
li3b.caption := inttostr(Level); |
li4b.caption := inttostr(Leben); |
li2b.caption := inttostr(Punkte); |
350,7 → 367,7 |
function TSpeicherungForm.GetSpielstandVerzeichnisSystem: string; |
begin |
// nicht mehr verwendet seit version 1.2 |
result := OwnDirectory + 'Spielstände'; |
result := OwnDirectory + SSaveGameFolder; |
end; |
function TSpeicherungForm.GetSpielstandVerzeichnisUser: string; |
363,12 → 380,12 |
if result = '' then |
begin |
// Pre Vista |
result := OwnDirectory + 'Spielstände'; |
result := OwnDirectory + SSaveGameFolder; |
end |
else |
begin |
result := IncludeTrailingPathDelimiter(result); |
result := result + 'SpaceMission'; |
result := result + SSaveGameSubFolder; |
end; |
result := IncludeTrailingPathDelimiter(result); |
ForceDirectories(result); |
/Global.pas |
---|
3,12 → 3,15 |
interface |
const |
ProgramVersion = '1.2'; |
RasterW = 48; |
RasterH = 32; |
ProgramVersion = '1.2.1'; |
LevEditRasterW = 48; |
LevEditRasterH = 32; |
MaxPossibleEnemyLives = 999; |
MaxPossibleLevels = 999; |
RegistrySettingsKey = 'SOFTWARE\ViaThinkSoft\SpaceMission\Settings'; |
RegistrySettingsKey = 'SOFTWARE\ViaThinkSoft\SpaceMission\Settings'; // do not localize |
MusicSettingKey = 'Music'; // do not localize |
SoundSettingKey = 'Sound'; // do not localize |
SpeedSettingKey = 'Speed'; // do not localize |
DefaultLevelLength = 1200; |
StartLives = 6; |
conleicht = 650 div 60; // 10 |
16,7 → 19,11 |
conschwer = 1350 div 60; // 22 |
conmaster = 2000 div 60; // 33 |
DEFAULT_ANIMSPEED = 15/1000; |
ADDITIONAL_ENEMIES_PER_LEVEL = 75; |
ADDITIONAL_ENEMIES_PER_LEVEL = 75; // Zufalls-Level |
BossWidth = 4; |
BossHeight = 2; |
SpaceMissionExe = 'SpaceMission.exe'; // do not localize |
LevEditExe = 'LevEdit.exe'; // do not localize |
type |
// DirectX\Music.dxm |
68,7 → 75,7 |
function OwnDirectory: string; |
const |
FOLDERID_SavedGames: TGuid = '{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}'; |
FOLDERID_SavedGames: TGuid = '{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}'; // do not localize |
// Useful functions |
function GetKnownFolderPath(const rfid: TGUID): string; |
78,7 → 85,8 |
implementation |
uses |
Windows, SysUtils, ActiveX, ShlObj, TlHelp32, wininet, Forms, ShellAPI; |
Windows, SysUtils, Dialogs, ActiveX, ShlObj, TlHelp32, wininet, Forms, ShellAPI, |
System.UITypes; |
function GetKnownFolderPath(const rfid: TGUID): string; |
var |
148,14 → 156,14 |
Str : pansichar; // SIC! pansichar |
begin |
ResStr:=''; |
if (system.pos('http://',lowercase(AUrl))=0) and |
(system.pos('https://',lowercase(AUrl))=0) then |
AUrl:='http://'+AUrl; |
if (system.pos('http://',lowercase(AUrl))=0) and // do not localize |
(system.pos('https://',lowercase(AUrl))=0) then // do not localize |
AUrl:='http://'+AUrl; // do not localize |
// Hinzugefügt |
if Assigned(Application) then Application.ProcessMessages; |
hSession:=InternetOpen('InetURL:/1.0', |
hSession:=InternetOpen('InetURL:/1.0', // do not localize |
INTERNET_OPEN_TYPE_PRECONFIG, |
nil, |
nil, |
185,7 → 193,7 |
dwIndex); |
res := pchar(@dwcode); |
dwNumber := sizeof(databuffer)-1; |
if (res ='200') or (res = '302') then |
if (res ='200') or (res = '302') then // do not localize |
begin |
while (InternetReadfile(hfile, |
@databuffer, |
204,7 → 212,7 |
end; |
end |
else |
ResStr := 'Status:'+AnsiString(res); |
ResStr := 'Status:'+AnsiString(res); // do not localize |
if assigned(hfile) then |
InternetCloseHandle(hfile); |
end; |
217,24 → 225,28 |
end; |
procedure CheckForUpdates(ViaThinkSoftProjectName: string; AVersion: string); |
resourcestring |
SDownloadError = 'Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server vorübergehend offline.'; |
SNewProgramVersionAvailable = 'Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?'; |
SNoUpdateAvailable = 'Es ist keine neue Programmversion vorhanden.'; |
var |
cont: RawByteString; |
begin |
cont := GetHTML('https://www.viathinksoft.de/update/?id='+ViaThinkSoftProjectName); |
cont := GetHTML('https://www.viathinksoft.de/update/?id='+ViaThinkSoftProjectName); // do not localize |
if copy(cont, 0, 7) = 'Status:' then |
begin |
Application.MessageBox('Ein Fehler ist aufgetreten. Wahrscheinlich ist keine Internetverbindung aufgebaut, oder der der ViaThinkSoft-Server vorübergehend offline.', 'Fehler', MB_OK + MB_ICONERROR) |
MessageDlg(SDownloadError, mtError, [mbOk], 0); |
end |
else |
begin |
if string(cont) <> AVersion then |
begin |
if Application.MessageBox('Eine neue Programmversion ist vorhanden. Möchten Sie diese jetzt herunterladen?', 'Information', MB_YESNO + MB_ICONASTERISK) = ID_YES then |
shellexecute(application.handle, 'open', pchar('https://www.viathinksoft.de/update/?id=@'+ViaThinkSoftProjectName), '', '', sw_normal); |
if MessageDlg(SNewProgramVersionAvailable, mtConfirmation, mbYesNoCancel, 0) = mrYes then |
shellexecute(application.handle, 'open', pchar('https://www.viathinksoft.de/update/?id=@'+ViaThinkSoftProjectName), '', '', sw_normal); // do not localize |
end |
else |
begin |
Application.MessageBox('Es ist keine neue Programmversion vorhanden.', 'Information', MB_OK + MB_ICONASTERISK); |
MessageDlg(SNoUpdateAvailable, mtInformation, [mbOk], 0); |
end; |
end; |
end; |
/LevEdit.dproj |
---|
90,7 → 90,8 |
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode> |
<VerInfo_Locale>1033</VerInfo_Locale> |
<VerInfo_MinorVer>2</VerInfo_MinorVer> |
<VerInfo_Keys>CompanyName=ViaThinkSoft;FileDescription=SpaceMission LevelEditor;FileVersion=1.2.0.0;InternalName=SpaceMission LevEdit;LegalCopyright=(C) 2001-2024 Daniel Marschall, ViaThinkSoft;LegalTrademarks=;OriginalFilename=LevEdit.exe;ProductName=SpaceMission;ProductVersion=1.2;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys> |
<VerInfo_Keys>CompanyName=ViaThinkSoft;FileDescription=SpaceMission LevelEditor;FileVersion=1.2.1.0;InternalName=SpaceMission LevEdit;LegalCopyright=(C) 2001-2024 Daniel Marschall, ViaThinkSoft;LegalTrademarks=;OriginalFilename=LevEdit.exe;ProductName=SpaceMission;ProductVersion=1.2.1;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys> |
<VerInfo_Release>1</VerInfo_Release> |
</PropertyGroup> |
<PropertyGroup Condition="'$(Cfg_2)'!=''"> |
<Version>7.0</Version> |
/LevMain.pas |
---|
128,7 → 128,7 |
procedure DoMove(MoveCount: Integer); override; |
end; |
TEnemy = class(TImageSprite) |
TEnemyOrItem = class(TImageSprite) |
strict private |
FLives: integer; |
FEnemyType: TEnemyType; |
144,23 → 144,23 |
procedure TBackground.DoMove(MoveCount: Integer); |
begin |
X := -(MainForm.ScrollP * RasterW); |
X := -(MainForm.ScrollP * LevEditRasterW); |
end; |
{ TEnemy } |
procedure TEnemy.DoMove(MoveCount: Integer); |
procedure TEnemyOrItem.DoMove(MoveCount: Integer); |
begin |
if not FCorInit then |
begin |
FXCor := trunc(x) + (MainForm.ScrollP * RasterW); |
FXCor := trunc(x) + (MainForm.ScrollP * LevEditRasterW); |
FCorInit := true; |
end; |
if MainForm.LevData.IndexOfEnemy(FXCor, integer(round(Y)), FEnemyType, FLives) = -1 then dead; |
X := FXCor - (MainForm.ScrollP * RasterW); |
X := FXCor - (MainForm.ScrollP * LevEditRasterW); |
end; |
constructor TEnemy.Create(AParent: TSprite; AEnemyType: TEnemyType; ALives: Integer); |
constructor TEnemyOrItem.Create(AParent: TSprite; AEnemyType: TEnemyType; ALives: Integer); |
begin |
inherited Create(AParent); |
if AEnemyType = etEnemyAttacker then Image := MainForm.GetSpriteGraphic(smgEnemyAttacker); |
186,7 → 186,7 |
procedure TMainForm.DXInit; |
begin |
Imagelist.Items.LoadFromFile(OwnDirectory+'DirectX\Graphics.dxg'); |
Imagelist.Items.LoadFromFile(OwnDirectory+'DirectX\Graphics.dxg'); // do not localize |
ImageList.Items.MakeColorTable; |
DXDraw.ColorTable := ImageList.Items.ColorTable; |
DXDraw.DefColorTable := ImageList.Items.ColorTable; |
197,6 → 197,7 |
procedure TMainForm.FormCreate(Sender: TObject); |
resourcestring |
SFileError = 'Die Datei kann von SpaceMission nicht geöffnet werden!'; |
SCaption = 'SpaceMission %s - Leveleditor'; |
begin |
{ VCL-Ersatz start } |
dxtimer := tdxtimer.create(self); |
248,8 → 249,8 |
// Leeres Level am Anfang braucht keine Beenden-Bestätigung. |
// LevChanged := true; |
//Application.Title := 'SpaceMission '+ProgramVersion+' - Leveleditor'; |
Caption := 'SpaceMission '+ProgramVersion+' - Leveleditor'; |
//Application.Title := Format(SCaption, [ProgramVersion]); |
Caption := Format(SCaption, [ProgramVersion]); |
DXInit; |
LevData := TLevelData.create; |
ProgramInit; |
481,7 → 482,7 |
sav: TSaveData; |
tmp: string; |
begin |
KillTask('SpaceMission.exe'); |
KillTask(SpaceMissionExe); |
sav := TSaveData.Create; |
try |
496,7 → 497,7 |
sav.LevelData.Assign(LevData); |
tmp := GetTestlevelFilename; |
sav.SaveToFile(tmp); |
ShellExecute(Handle, 'open', PChar(OwnDirectory+'SpaceMission.exe'), PChar('"'+tmp+'"'), PChar(OwnDirectory), SW_NORMAL); |
ShellExecute(Handle, 'open', PChar(OwnDirectory+SpaceMissionExe), PChar('"'+tmp+'"'), PChar(OwnDirectory), SW_NORMAL); // do not localize |
finally |
FreeAndNil(sav); |
end; |
525,7 → 526,7 |
function TMainForm.GetTestlevelFilename: string; |
begin |
result := IncludeTrailingPathDelimiter(TPath.GetTempPath) + 'SpaceMissionTest.sav'; |
result := IncludeTrailingPathDelimiter(TPath.GetTempPath) + 'SpaceMissionTest.sav'; // do not localize |
end; |
procedure TMainForm.HilfeTopicClick(Sender: TObject); |
567,8 → 568,8 |
j, k, l, ex, ey: integer; |
ok, breaked: boolean; |
begin |
ex := trunc(x/RasterW) * RasterW; |
ey := trunc(y/RasterH) * RasterH; |
ex := trunc(x/LevEditRasterW) * LevEditRasterW; |
ey := trunc(y/LevEditRasterH) * LevEditRasterH; |
EnemyCreateSprite(ex, ey, SelectedEnemyType, LivesEdit.Value); |
breaked := false; |
{ Setzen } |
585,11 → 586,11 |
begin |
if boss then |
begin |
for k := 0 to 3 do |
for k := 0 to BossWidth-1 do |
begin |
for l := 0 to 1 do |
for l := 0 to BossHeight-2 do |
begin |
if LevData.IndexOfEnemy(ex + ((ScrollP - k) * RasterW), ey - (RasterH * l), etEnemyBoss, j) <> -1 then |
if LevData.IndexOfEnemy(ex + ((ScrollP - k) * LevEditRasterW), ey - (LevEditRasterH * l), etEnemyBoss, j) <> -1 then |
begin |
ok := false; |
break; |
599,7 → 600,7 |
end; |
if not ok then break; |
end; |
if LevData.IndexOfEnemy(ex + (ScrollP * RasterW), ey, i, j) <> -1 then |
if LevData.IndexOfEnemy(ex + (ScrollP * LevEditRasterW), ey, i, j) <> -1 then |
begin |
ok := false; |
break; |
611,9 → 612,9 |
if ok then |
begin |
if EnemyTypeHasLives(SelectedEnemyType) then |
LevData.AddEnemy(ex + (ScrollP * RasterW), ey, SelectedEnemyType, LivesEdit.Value) |
LevData.AddEnemy(ex + (ScrollP * LevEditRasterW), ey, SelectedEnemyType, LivesEdit.Value) |
else |
LevData.AddEnemy(ex + (ScrollP * RasterW), ey, SelectedEnemyType, 0); |
LevData.AddEnemy(ex + (ScrollP * LevEditRasterW), ey, SelectedEnemyType, 0); |
inc(NumEnemys); |
if SelectedEnemyType = etEnemyBoss then boss := true; |
end |
628,13 → 629,13 |
begin |
if boss and (i = etEnemyBoss) then |
begin |
for k := 0 to 3 do |
for k := 0 to BossWidth - 1 do |
begin |
for l := 0 to 1 do |
for l := 0 to BossHeight - 1 do |
begin |
if LevData.IndexOfEnemy(ex + ((ScrollP - k) * RasterW), ey - (RasterH * l), i, j) <> -1 then |
if LevData.IndexOfEnemy(ex + ((ScrollP - k) * LevEditRasterW), ey - (LevEditRasterH * l), i, j) <> -1 then |
begin |
LevData.DeleteEnemy(ex + ((ScrollP - k) * RasterW), ey - (RasterH * l), i, j); |
LevData.DeleteEnemy(ex + ((ScrollP - k) * LevEditRasterW), ey - (LevEditRasterH * l), i, j); |
Boss := false; |
dec(NumEnemys); |
breaked := true; |
644,9 → 645,9 |
if breaked then break; |
end; |
end; |
if LevData.IndexOfEnemy(ex + (ScrollP * RasterW), ey, i, j) <> -1 then |
if LevData.IndexOfEnemy(ex + (ScrollP * LevEditRasterW), ey, i, j) <> -1 then |
begin |
LevData.DeleteEnemy(ex + (ScrollP * RasterW), ey, i, j); |
LevData.DeleteEnemy(ex + (ScrollP * LevEditRasterW), ey, i, j); |
if i = etEnemyBoss then Boss := false; |
dec(NumEnemys); |
breaked := true; |
674,7 → 675,7 |
var |
Enemy: TSprite; |
begin |
Enemy := TEnemy.Create(SpriteEngine.Engine, AEnemyType, ALives); |
Enemy := TEnemyOrItem.Create(SpriteEngine.Engine, AEnemyType, ALives); |
Enemy.x := x; |
Enemy.y := y; |
end; |
711,32 → 712,37 |
end; |
procedure TMainForm.AnzeigeAct; |
resourcestring |
SYes = 'Ja'; |
SNo = 'Nein'; |
begin |
SLabel1b.Caption := inttostr(NumEnemys); |
if Boss then SLabel2b.Caption := 'Ja' else SLabel2b.Caption := 'Nein'; |
if Boss then SLabel2b.Caption := SYes else SLabel2b.Caption := SNo; |
SLabel3b.Caption := inttostr(ScrollBar.Max); |
if LevChanged then |
begin |
SLabel4a.Font.Color := $00000096; |
SLabel4b.Font.Color := $00000096; |
SLabel4b.Caption := 'Nein'; |
SLabel4b.Caption := SNo; |
end |
else |
begin |
SLabel4a.Font.Color := $00009600; |
SLabel4b.Font.Color := $00009600; |
SLabel4b.Caption := 'Ja'; |
SLabel4b.Caption := SYes; |
end; |
end; |
procedure TMainForm.AufUpdatesprfen1Click(Sender: TObject); |
begin |
CheckForUpdates('spacemission', ProgramVersion); |
CheckForUpdates('spacemission', ProgramVersion); // do not localize |
end; |
procedure TMainForm.NeuClick(Sender: TObject); |
resourcestring |
SReallyDeleteLevel = 'Level wirklich löschen?'; |
begin |
if MessageDlg('Level wirklich löschen?', mtConfirmation, mbYesNoCancel, 0) = mrYes then |
if MessageDlg(SReallyDeleteLevel, mtConfirmation, mbYesNoCancel, 0) = mrYes then |
DestroyLevel; |
end; |
746,6 → 752,7 |
status_info = 'Zeigen Sie mit dem Mauszeiger auf eine Einheit, um deren Eigenschaften anzuzeigen...'; |
status_lives = 'Leben: '; |
status_nolives = 'Einheit hat keine Lebensangabe'; |
SUnknownEnemyType = '???'; |
var |
i: TEnemyType; |
ex, ey, j, k, l: integer; |
759,8 → 766,8 |
StatusBar.SimpleText := ' ' + status_info; |
exit; |
end; |
ex := trunc(x/RasterW) * RasterW; |
ey := trunc(y/RasterH) * RasterH; |
ex := trunc(x/LevEditRasterW) * LevEditRasterW; |
ey := trunc(y/LevEditRasterH) * LevEditRasterH; |
lifes := -1; |
enemyType := etUnknown; |
breaked := false; |
770,11 → 777,11 |
begin |
if boss and (i = etEnemyBoss) then |
begin |
for k := 0 to 3 do |
for k := 0 to BossWidth - 1 do |
begin |
for l := 0 to 1 do |
for l := 0 to BossHeight - 1 do |
begin |
if LevData.IndexOfEnemy(ex + ((ScrollP - k) * RasterW), ey - (RasterH * l), i, j) <> -1 then |
if LevData.IndexOfEnemy(ex + ((ScrollP - k) * LevEditRasterW), ey - (LevEditRasterH * l), i, j) <> -1 then |
begin |
lifes := j; |
breaked := true; |
784,7 → 791,7 |
if breaked then break; |
end; |
end; |
if (breaked = false) and (LevData.IndexOfEnemy(ex + (ScrollP * RasterW), ey, i, j) <> -1) then |
if (breaked = false) and (LevData.IndexOfEnemy(ex + (ScrollP * LevEditRasterW), ey, i, j) <> -1) then |
begin |
lifes := j; |
enemyType := i; |
804,7 → 811,7 |
else if Ord(enemyType) = 6 then enemyName := Enemy6.Caption |
else if Ord(enemyType) = 7 then enemyName := Enemy7.Caption |
else if Ord(enemyType) = 8 then enemyName := Enemy8.Caption |
else enemyName := '???'; |
else enemyName := SUnknownEnemyType; |
if lifes > 0 then |
StatusBar.SimpleText := ' ' + enemyName + ' - ' + status_lives + inttostr(lifes) |
else |
815,9 → 822,11 |
end; |
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); |
resourcestring |
SExitWithoutSave = 'Beenden ohne abspeichern?'; |
begin |
if Assigned(LevData) and LevChanged and (LevData.CountEnemies>0) then |
CanClose := MessageDlg('Beenden ohne abspeichern?', mtConfirmation, mbYesNoCancel, 0) = mrYes; |
CanClose := MessageDlg(SExitWithoutSave, mtConfirmation, mbYesNoCancel, 0) = mrYes; |
end; |
procedure TMainForm.Spielfelderweitern1Click(Sender: TObject); |
/LevOptions.dfm |
---|
69,6 → 69,7 |
Width = 329 |
Height = 57 |
Color = clInfoBk |
ParentBackground = False |
TabOrder = 0 |
object ElLabel1: TLabel |
Left = 8 |
92,11 → 93,13 |
'Die Spielfeldgr'#246#223'e wird nur von dem Leveleditor '#13#10'ben'#246'tigt. Das ' + |
#196'ndern dieser Gr'#246#223'e wirkt sich '#13#10'nicht auf die Gr'#246#223'e der Levels ' + |
'aus.' |
Color = clInfoBk |
Font.Charset = DEFAULT_CHARSET |
Font.Color = clWindowText |
Font.Height = -11 |
Font.Name = 'MS Sans Serif' |
Font.Style = [] |
ParentColor = False |
ParentFont = False |
end |
end |
/LevSpeicherung.pas |
---|
59,6 → 59,7 |
resourcestring |
SLevelListBox = 'Level %d'; |
SSelbsterstellt = 'Selbsterstellt'; |
SLevelFileNotFound = 'Leveldatei nicht gefunden'; |
procedure TSpeicherungForm.SearchLevels; |
var |
92,12 → 93,14 |
end; |
procedure TSpeicherungForm.LoeschenBtnClick(Sender: TObject); |
resourcestring |
SReallyDeleteLevel = 'Dieses Level wirklich löschen?'; |
var |
fil: TLevelFile; |
begin |
if LevelListBox.ItemIndex = -1 then exit; |
if MessageDlg('Dieses Level wirklich löschen?', mtConfirmation, mbYesNoCancel, 0) = mrYes then |
if MessageDlg(SReallyDeleteLevel, mtConfirmation, mbYesNoCancel, 0) = mrYes then |
begin |
li1a.visible := false; |
li2a.visible := false; |
111,7 → 114,7 |
LadenBtn.enabled := false; |
LoeschenBtn.enabled := false; |
fil := GetLevelFileName(GetListBoxSelectedLevelNumber,false); |
if not fil.found then raise Exception.Create('Leveldatei nicht gefunden'); |
if not fil.found then raise Exception.Create(SLevelFileNotFound); |
deletefile(fil.fileLocation); |
SearchLevels; |
end; |
118,6 → 121,8 |
end; |
procedure TSpeicherungForm.LadenBtnClick(Sender: TObject); |
resourcestring |
SLoadNewLevelAndDiscardChanges = 'Neues Level laden und Änderungen verwerfen?'; |
var |
fil: TLevelFile; |
begin |
125,7 → 130,7 |
if MainForm.LevChanged and (MainForm.LevData.CountEnemies>0) then |
begin |
if MessageDlg('Neues Level laden und Änderungen verwerfen?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then exit; |
if MessageDlg(SLoadNewLevelAndDiscardChanges, mtConfirmation, mbYesNoCancel, 0) <> mrYes then exit; |
end; |
// Da Button bei ungültigen Level deaktiviert wird, ist das nicht mehr nötig. |
135,7 → 140,7 |
MainForm.DestroyLevel; |
MainForm.LevData.RasterErzwingen := true; |
fil := GetLevelFileName(GetListBoxSelectedLevelNumber,false); |
if not fil.found then raise Exception.Create('Leveldatei nicht gefunden'); |
if not fil.found then raise Exception.Create(SLevelFileNotFound); |
MainForm.LevData.LoadFromFile(fil.fileLocation); |
MainForm.RefreshFromLevData; |
MainForm.LevChanged := false; |
144,10 → 149,14 |
end; |
procedure TSpeicherungForm.SpeichernBtnClick(Sender: TObject); |
resourcestring |
SLevelIsEmpty = 'Das Level ist leer!'; |
SLevelAlreadyExists = 'Level ist bereits vorhanden. Ersetzen?'; |
SNoValidLevelName = 'Dies ist kein gültiger Levelname!'; |
begin |
if MainForm.LevData.CountEnemies = 0 then |
begin |
MessageDlg('Das Level ist leer!', mtError, [mbOK], 0); |
MessageDlg(SLevelIsEmpty, mtError, [mbOK], 0); |
LevelNumber.SetFocus; |
exit; |
end; |
163,7 → 172,7 |
(copy(LevelName.text, i, 1) = '>') or |
(copy(LevelName.text, i, 1) = '|') then |
begin |
MessageDlg('Dies ist kein gültiger Levelname!', mtError, [mbOK], 0); |
MessageDlg(SNoValidLevelName, mtError, [mbOK], 0); |
LevelName.SetFocus; |
exit; |
end; |
171,7 → 180,7 |
if (LevelListBox.items.IndexOf(Format(SLevelListBox, [LevelNumber.Value])) > -1) or |
(LevelListBox.items.IndexOf(Format(SLevelListBox, [LevelNumber.Value])+' ('+SSelbsterstellt+')') > -1) then |
begin |
if MessageDlg('Level ist bereits vorhanden. Ersetzen?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then |
if MessageDlg(SLevelAlreadyExists, mtConfirmation, mbYesNoCancel, 0) <> mrYes then |
exit; |
end; |
186,6 → 195,11 |
end; |
procedure TSpeicherungForm.LevelListBoxClick(Sender: TObject); |
resourcestring |
SYes = 'Ja'; |
SNo = 'Nein'; |
SFields = '%d Felder'; |
SXbyY = '%s von %s'; |
var |
LevelData: TLevelData; |
boss: boolean; |
217,7 → 231,7 |
LevelData.RasterErzwingen := true; |
fil := GetLevelFileName(GetListBoxSelectedLevelNumber,false); |
if not fil.found then raise Exception.Create('Leveldatei nicht gefunden'); |
if not fil.found then raise Exception.Create(SLevelFileNotFound); |
LevelData.LoadFromFile(fil.fileLocation); |
boss := false; |
241,11 → 255,11 |
LoeschenBtn.enabled := true; |
li1b.caption := inttostr(anzahlEinheiten); |
if boss then |
li2b.caption := 'Ja' |
li2b.caption := SYes |
else |
li2b.caption := 'Nein'; |
li3b.caption := IntToStr(LevelData.LevelEditorLength) + ' Felder'; |
li4.Caption := Trim(LevelData.LevelName + ' von ' + LevelData.LevelAuthor); |
li2b.caption := SNo; |
li3b.caption := Format(SFields, [LevelData.LevelEditorLength]); |
li4.Caption := Trim(Format(SXbyY, [LevelData.LevelName, LevelData.LevelAuthor])); |
finally |
FreeAndNil(LevelData); |
end; |
/SpaceMission.dproj |
---|
90,7 → 90,8 |
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode> |
<VerInfo_MinorVer>2</VerInfo_MinorVer> |
<VerInfo_Locale>1033</VerInfo_Locale> |
<VerInfo_Keys>CompanyName=ViaThinkSoft;FileDescription=SpaceMission;FileVersion=1.2.0.0;InternalName=SpaceMission;LegalCopyright=(C) 2001-2024 Daniel Marschall, ViaThinkSoft;LegalTrademarks=;OriginalFilename=SpaceMission.exe;ProductName=SpaceMission;ProductVersion=1.2;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys> |
<VerInfo_Keys>CompanyName=ViaThinkSoft;FileDescription=SpaceMission;FileVersion=1.2.1.0;InternalName=SpaceMission;LegalCopyright=(C) 2001-2024 Daniel Marschall, ViaThinkSoft;LegalTrademarks=;OriginalFilename=SpaceMission.exe;ProductName=SpaceMission;ProductVersion=1.2.1;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys> |
<VerInfo_Release>1</VerInfo_Release> |
</PropertyGroup> |
<PropertyGroup Condition="'$(Cfg_2)'!=''"> |
<Version>7.0</Version> |
/SpaceMission.res |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/TODO.md |
---|
1,11 → 1,15 |
# SpaceMission TODO |
## Geplant für 1.2.1 |
- Registry individual Speed Setting |
## Geplant für 1.3 |
- Neue Einheit: Ufo, das im Kreis fliegt und nicht weggeht |
- Bei Pause => Entweder alles grau werden lassen, oder vielleicht ganz groß Pause in die Bildschirmmitte schreiben |
- Vorbereitung auf Multilingualität (all strings in resourcestrings), ggf. sogar schon auf Englisch übersetzen |
- Auf Englisch übersetzen |
- SplashScreens: als BMP |
## Kleinigkeiten |
22,14 → 26,15 |
## Ideen |
- Neue Einheit: 10 Sekunden unverwundbarkeit item |
- Neue Einheit: Geld Geschenk item |
- Neues Item: 10 Sekunden unverwundbarkeit item |
- Neues Item: Geld Geschenk item |
- Boss schwieriger machen: Er soll auch nach links und rechts gehen? |
- Spiellogik: Wenn man gegen einen gegner fliegt, soll er schaden haben! |
- Cooldown für Laser? Limitierung der Schüsse (Kanone wird heiß). |
- Highscore Liste |
- "Throwback" nach einem hit. Nach Treffer, zurückgeschleudert werden |
- "unsaubere" levels akzeptieren! komet mit 1+ leben, einheiten, die nicht auf der linie sind. |
- Anderer Soundeffekt, wenn man selbst getroffen ist |
- Leveleditor: "unsaubere" levels akzeptieren! komet mit 1+ leben, einheiten, die nicht auf der linie sind. |
- Netzwerkspiel? |
- Leveleditor: Ober - / Unterfelder? (was meinte ich damit?) |
- Leveleditor: Höchstens 9999 enemies? |
38,17 → 43,14 |
- Verschiedene Dinge bei schnelligkeit anders?! z.B. Boss-Explore, Schießende Einheiten (Tamas) |
- Tama 1 must damage PL. Sprite! |
- Tama 2 must damage EN. Sprite! |
- Tama 1 und Tama 2 müssten sich gegenseitig kaputt machen? |
- Tama 1 und Tama 2 könnten sich gegenseitig kaputt machen? |
- Speicherung: Umbenennen - Button? |
## Repro, Unklar, Fragen |
- Was ist wenn man mission erfolgreich hatte und dann doch stirbt? |
- Kartengröße bleibt bei "Neu" |
- Que: Gibt es eine neuere Version des Shoot-Samples (nicht mehr vorhanden in aktueller DelphiX) |
- Tastenspeere bei runter+links+shot |
- "Beenden ohne Speichern?" bei leerem Level |
- Leveleditor performance oder deadlock probleme? |
- Leveleditor performance oder deadlock probleme? bei vielen einheiten wird das sehr langsam? algo verbessern? |
- Leveleditor Probleme mit Schiebebildchen... (was meinte ich damit?) |
- Leveleditor Wenn Level gelöscht, dass gerade geladen ist, dann LevChanged = true! |
- Leveleditor Boss überschneidet Einheiten ? |
62,10 → 64,8 |
- Dec(live) bei attacker1 manchmal kein Ton? |
- Sequences (was meinte ich damit?) |
- Rnd: Soll Boss-Live auch RND? |
- leicht, mittel, schwer: falsche fps bei langsameren karten |
- auch mit einer symbolleiste mit ordnersymbol bei lev (oder sm)? |
- leicht, mittel, schwer: falsche fps bei langsameren grafikkarten? |
- lev: überall ok = focus! |
- lev: bei vielen einheiten wird das arsch-langsam? algo verbessern? |
- systemmodus auswählen (move rechnungen ausführen?) oder ein rechenlabel mit dem status anzeigen |
- SM: Soll bei Verwaltung lieber statt dem ersten satz bei status was anderes hin? Levelart: Normales Level. |
Wenn es bleiben soll, dann den satz verbessern. Das Level ist ein Zufallslevel |