source: OniSplit/Level/LevelDatWriter.cs@ 1193

Last change on this file since 1193 was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 11.8 KB
Line 
1using System;
2using System.Collections.Generic;
3
4namespace Oni.Level
5{
6 internal class LevelDatWriter
7 {
8 private readonly Importer importer;
9 private readonly DatLevel level;
10
11 public class DatLevel
12 {
13 public string name;
14 public string skyName;
15 public readonly List<ObjectSetup> physics = new List<ObjectSetup>();
16 public readonly List<Physics.ObjectParticle> particles = new List<Physics.ObjectParticle>();
17 public readonly List<ScriptCharacter> characters = new List<ScriptCharacter>();
18 public readonly List<Corpse> corpses = new List<Corpse>();
19 public Akira.PolygonMesh model;
20 }
21
22 private LevelDatWriter(Importer importer, DatLevel level)
23 {
24 this.importer = importer;
25 this.level = level;
26 }
27
28 public static void Write(Importer importer, DatLevel level)
29 {
30 var writer = new LevelDatWriter(importer, level);
31 writer.WriteONLV();
32 }
33
34 private void WriteONLV()
35 {
36 var onlv = importer.CreateInstance(TemplateTag.ONLV, level.name);
37 var oboa = importer.CreateInstance(TemplateTag.OBOA);
38 var aisa = importer.CreateInstance(TemplateTag.AISA);
39 var onoa = importer.CreateInstance(TemplateTag.ONOA);
40 var envp = importer.CreateInstance(TemplateTag.ENVP);
41 var crsa = importer.CreateInstance(TemplateTag.CRSA);
42 var onsk = importer.CreateInstance(TemplateTag.ONSK, level.skyName);
43 var akev = importer.CreateInstance(TemplateTag.AKEV, level.name);
44
45 using (var writer = onlv.OpenWrite())
46 {
47 writer.Write(level.name, 64);
48 writer.Write(akev);
49 writer.Write(oboa);
50 writer.Write(0);
51 writer.Write(0);
52 writer.Write(0);
53 writer.Write(onsk);
54 writer.Write(0.0f);
55 writer.Write(aisa);
56 writer.Write(0);
57 writer.Write(0);
58 writer.Write(0);
59 writer.Write(onoa);
60 writer.Write(envp);
61 writer.Skip(644);
62 writer.Write(crsa);
63 }
64
65 WriteOBOA(oboa);
66 WriteAISA(aisa);
67 WriteONOA(onoa);
68 WriteENVP(envp, level.particles);
69 WriteCRSA(crsa, level.corpses);
70 }
71
72 private void WriteOBOA(ImporterDescriptor oboa)
73 {
74 var objects = level.physics;
75 var m3ga = new ImporterDescriptor[objects.Count];
76 var oban = new ImporterDescriptor[objects.Count];
77 var envp = new ImporterDescriptor[objects.Count];
78
79 for (int i = 0; i < objects.Count; i++)
80 {
81 var obj = objects[i];
82
83 var m3gms = new List<ImporterDescriptor>();
84
85 foreach (var geom in obj.Geometries)
86 {
87 if (geom is string)
88 m3gms.Add(importer.CreateInstance(TemplateTag.M3GM, (string)geom));
89 else
90 m3gms.Add(Motoko.GeometryDatWriter.Write((Motoko.Geometry)geom, importer.ImporterFile));
91 }
92
93 m3ga[i] = importer.CreateInstance(TemplateTag.M3GA);
94
95 WriteM3GA(m3ga[i], m3gms);
96
97 if (obj.Animation != null)
98 oban[i] = importer.CreateInstance(TemplateTag.OBAN, obj.Animation.Name);
99
100 if (obj.Particles.Count > 0)
101 {
102 envp[i] = importer.CreateInstance(TemplateTag.ENVP);
103 WriteENVP(envp[i], obj.Particles);
104 }
105 }
106
107 int unused = 32;
108
109 using (var writer = oboa.OpenWrite(22))
110 {
111 writer.WriteUInt16(objects.Count + unused);
112
113 for (int i = 0; i != objects.Count; i++)
114 {
115 var obj = objects[i];
116
117 writer.Write(m3ga[i]);
118 writer.Write(oban[i]);
119 writer.Write(envp[i]);
120 writer.Write((uint)(obj.Flags | Physics.ObjectSetupFlags.InUse));
121 //writer.Write(obj.DoorGunkIndex);
122 writer.Write(0);
123 writer.Write(obj.DoorScriptId);
124 writer.Write((uint)obj.PhysicsType);
125 writer.Write(obj.ScriptId);
126 writer.Write(obj.Position);
127 writer.Write(obj.Orientation);
128 writer.Write(obj.Scale);
129 writer.WriteMatrix4x3(obj.Origin);
130 writer.Write(obj.Name, 64);
131 writer.Write(obj.FileName, 64);
132 }
133
134 writer.Skip(unused * 240);
135 }
136 }
137
138 private void WriteM3GA(ImporterDescriptor m3ga, ICollection<ImporterDescriptor> geometries)
139 {
140 using (var writer = m3ga.OpenWrite(20))
141 {
142 writer.Write(geometries.Count);
143 writer.Write(geometries);
144 }
145 }
146
147 private void WriteAISA(ImporterDescriptor aisa)
148 {
149 var characterClasses = new Dictionary<string, ImporterDescriptor>(StringComparer.Ordinal);
150 var weaponClasses = new Dictionary<string, ImporterDescriptor>(StringComparer.Ordinal);
151
152 foreach (var chr in level.characters)
153 {
154 if (!characterClasses.ContainsKey(chr.className))
155 characterClasses.Add(chr.className, importer.CreateInstance(TemplateTag.ONCC, chr.className));
156
157 if (!string.IsNullOrEmpty(chr.weaponClassName) && !weaponClasses.ContainsKey(chr.weaponClassName))
158 weaponClasses.Add(chr.weaponClassName, importer.CreateInstance(TemplateTag.ONWC, chr.weaponClassName));
159 }
160
161 using (var writer = aisa.OpenWrite(22))
162 {
163 writer.WriteUInt16(level.characters.Count);
164
165 foreach (var chr in level.characters)
166 {
167 ImporterDescriptor characterClass, weaponClass;
168
169 characterClasses.TryGetValue(chr.className, out characterClass);
170
171 if (!string.IsNullOrEmpty(chr.weaponClassName))
172 weaponClasses.TryGetValue(chr.weaponClassName, out weaponClass);
173 else
174 weaponClass = null;
175
176 writer.Write(chr.name, 32);
177 writer.WriteInt16(chr.scriptId);
178 writer.WriteInt16(chr.flagId);
179 writer.WriteUInt16((ushort)chr.flags);
180 writer.WriteUInt16((ushort)chr.team);
181 writer.Write(characterClass);
182 writer.Skip(36);
183 writer.Write(chr.onSpawn, 32);
184 writer.Write(chr.onDeath, 32);
185 writer.Write(chr.onSeenEnemy, 32);
186 writer.Write(chr.onAlarmed, 32);
187 writer.Write(chr.onHurt, 32);
188 writer.Write(chr.onDefeated, 32);
189 writer.Write(chr.onOutOfAmmo, 32);
190 writer.Write(chr.onNoPath, 32);
191 writer.Write(weaponClass);
192 writer.WriteInt16(chr.ammo);
193 writer.Skip(10);
194 }
195 }
196 }
197
198 private void WriteONOA(ImporterDescriptor onoa)
199 {
200 var map = new Dictionary<int, List<int>>();
201 int pi = 0;
202
203 foreach (var poly in level.model.Polygons)
204 {
205 if (poly.ObjectId > 0)
206 {
207 List<int> indices;
208
209 int objectId = poly.ObjectType << 24 | poly.ObjectId;
210
211 if (!map.TryGetValue(objectId, out indices))
212 {
213 indices = new List<int>();
214 map[objectId] = indices;
215 }
216
217 indices.Add(pi);
218 }
219
220 pi++;
221 }
222
223 var elt = new List<KeyValuePair<int, ImporterDescriptor>>();
224
225 foreach (var pair in map)
226 {
227 var idxa = importer.CreateInstance(TemplateTag.IDXA);
228
229 elt.Add(new KeyValuePair<int, ImporterDescriptor>(pair.Key, idxa));
230
231 using (var idxaWriter = idxa.OpenWrite(20))
232 {
233 idxaWriter.Write(pair.Value.Count);
234 idxaWriter.Write(pair.Value.ToArray());
235 }
236 }
237
238 using (var writer = onoa.OpenWrite(20))
239 {
240 writer.Write(elt.Count);
241
242 foreach (var e in elt)
243 {
244 writer.Write(e.Key);
245 writer.Write(e.Value);
246 }
247 }
248 }
249
250 private void WriteENVP(ImporterDescriptor envp, List<Physics.ObjectParticle> particles)
251 {
252 using (var writer = envp.OpenWrite(22))
253 {
254 writer.WriteUInt16(particles.Count);
255
256 foreach (var particle in particles)
257 {
258 writer.Write(particle.ParticleClass, 64);
259 writer.Write(particle.Tag, 48);
260 writer.WriteMatrix4x3(particle.Matrix);
261 writer.Write(particle.DecalScale);
262 writer.Write((ushort)particle.Flags);
263 writer.Skip(38);
264 }
265 }
266 }
267
268 private void WriteCRSA(ImporterDescriptor crsa, List<Corpse> corpses)
269 {
270 //
271 // Ensure that there are at least 20 corpses
272 //
273
274 while (corpses.Count < 20)
275 corpses.Add(new Corpse());
276
277 int fixedCount = corpses.Count(c => c.IsFixed);
278 int usedCount = corpses.Count(c => c.IsUsed);
279
280 //
281 // Ensure that there are at least 5 unused corpses
282 // so new corpses can be created at runtime
283 //
284
285 while (corpses.Count - usedCount < 5)
286 corpses.Add(new Corpse());
287
288 corpses.Sort((x, y) => x.Order.CompareTo(y.Order));
289
290 var onccDesriptors = new Dictionary<string, ImporterDescriptor>();
291
292 using (var writer = crsa.OpenWrite(12))
293 {
294 writer.Write(fixedCount);
295 writer.Write(usedCount);
296 writer.Write(corpses.Count);
297
298 foreach (var corpse in corpses)
299 {
300 writer.Write(corpse.FileName ?? "", 32);
301 writer.Skip(128);
302
303 if (corpse.IsUsed)
304 {
305 ImporterDescriptor oncc = null;
306
307 if (!string.IsNullOrEmpty(corpse.CharacterClass))
308 {
309 if (!onccDesriptors.TryGetValue(corpse.CharacterClass, out oncc))
310 {
311 oncc = importer.CreateInstance(TemplateTag.ONCC, corpse.CharacterClass);
312 onccDesriptors.Add(corpse.CharacterClass, oncc);
313 }
314 }
315
316 writer.Write(oncc);
317
318 foreach (var transform in corpse.Transforms)
319 writer.WriteMatrix4x3(transform);
320
321 writer.Write(corpse.BoundingBox);
322 }
323 else
324 {
325 writer.Skip(940);
326 }
327 }
328 }
329 }
330 }
331}
Note: See TracBrowser for help on using the repository browser.