-
Notifications
You must be signed in to change notification settings - Fork 31
/
NPCs.txt
3909 lines (3702 loc) · 182 KB
/
NPCs.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
NPC Overview
===========
NPCs are of (at least) two types: humans, and furniture. Human NPCs have an AI and move on their own; furniture NPCs are simply static.
NPC Physics
===========
An NPC is represented by one (or more) particles, exactly like all the other particles in FS. The way the NPC particles move depends on the "regime" or "state" in which an NPC finds itself at the moment. There are two possible "regimes".
** Constrained Regime **
This is the initial regime, when the NPC is spawn. In this regime the NPC particle is constrained to move within triangles of the ship, and the particle's movement is the resultant of the NPC's own movement (e.g. walking), friction along "floor" sides of the triangles, counter-gravity generated by "floor" sides of the triangles, and any external forces (including gravity). As an example, when walking on an almost-flat horizontal side of a triangle, gravity has little effect and the NPC moves freely. As soon as the spring becomes too steep, however, gravity wins and the NPC "slides" down the spring.
In the Constrained Regime NPC's normally walk along the springs of the "Paths" layer, i.e. springs of the ship that are also present in the Paths layer. These springs make up the paths that NPC's walk along. However, NPCs may eventually lose their path, for example when at the end of a path or when the path becomes too steep. When this happens, they fall along the ship springs according to gravity, until they reach a path spring again.
An NPC remains in the constrained regime until it reaches a "hole" point, i.e. a point through which normally water would enter. When this happens, the NPC enters the "Free" regime.
** Free Regime **
In this regime NPC's do not follow paths, but rather move freely according to particle physics. They fall when in air or sink, rise, and float when in water. Once in this regime, the NPC stays in this regime.
This regime has a few sub-states controlling the actions and the rendering of the NPC, and a separate algorithm governs the transitions between these states. The states are:
- Floating: the NPC particle is stationary on the sea surface; the NPC might be rendered as asking for help with its arms and moving its legs to stay afloat.
- Sinking: the NPC particle is sinking towards the bottom of the sea; the NPC might be rendered as rotating with limbs moving in arbitrary directions.
- Rising: the NPC particle is underwater but rising towards the sea surface; the NPC might be rendered as swimming upwards.
NPC particles are made of one of few new "NPC" materials, for example "Human NPC", which simulates human mass and buoyancy when breathing, resulting in a particle that would normally *barely* float. If arbitrary transitions between the three states are required - e.g. a floating NPC should start sinking all of a sudden - these will be implemented by means of forces added onto the particles.
NPC Rendering
===========
The particle of a "Human" NPC is considered to be its feet. An additional particle - the head - is attached to the feet via a spring, and only used for rendering purposes. This allows for bending and angular momentum.
This additional particle's mechanical physics is updated at the same loop as the main particle's mechanical physics, but doesn't participate in anything else.
If the concept works fine, we can enhance it later with additional springs for limbs. We will begin with one single particle though.
The two particles define a simple elongated quad. This quad is then rendered with textures, from an atlas containing all NPC frames.
The texture groups' are the "movement types" (e.g. "walking", "falling", "swimming", etc.) (together with furniture NPC types, such as "table", etc.). Each movement type has 1 or more frames, numbered consecutively.
Color coding:
- TODO: https://www.reddit.com/r/gamedev/comments/3215xb/replacing_texture_colors_in_opengl/
- texture3d:
- Basics: https://stackoverflow.com/questions/20012676/glsl-change-specific-color-of-texture-to-another-color and https://www.reddit.com/r/opengl/comments/1x2rq1/how_can_i_replace_one_color_from_a_texture/
- To create LUT table: http://kpulv.com/359/Dev_Log__Color_Grading_Shader/
Frame rules:
1. Frame size is 64x64
Max 4096 frames
2. Try to maximize canvas usage. Ideally, for the "standing" frame, the feet touch the bottom edge, and the topmost pixel (above the head) touches to top edge
3. Each frame will have to be duplicated for man and woman appearances
4. We're going to use color coding. Basically, some of the colors in each frame will be changed at rendering time to allow for differently-colored uniforms for different roles.
So for example we can have role-specific colors for the pants/skirts, jackets/shirts, and if we want, stripes on the arms and/or legs, and hats
Like at https://www.reddit.com/r/gamedev/comments/3215xb/replacing_texture_colors_in_opengl/
4a. Background has to be full white (255, 255, 255)
If we need actual white in the frame, we can use a slightly darker white
4b. (and this part I still have to think through completely, but we can start with this)
You may use any colors you want, but these are special and will be replaced by role-specific colors
medium red: 128, 0, 0: upper body (shirt/jacket)
light red: 255, 0, 0: lower body (pants/shirt)
medium green: 0, 128, 0: shoes
light green: 0, 255, 0: hat
medium blue: 0, 0, 128: unassigned at this moment (don't use)
light blue: 0, 0, 255: unassigned at this moment (don't use)
5. Walking frames:
5a. 3 frames (1: left leg ahead, 2: both legs vertical, 3: right leg ahead)
5b. The upper body vertical axis needs to be horizontally centered and needs to stay aligned vertically - basically, the animation needs to look fluid if you alternate the three frames always at the same position
Details
===========
Arch
===========
- Type hierarchy:
- NpcType:
- Human
- HumanNpcRoleType:
- Passenger
- ...
- Furniture
- FurnitureNpcType:
- Table
- ...
- One single NPCs container class owned and managed by World
- Render dispatched to individual ShipRenderContext's, as each ship has own Z world
X Though we could have a NpcRenderContext which passes ship's Z in vertex buffers, but then there's the order/alpha problem; best if rendered by each ship individually
- NPCs class:
- Container of NPCs
- Owned by World; accessed - when needed - by Ships via ParentWorld
- Contains all NPCs from all ships
- Organized internally by ship, so that updates - which require ship triangles, etc. - are optimized, and ships may easy access all their NPCs easily e.g. when doing interactions
- For each ship there is a vector (e.g. std::vector<NPCState>), kept compact during removals and additions of NPCs
- The vector of ships is also itself kept compact during removals and additions of ships
- Separately, a "ship index" vector and a "NPC index" vector for each ship form a two-tier "index" for mapping stable ShipIDs and NPCIDs to elements in these buffers
- Index also kept up-to-date at Ship and NPC addition/removal
- The pair of these indices is the NPC's stable, globally-unique ID, unrelated to the position it occupies in NPC buffers
- One single buffer per-physical quantity though, across all ships and NPCs, so that we do physics updates with one single pass
- Buffer is pre-sized to max # of NPCs
- Need same tricks as other physics buffers wrt additions/removals, e.g. zeroing coeffs so that update steps are essentially nop's
- NPC struct:
- Type: NpcKindType
- Union of all kind-specific attributes
- Note: many attributes will be common, hence we'll be saving room
- type: NpcParticleState
- ElementIndex ParticleIndex (index in NpcParticles)
- opt<ConstrainedStateType> ConstrainedState
- TriangleElementIndex
- BaryCoords
- opt<OnFloorStateType> OnFloorState
- EdgeOrdinal
- SimulationTime startTime - TODO: dynamics of when reset
- NpcParticleStateType PrimaryParticleState (i.e. feet)
- opt<NpcParticleStateType> SecondaryParticleState (i.e. head)
- Update(Ship const &)
- Upload(Ship const &, ShipRenderContext)
- Invoked by World for each ship separately
- Invokes ShipRenderContext::UploadNPC:
- A TextureGroup enum - declared in RenderTypes like AntiMatterBomb is for example - is used to identify the "movement types" for rendering. An additional index provides the sub-frame. The whole coordinate is a TextureFrameId<NPC group>.
- TODO: actually the enum might not have to be in RenderTypes; we could use a generic "state" enum in GameTypes which *also* serves as a groups enum
- In the constrained regime:
- A particle always belongs to a triangle
- And its world coords can be translated back and forth into barycentric coords wrt the triangle
- As a special case, the particle may strictly belong to a *side* of that triangle, in which case we assume it's walking on it
- We can store a "sticking" boolean to remember it's attached, though it's simply a special case of barycentric coords saying it's on a *side*
- Core of the simulation of an NPC:
- Starting point:
- SW: World coords: current coords
- SR: Relative coords: barycentric coords of SW in old "state of triangles" (which may just be stored at previous frame)
- Ending point:
- EW: World coords: SW + integration of:
- NPC's velocity
- Floor's friction (only when "sticking" to the floor): f(M * A)
- M = NPC's mass
- A = acceleration experienced by triangle
- Calculated as delta between previous SW and new world position of same barycentric coords
- f(.) = non-linear switch: force only applied if it's less than a threshold
- Floor's counter-gravity
- Only if NPC is on a side of its triangle at beginning of frame
- In the direction perpendicular to the triangle's side
- So that we correctly apply it also when NPC is upside-down
- Forces acting on NPC (gravity, etc.)
- Algo for physics update of one NPC:
- Done at BaryLab
- 0. If current triangle (guaranteed to be set) has been deleted, transition to free regime
- Note: triangle deleted <=> spring deleted, so this is regardless of whether NPC is on spring or not
- 1. F = {g & world forces, ext forces}
- 2. Note: the below is apparently not needed if we take care of impact response when on edge (below); verify that we can take care of friction well in the edge code,
when we have no idea of relative movements
- If NPC on spring:
- And: allow when in floor to move like ghost, so if NPC on spring _and_ not all springs of triangle are floor
- Calculate Fa == apparent force due to difference between (current) world position of NPC (SW) and current position of triangle's pos (World(SR))
- F' = opposite of component of (F+Fa) perpendicular to spring (from inside of triangle)
- Note: only if normal is right direction wrt trajectory
- F' += (non-linear) friction due to normal component of (F+Fa) above, tangential to spring
- Static and kynetic friction:
- http://www.epi-eng.com/mechanical_engineering_basics/force_and_friction.htm
- https://www.motioncontroltips.com/why-is-static-friction-greater-than-kinetic-friction/#:~:text=Static%20friction%20is%20what%20keeps,once%20they%27re%20in%20motion.
- https://openstax.org/books/university-physics-volume-1/pages/6-2-friction
- The direction of friction is always opposite that of motion, parallel to the surface between objects, and perpendicular to the normal force.
- 3. Integrate (F + F') and of course NPC's velocity, calculating EW
- 3b. Reset ext forces
- 4. Perform ray tracing; if collision, move NPC and update velocity (bounce) as usual (i.e. with normal and tangential velocity responses)
- Impact forces: https://www.studysmarter.co.uk/explanations/physics/force/impact-forces/
- Details on ray tracing:
- Perform ray tracing from SR to EW, in order to:
- Determine exit & entry through all triangles in path
- Eventually stop at a floor
- Details:
- Start in SR triangle == ST
- Loop:
- Calculate relative coords of EW in ST == E'R
- If E'R is *within* ST:
- We're done
- Else:
- Determine side of ST that SR->E'R path crossed
- It's gotta be one and only one
- Check if that side is floor; if it is: apply "collision" logic and stop
- (if not floor) Find next triangle (triangle opposite that side)
- If there's no triangle: enter "free regime" and stop
- ST = this new triangle; continue loop
- Details:
- Barycentric coords, and intersection of segment with triangle:
- https://math.stackexchange.com/questions/2382016/determine-if-a-line-segment-passes-through-a-triangle
- Cartesian to Barycentric: https://en.wikipedia.org/wiki/Barycentric_coordinate_system#Conversion_between_barycentric_and_Cartesian_coordinates
- More barycentric coords, and ray tracing w/C algo:
- https://web.archive.org/web/20170517125238/http://www.cs.virginia.edu/~gfx/courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
- Plucker coords, and intersection of segment with triangle:
- https://members.loria.fr/SLazard/ARC-Visi3D/Pant-project/files/Line_Segment_Triangle.html
- 2D: https://www.geogebra.org/m/aRMTsu62
========================================================
Current perf:
- Update: 3.0/3.1
- RenderUpload: 1.1
===================
Next release:
- Lights:
- Option 1: second pass of lighting algorithm with all NPC particles, for each ship
- Adding to NPC particles' _light_ buffer
- But test it first, as humans are dipoles and dubious result
- Option 2: populate NPC particles' _light_ buffer with triangles' endpoints (so only for constrained)
- Misc:
- Ghost Walls bug:
- https://discord.com/channels/559822257275404307/561385724855975938/1309212146508365834
- Test Data/NPC walls bug report ship.shps
- Remove NPC tool for "area"
"Perhaps some sort of selection tool to remove NPCs - removing a large cluster 1 by 1 is a bit annoying"
- RemoveNpcTool: when mouse down on no candidate, enter "selection mode" drawing rectangle
- During selection drawing, talks to GameController in no-NPC language to just draw selection rectangle
- Then at mouse up, call Npcs::RemoveNpcs(AABB region)
- Selectrion rectangle: expand current "Line Guide" into set of them - so can draw quads
- TODO: need to clear via name, as line are sticky and we may have many types
- Injury system - requires further thought
"Something that might be interesting would be to add some sort of injuries system for the NPCs. Doesn't have to be gruesome or gory, but since they seems to be made out of individual joints,
they could have some sort of HP and if a bomb blows off, they might lose their legs or something like that"
- "Gravity doesn't seem to be constant for the NPC and objects. Unless it's just some serious drag that prevents them from falling fast"
! Indeed we have NPC-specific damping
! A too-low NPC-specific damping causes NPCs to bounce forever when in water, though
- I changed the size multiplier of the NPCs to 10, and then shrunk it down to a little above the default, ~1.25 or something similar, and multiple (though not all) of the NPCs seemed to become very oddly proportioned with their torso stretched to what I assume is the size multiple of 10 (leading to very tall figures) though their width was at what I assume to be what I set the multiple to, while these people were also stuck in a diagonal rotation, in short, multiple NPCs became the shape of diagonal baguettes, though would return to normal if I destroyed the structure they were on. All of the affected NPCs were in some form of non-permeable material, so I've concluded that I believe some of the NPCs don't stretch back to their newly-set size multiple because they are stuck in a hull material as a result of being set to a size that put them into that stuck position.
- Explosive NPCs
- Explode at hit
- Just inject explosion state machine @ Ship, should take care of physics _and_ rendering!
- Need mechanism for dynamic removal of NPCs
- Will be useful also for human removal animation (*)
- Option 1: set of NpcId's to remove, scavenged at end of Update()
- BeingRemoved regime: to keep the NPC "out of the loop" for the rest of the simulation step
- No physics, no human behavior update (other than for BeingRemoved itself, updating orientations and progress for animation)
- BeingRemoved is in fact also a human behavior for completeness, for consistency with UpdateHumanBehavior and UpdateAnimation
- When marking an NPC for removal (via helper):
- Add to set
- Set regime and human behavior (iff human)
- New human behaviors:
- Prelim work:
- Backport FS to Barylab
- 1. Commodotize finding alternatives at end of triangle
! Used by: panic checks, forward-looking walk planning, are walk planning
- Extract vertex navigation w/candidates finding into own function
- Args, among others:
- xDir: translated into CCW or CW
- Shortcut to stop search if at least one candidate is found
- Activated at forward-looking walk planning (* below), where we are interested in just knowing whether we can proceed or not
- Return type: TODO: candidates, etc.
- 2: "next triangle" check when planning:
- @ UpdateBehavior while walking (or at WalkingUpdate):
- WalkingBehaviorState contains: tri index, edge ordinal, "half edge sign", and x orientation
- Init'd to None at each transition into Walking
- Used to detect change of "half edge"
- When change detected:
- if half_change*orientation > 0: we're walking into next edge in this direction; do <X>
- Update state with current half
- Panic when water entering
- Panic when sinking detected
- Walk away from water
- Forward-looking walk planning:
! Goal: while walking, do not proceed towards a wall or a hole
- Requires 1. and 2. @ Prelim work above
- @ Walking behavior (* above): when "half-triangle" change detected (* above):
- Find if a next floor exists in cur triangle along walking direction
- Via commoditized helper
- Can not proceed if doesn't find any alternatives
- Flip
! We'll figure out whether we're stuck also in other direction after we cross the half-triangle again
- When human removed, enters "Removal" behavior: rotates with arms 90degree and legs closed, alpha down, then disappears; shader does pixie dust
- Requires delayed removal (@UpdateBehavior), see (*) above at Explosive NPCs
- "BeingRemoved" regime for whole NPC
- Can't be picked when in this regime
- Physics update: skipped when in this regime (kinda like BeingPlaced, but for whole NPC this time)
- Furniture animation:
- Same shader, but no rotation
- Pixie dust shader:
- Quad is "covering" quad: along H->F for humans - considering extended arms - or quad itself for particle
- Then feathers out by a tiny little bit
- Vortex, growing from centerH below to whole rect
- Noise sampled moving L to R
- Tests:
- Stats published
- TODO: need to figure out:
- Shader: existing shader with toggled enrichments, or shader on top?
- Shader on top has potential Z issues?
- When ship is repaired, all NPCs do a little dance
- Sync is simulation clock (so all are in sync)
- Signal from outside (OnShipRepaired) sets global flag
- Global flag is decayed to false (-1)
- At HumanUpdate:
- If state is Equilibrium|Walking: if global flag is set: transition to Dancing
- Elif state is Dancing: if global flag is not set: transition to Equlibrium
- Dancing:
- Transitions out with same out transitions as Equilibrium
- At UpdateAnimation:
- Do dance
- Engineers repair ship
- Ship exposes simplified "Repair" functionality, which simply detaches needed particles from where they are and brings them close
- New human NPC behavior ("Constrained_Repairing") with associated animation
- Entered when wanting to cross a triangle that on the other side is free because it's damaged
- Might also have "Underwater Engineers" with zero buoyancy, unaffected by internal water, who can thus repair when the ship is flooded
- This guys has speed multiplier, applies to walk speed and all other movements that are on a period
- Thanos interactions
- New furniture behaviors:
- Exploding
- Depth charges
- Doors:
- New "DoorHinge" electrical element (with both ON/OFF types), controls floorness of springs (and thus triangle edges) among neighboring pairs of them
- Triangle edge/floors: split "factory" vs. "actual"
- Iff we end up having H1,V1,S2,etc. floor types, as resetting door would need to know that
- Otherwise, if we only have Floor+Open, can live without factory
- However, now we need to set Floor_Geometry_ for every triangle edge, also not floor ones
- And thus NPC code should only look at FloorGeometry when edge is Floor
- Element is conductive, so two neighboring hinges are electrically connected
- Dynamics of door-induced floorplan changes
- Ship has InteriorTexture, so it can re-build parts of InteriorView upon floor state changes
- @ ElectricalElement update:
- For all (one) spring that have another DoorHinge at an endpoint:
- Note: here we don't care whether the spring is deleted or not
- Calculate spring's newFloorness as:
- If at least one open: open; else: floor
- Invoke IShipPhysicsHandler::UpdateNpcFloor(springElementIndex, newFloorness)
- If spring is NOT deleted:
- Sound
- Highlight
- @ IShipPhysicsHandler::UpdateNpcFloor(springElementIndex, newFloorness):
- Find all triangles (0, 1, or 2) that have this spring as an edge
- Change their floorness
- // Need to make sure that two adjacent triangles are done together
- It's a must iff joined by a diagonal spring
- It's a nice-to-have (for perf) iff joined by a H or V spring
- Invoke ShipTexturizer() method (on ShipTexturizer& member) -> new InteriorView texture quad:
- First triangle as TriangleElementIndex (never None)
- Second triangle as TriangleElementIndex (evt. None) // Joined to first
- InteriorView
- Calcs rectangle, creates buffer (copied from rect of InteriorView)
- Quads used for buffer (i.e. workspace):
- // Because drawing an edge goes out to other quads
- One triangle:
- For a diag edge: need L and R quads
- For a vert edge: need either L (if V edge is left) or R (if V edge is right) quads
- For a horiz edge: need either T (if H edge is top) or B (if H edge is bottom) quads
- Two triangles:
- Same as above, and do union
- Proceeds with already-existing internal helper, for each triangle
- Returns also origin of new InteriorView texture quad within InteriorView pixel space
- Store new InteriorView texture quad to a (temporary) list member of Ship, together with origin
- @ Ship::RenderUpload(): gives (std::move) the list to ShipRenderContext which will a) upload to texture as subtextures (on thread!), and b) apply to own Interior view texture (so that it's also
persistent across view switches)
- Initialize door states at ship initialization
- So that floors are the ones in agreement with hinges' operation
- Like we do with Watertight doors and others
- Sounds
- See what we do with Watertight doors and whether they can also act as doors, in which case we can put them in the same category (Watertight door == door but also controlling WaterPermeability)
- Sounds:
- Hit:
- Material has SoundType property; NPC can override with own
- Furniture overrides for e.g. piano
- HumanNPC materials have two different sound types
- New IGameEventHandler event, which takes kinetic energy (dissipated)
- Aggregated
- Invoked @ Npcs bounce
- SoundController
- Transforms kinetic energy to volume
- Sounds:
- NPCHead X {2..4}X[non-uw, uw}
- NPCFoot X {2..4}X[non-uw, uw}
- Piano X {2..4}X[non-uw, uw}
- InternalTexture layer from user:
- ShipDe/Serialization
- Make sure that:
- Old FS can read new ship with layer
- New FS can read old ship (without layer obviously)
- ShipBuilder
- Re-enable/take care of various "TODOHERE"'s for not-yet implemented things
- Ability to auto-generate InteriorTexture from structure
- via ShipTexturizer
- Tests:
- Old FS loading ship with it
- Floorplan layer from user:
- ShipFactory: when a floorplan layer is present, it needs to inform tessellation (i.e. triangle selection) which needs
to use floor information (ipv hull springs) to decide between covering triangles
- Tests:
- Old FS loading ship with it
- NPC layer from user:
- First off, global ship setting for NPC size
- Area walk planning:
- TODO: floor types (normal, assembly, lifeboats, etc.)
- TODO: states (random walk, to assembly, to lifeboats, etc.)
- Separate thread calcs <floor_point>->[distance to each floor area connected component]
! Make calculations unit-testable
- Step 1: visit graph with flooding and store all pointsVedges in N queues, one for each floor type are we want to track
- Step 2: for each queue Qn:
- Breadth first visit of elements in the queue (all elements first, all reachable from those next, etc.), storing distance in n's slot
- Do not revisit a pointVedge if it has been already visited at this full run
- Guaranteed to stop mid-way
- NPC packs:
! Gist:
! An NPC pack is three files:
! An atlas (png and json)
! npcs.json
! At startup, new "Combining NPC Packs" step
! Combines all packs - starting with "Default" - merging jsons (NPC db) and atlases, until eventually exceeds max atlas dimensions
! Driven by three sets:
! All packs found in file system, + "Default"
! List of Accepted packs (saved in UI prefs)
! List of Blocked packs (saved in UI prefs)
! If exceeds, popup (passive) dialog at startup communicating which ones have been blocked, and hinting at Preferences
- Impl:
- static AtlasBuilder::Combine(Atlas 1, Atlas 2) -> Atlas
- Requires changes to main atlas builder func (default one only is ok, no need for Regular)
- FrameProvider callback might require an additional "source ordinal" argument, and extracts RgbaImage from atlas
- Also requires changes to frame IDs so that the output has unique frame IDs
- Also store original source frame ID, needed later to combine NPC databases (frame lookup is from individual json's hence refer to original frame IDs)
- Might also require sorting to guarantee determinism (needs to build same Atlas with same inputs every time)
- Unit tests
- Verify multi-step idempotence
- static NpcDatabase::Combine(NpcDb 1, NpcDb 2, Atlas (combined), ...)
- static NpcPackManager::CombinePacks(Filesystem list, Accepted list, Blocked list)
- Uses AtlasBuilder::Combine plus NpcDatabase::Combine
- NpcDatabase:
- AddDatabase(NpcDatabase const &, TextureAtlas(Metadata?) const &, MaterialDB const &) // (this might be old)
===================
===================
COMPLETED
===================
===================
+ Human behavioral state machine(s):
! Principles:
! Most transitions happen with "convergence" of some quantity
! Only the convergence rate is a state-specific parameter; the threshold for reaching a target is not (i.e. it's a global constant)
! So that there's only one param per state controlling timing
+ States:
+ State:KnockedOut
! Does nothing
+ Transitions out:
+ When whole NPC becomes free: transition to FreeKnockedOut
+ When the following applies for at least Tx: transition to Rising
+ P particle:
+ On (at least one) floor edge (& in constrained state)
+ Zero relative velocity
+ S particle:
+ On (at least one) floor edge (& in constrained state)
+ Zero relative velocity
+ State:Rising
! Tries to stand up (applying torque)
+ Transitions out:
+ When whole NPC becomes free: transition to FreeKnockedOut
+ When the following stops applying: transition to KnockedOut
+ P particle:
+ On (at least one) floor edge (& in constrained state)
+ Zero relative velocity
+ When MaintainAndCheckEquilibrium() returns false: transition to KnockedOut
+ When line from S to P is aligned with GravityDir: transition to Equilibrium
+ External out transitions:
+ Impact: transition to KnockedOut
+ UpdateStep() takes NpcState as well, and calls Npcs::OnHumanNpcImpact(NpcState, impact quantities) upon impact
+ OnHumanNpcImpact: switches on state
+ MaintainAndCheckEquilibrium() -> bool:
+ Does torque calculation and reaches/maintains equilibrium, but also checks if conditions to stay in equlibrium apply; stops equlibrium (and returns false) if:
+ HumanVector is outside of -alpha->alpha sector around vertical, with rotation velocity towards outside of sector
+ Impart torque:
+ With Particles::VoluntaryForces
= Reset at state transitions
+ Formula:
+ Force perpendicular to human vector; magnitude depends on angle
+ See why primary moves: is it because of spring being compressed?
+ It's because at beginning each step stretches it, then the spring recoils to the point that the other endpoint needs to move
+ Force can't be directional - or else it cancels gravity - not that it's important, but highlights un-soundness of approach
+ Go full rotational spring
+ See if can raise faster with less overshooting
+ State:Equilibrium
! Stands up; continues to adjust alignment with torque
+ Transitions out:
+ When whole NPC becomes free: transition to FreeKnockedOut
+ When the following stops applying: transition to KnockedOut
+ P particle:
+ On (at least one) floor edge (& in constrained state)
+ Zero relative velocity
+ When MaintainAndCheckEquilibrium() returns false: transition to KnockedOut
+ When enough time has passed in this state: transition to Walking
+ External out transitions:
+ Impact: transition to KnockedOut
+ State:Walking
! Walks; continues to adjust alignment with torque
+ Transitions out:
+ When whole NPC becomes free: transition to FreeKnockedOut
+ When the following stops applying: transition to KnockedOut
+ P particle:
+ On (at least one) floor edge (& in constrained state)
+ Zero relative velocity (excld. walking)
+ When MaintainAndCheckEquilibrium() returns false: transition to KnockedOut
X External out transitions:
X Impact: transition to KnockedOut
+ Walking state machine:
+ Impart displacement
+ First, allow traj calculation to also pre-calculate final (absolute) velocity
+ Traj calc state keeps it, "TargetAbsoluteVelocity"
+ UpdateTrajState() returns it when completing w/out impact
+ finalParticleState has velocity not as optional but as mandatory
+ No more automatic vel calc'n at end of UpdateTrajState()
+ Always provide target vel from within UpdateTrajState()
+ Assert it matches a calculated one - when no impact has happened
+ Then: at traj calculation, superimpose raw walking displacement (calcd at HumanUpdate and stored in VoluntarySuperimposedDisplacement) to traj, without affecting velocity
+ FIX: secondary trails primary
+ @ TrajCalculation: when we incorporate walking displacement, if it's primary we save same vector as displacement of secondary
+ At HumanNpcStateUpdate: clear also secondary displacement on transitions out
+ Problem: while walking, difference in slope is giving us troubles:
+ When moving from edge floor to edge floor with lower slope, we lose contact with edge
+ And this happens because once we climb to other triangle, the trajectory end in bary coords does not lie anymore on edge
+ When moving from edge floor to edge floor with higher slope, primary has impact and gains bounce velocity (because we check traj)
+ Solution: might need to change how impact works, if walking reaching slope that it should try to climb causes noticeable impact
+ We can solve it @ impact detection by checking slope first, and not causing impact bounce when slope is gentle - but still climbing over edge in order to "land" on slope
+ Do rearc
+ RETHINK: do we really want primary non-inertial to gain physical velocity when walking? Example: climbing up, gains however down velocity until this one overpasses walking
- May be it's OK after all...
! Also: BUG: impact of secondary when discounting walk vector results in anti-impact
X Algo 1 for fix:
! If result is opposite sign of phys, shouldn't gain phys but zero instead
! If result is opposite sign of zero, should gain zero (this is now)
! In other words: velocity gain should never be opposite of result (actual)
! Generalization: velocity gain max should be:
! Result > 0: max(phys, 0) // e.g. climbing up on \* : only gains > 0
! Result < 0: min(phys, 0) // e.g. climbing up on */ : only gains < 0
X Do:
X Do new velocity calculation at 3 places (TODOHEREx):
= 1: NonConstrained move-to-place: here we do with edgeDir
= 2: Constrained move-to-place: here we do with absolute positions; velocity is:
- Direction: of result (i.e. result_dir)
- Magnitude: as above, with phys projected onto result_dir
- TODOHERE: conflicts with torque!
- 3: Bounce when calculating incident velocity - incident velocity must be same as in 1 and/or 2
- Double-check passed walked total/planned vs primary/secondary
- Primary: totalEdgeWalkedActual starts from zero and grows step-by-step
- Secondary: totalEdgeWalkedActual is resultant from primary since the beginning, and immutable
+ Algo 2 for fix:
! Add walk *displacement* calculated to *fill-in* (desired) walk vector
! If (planned) physics is already more, then displacement is zero
! The displacement then takes part in normal velocity calculations
! This displacement is the one which, summed up, goes to secondary
+ Plan:
+ Undo other changes (surgically)
+ Main loop:
+ Primary: new plannedWalk calculation
+ if walk in same direction as edgeDir:
- plannedWalkVector = edgeDir * max(walk desired along edge - planned phys along edge, 0)
OR - so that we don't walk on too steep:
- plannedWalkVector = edgeDir * min(max(walk desired along edge - planned phys along edge, 0), walk desired along edge)
+ else:
- ...
+ actualWalk is then added to total (as now)
+ plannedWalk is added to plannedPhys to make plannedTotal (as now)
+ Secondary:
+ actualWalk taken from primary (as now)
+ All three velocity calculations:
+ Back to basics: just take whole, real displacement - hear nothing of "walked"
+ Remove "walked" and "phys/total" args
+ Unless phys/total is used for assert - keep "total" for that
+ Problem fix
+ Disable storing equilibrium torque
+ Disable secondary displacement
X At phys force calc: if NPC is walking and this is secondary, calc additional force - on top of calcd - needed to bring sec'ry on top of primary
X Do in "distance" space
+ Do normal torque here
+ Things to fix:
+ Bump with vertical wall: going back (when floor is sloped down-right) is not constrained anymore because of |\*| <--
! Which means we do another bump (albeit negligible but still against direction, dot is 0.06) and thus another flip
+ Option 1: at main loop: if we are at a cuspid, we shouldn't be too hasty in deciding non-inertial vs inertial; should move out of cuspid first
+ Almost same loop as in NonInertial: until in triangle or becomes free or hits floor
+ Walks on too steep of a wall
+ Happens because friction zeroes out G
X This should be fixed with less friction
+ Two-sided fix:
+ Clip and squash dot product at actual walk plan calculation ("apply G resistance")
+ MaintainWalk: flip if too long at V opposite of W
+ 1: Check condition (V opposite of W); if true: TargetFlipDecision=1.0; else: Target&CurrentFlipDecision=0.0
+ 2: Advance CurrentFlipDecision towards TargetFlipDecision
+ 3: If CurrentFlipDecision > 0.95: flip, & set Target&CurrentFlipDecision=0.0
+ Replaces having to flip at well detection? Verify it can, both sides
+ Turn off first for test
+ Verify it replaces it
+ Set FlipDecisions to zero at bounce flip
+ HumanFlip(isImmediate)
+ Called by both places at which we flip
+ Later: test with a margin above @ 1 so that we also flip when yes V in same direction as W but not quite there
+ FIX: looping around 0.0/~-0.0
+ See if can lose equilibrium when secondary too skewed but not too much relative opposite vel because of secondary being blocked
+ In equilibrium only, CheckEq breaks Eq at lower threshold - even when relative V is same sign but quite low
+ Slow when walking downhill: because of "gravity resistance", which shouldn't apply when downhill (only uphill)
+ Do better curve w/Smoothstep
+ "GravityAllowance"
+ Higher static and kinetic friction
X Google V for change from S to K
+ Lower walking speed
+ Fix torque w/mesh velocity
+ Is it that at torque calc we see primary already moved - by a lot - while secondary hasn't moved yet?
+ Video
+ Code for script
+ Fix little jump
+ Add tolerance
+ Don't invoke walk update when not on edge - w/comment on not needing to silence walk mag as we don't apply walking when inertial (i.e. when not on edge)
+ Test flying off
+ "Jump tolerance" also for other two states
+ 1. Thus regardless of state
+ HumanState.CurrentEquilibriumSoftTerminationDecision
+ Reset also at transitions to Rising & Equilibrium
+ Do not start walking if not on edge
+ Test w/walk & ball
+ Commit before next one
+ 2. Do not apply torque while not on edge
+ Go back to having NpcParticle float buffer (w/1.0 or 0.0) for torque
+ reset() buffer before NPC loop
+ w/note for prelim forces
+ Set it from CheckAndMaintainEq(...isOnEdge...)
+ Simply iff on edge
X Make sure we zero it when leaving any of the states
+ Use at torque calculation
+ Flip also when *secondary* bounces
+ Sea level:
+ Param, UI
+ World forces: if free
+ Find sweet spot for thickness of interface/buoying at shoulders
+ Offset for sea level to account for head
+ Forces rearc:
+ NpcParticle has PreliminaryForces
+ All those we can calc @ beginning
+ Npcs::CalculatePreliminaryForces()
+ Takes care or cleaning - if needed
+ Npcs::CalculateDefinitiveForces()
+ Material buoyancy
+ Rendering
+ Cleanups if ok:
+ CalculatePhysForces
+ Old torque
+ And "voluntary" buffer for torque force?
+ Do we still need "voluntary" displacement of secondary?
+ Make triangle-finding (rotate through vertices/cuspids) a function ("NavigateVertex")
+ Return: <do_stop> (note: no need to return traveled - it never moves)
+ Issues found along the way:
! With this func we bounce when we normally didn't
! e.g. in main loop: old: when detecting floor while navigating, we'd stop, and then yada yada (flatten -> move to target as it's in triangle);
new: we bounce instead
+ Fix: return indication of bounce but not bounce
+ FloorEncounteredOutcome/intervening
+ ~became free
+ ~completed navigation
+ Integrate with NonConstrained loop
+ Test
+ Cleanup old code
+ "TODO: get rid of this" (local triangle index)
+ @BlabTypes: baryCoords // copy vec3f parts we need
+ TryGetVertexOrdinal() -> opt<int>
+ When finding triangles (e.g. opposite triangle, but also initializations), check if triangle is deleted
+ Test:
+ Flying off
+ Primary into inertial
+ Secondary into inertial
+ Primary into free
+ Secondary into free
+ Starting at vertex
+ With mesh moving
+ Video
+ Animation:
+ Store current edge
! Needed for foot points
+ ConstrainedState::CurrentVirtualEdge // not really valid when at cuspids
+ Set after call to NonInertial
+ Render it (dark red)
X FIX: when trying to walk up, virt edge is the vertical one
! see below, at walking points-along-edge - see how it goes - WELL
+ Move behavior update: update behavior before animation, after physics
+ FIX: it's broken
+ Because we lose EquilibriumTorque, move reset afterwards
+ Test
+ Shader
+ Basic
+ Do ellipses
+ Store total travelled (displaced)
+ New HumanState member: float TotalEdgeTraveledWhileWalking
+ Reset it when entering Walking state
+ Also in inertial mode, as we now might be walking
+ Do it now and see if it looks weird
+ Decide where to put human body proportion constants
+ LabParameters
+ Move points (new Human members) @ Update
! Each state is responsible for it
+ Walking:
+ 1. Formula calculating angle, as rotation of legs
+ Angle members, and points as well (points will be useful for more calc's later, see below)
! We want angles so that we can smooth-merge sequences
+ CurrentLegRightAngle, no need for Target
+ AnimationUpdate calc's targets and smooths currents to it
+ Formula:
+ A: atan-based along horiz (horiz because human is vertical)
+ Calc points: trivial
+ Adjust step size
+ 2. Constrain points to be along current edge's direction
+ Direction dictated by angle, it's only *length* of leg that changes to stop at current edge
+ Option 1: https://math.stackexchange.com/questions/406864/intersection-of-two-lines-in-vector-form
- Option 2: by calculating from a "virtual crotch" perpendicular to edge
+ 3. Limit leg angles based on slope
+ Smooth out transition between current virtual edge presence & slope:
+ First off: if and when smoothing points, we don't smooth absolute positions (as limbs would elongate/shrink with fast moves) but rather angle & length,
or position relative to shoulder/crotch
! Same is for angles, but that comes by itself: angle gives position only after taking current absolute positions into account
+ New plan:
+ Each switch case calcs target angle and target length
+ Function then smooths those and calcs vectors - relative to shoulder (F/B), crotch (F/B)
+ Finish target leg length calculation
+ Lengths:
+ head, neck, shoulder, crotch, feet: based on current dipole length
+ arms, legs: based on ideal * adjustment
+ Make human walk up slope faster
+ Change formula
+ Add arms
+ Redo whole body, make proportion constants at top of file
+ Fix "running away" issue:
! Repro: start at vec2f const position = vec2f(-0.634f, -2.0f);
! At second iteration of main loop, dt is very small but trajectory is still original:
+ 0: Build repro with ball & velocity first
+ Attempt 1: budget-based:
+ 1: first flattening stores "budget" == length (abs) of flattened traj
! "budget" is the maximum *total* distance that we are allowed to travel in current time quantum
+ 2: "budget" is reduced after each iteration by (abs) scalar amount traveled (capped to zero - though shouldn't be needed)
+ 3a: any subsequent non-inertial flattening adjusts flattened trajectory to ensure its abs length is not over budget
X Adjust how we calc dt consumed - no need as long as we update edge total planned
X Adjust physical planned - no need as it's not used anymore
+ Adjust walk planned, which is used
X 3b: any subsequent inertial travel adjusts trajectory to ensure its abs length is not over budget
! Not needed: velocity here is calc'd from total trajectory in step divided by theoretical (whole) dt, so it's really physical
+ Then:
X a: Fn calculation (for friction) must take dt into account
! Actually, is not needed - N and FT would both be smaller - as smaller as T is
X b: Also bounce response must take dt into account?
! Actually, is not needed - bounce response is calc'd based on (flattened) trajectory and (remaining) dt
+ c: Impact continuation must also calc velocity as it might be last
! Next step might have 0 to travel after we shorten up traj
+ Test assert on non-inertial always leaving a crumble of distance to travel wrt budget
+ Seems to hold
+ Finalize, Cleanup, Merge
+ Fix "neverending navigation" issue:
+ Build repro
~ Do dual-side (0.0 and 1.0) clamping for each coord
Opposite Tr 46 oppositeTriangleCrossedEdgeOrdinal=0 newBCoords={_Elems=0x00000017b298da68 {1.00000000, 0.00000000, 0.00000000} }
vertexOrdinal=0 TrajEndBCoords={_Elems=0x00000017b298ddd0 {0.998306274, 0.00169372594, -3.49245965e-10} }
preV=2 nexV=1 => crossedEdgeOrdinal=0
Opposite Tr=81, oppositeTriangleCrossedEdgeOrdinal=1 newBCoords={_Elems=0x00000017b298da68 {0.00000000, 0.00000000, 1.00000000} }
vertexOrdinal=2 TrajEndBCoords={_Elems=0x00000017b298ddd0 {-1.61526414e-09, 0.00169372361, 0.998306274} }
prevV=1 nextV=2 => crossedEdgeOrdinal=1
Opposite Tr 46 oppositeTriangleCrossedEdgeOrdinal=0 newBCoords={_Elems=0x00000017b298da68 {1.00000000, 0.00000000, 0.00000000} }
vertexOrdinal=0 TrajEndBCoords={_Elems=0x00000017b298ddd0 {0.998306274, 0.00169372594, -3.49245965e-10} }
81:
|\
| \
| \
----
\ |
\ |
\| 46
+ Now that we clamp, we have the issue of bouncing once reached the edge - because of walk velocity going up
! Repro: vel=1.8, slightly rotated CCW
X Attempt 1: do not gain y velocity from walk
! But does it make sense? If we run up, we have velocity up...no?
X Attempt 2: limit y velocity from walk
+ Attempt 3: better bounce tolerance and equilibrium termination
+ Bounce tolerance to slope vs walking dir
! Repro: max speed & mesh rotated CW (1xV)
+ Equilibrium termination: provided we're reasonably vertical, if we lose it because of non-edge, be more tolerant
+ But now we have relative velocity kicking in
! Rethink relative velocity - after all we have walking velocity now!
+ If not walking: like now
+ Else:
X If against walk dir by limit1: break
! We do not need this - after all we flip - and also generates issue below (slamming against wall)
+ If more than walk velocity by limit2: break
+ Only consider component of MRV along walk dir
+ FIX: when we slam hard against a wall, we flip but still have old V => it looks like a lot of opposite
+ Attempt 1: see above: removing "Walking state abandonment" when too much opposite vel fixed this
+ Sfarfallio when we reach slope
! Repro: speed=1.75, rotated cw a bit (6 x -3.1f * 12.0f)
! We do impact continuation & try go up slope, but too much friction/nowalk/etc. and no move and go down
! We end up in a well, alternating last edge: 14-17-14-17 - detected well; 17-14-17-14 - detected well; 14-17-14-17...
+ Should detect well at second 14
+ New issue: freezing along wall:
! Repro: like for bounces above: vel=1.75, slightly rotated CCW (4 rotations of 3.1f * 3.0f)
! We are at 102
! We navigate to 137 (returning EncounteredFloor)
! Queue: 137 137 137
! We do non-inertial step: 102
! We don't see 102 in queue
! Loop
+ Fix:
+ Initialize with first before machinery
+ Update _after_ non-inertial step
+ Verify both well issues are fixed
+ Video
+ Put back temporarily orig values
+ Per-human PanicLevel [0.0f, +inf)
+ Walking anim: when !=0 -> arms still flipping but up
+ Adds to walking speed
+ Npcs::CalculateActualHumanWalkingAbsoluteSpeed(humanState, labParams)
+ Panic, Magnitude, labParams
+ Use it wherever magnitude is used
+ Test with max walking speed
+ Adds (marginally) to equilibrium torque
+ Increases converge rates for start rising
+ Lowers time to start walking
+ GUI slider
+ Npcs::SetPanicLevelForAll(panicLevel)
+ Play
+ RenderUpload: implement
+ F/B vs L/R geometry
+ Front
+ Back
+ Left
+ Right
+ Test flip
+ API calls take L/R/F/B info
+ API also takes *quads* rather than vector+width, for perf
+ Rendering honors L/R/F/B with shader hints
+ depth
+ L/R
+ Animation improvements:
+ Shading also at F: centered
+ direction basically shifts, can use it verbatim
+ Panic arms: now they look like it's flying :-)
+ Try: simply flap them around vertical _up_ rather than down
+ Panic arms at slope: npc is slow and arms also - should use time for arms
+ Swimming
+ Flight down should be faster
+ Do poor man's "mod" with asymmetric interval (ifs)
+ Also bottom angle should become more "flat" (around pi/2) when at surface
+ Don't start until rotation is small
+ Free_Aerial and Free_InWater - where we do "arms pointing downward": also move legs somehow
+ When arms are at highest, legs are 100% tight; when arms are at lowest, they are X apart (same X as now, initial)
+ Around center angle that is opposite to relvel component against perp to humanDir, with magnitude depending on that relval component magnitude
+ Swimming: speed affected by panic
+ Constrained_KnockedOut rearc:
+ Double-check all transitions _to_ Constrained_KnockedOut
+ Falling: constrain arm angles to the half-plane in the face direction
+ Try to progress aerial to falling (rather than immediately)
+ Also gets rid of becoming falling when hitting head
+ Try to aerial->falling also for head
@ Falling: arms should not extend perp...
a) in air when still on the back, and
b) against floor when on belly
+ Scale target by abs magnitude of head rel/abs vel along perp
+ Fight bounces
X Anim @ Falling: move arms towards zero depending on progress to knockout
X Avoid arms going up when we become Falling when we're basically still
+ Less walking in the air
+ Could have two "distance traveled" members: one on_edge, one not on edge (exclusive)
+ on_edge only updated with NonInertial
+ animation for C_Walking uses linear combo of both
+ animation for swimming uses not-on-edge
+ Less "dead body" when KO
+ Transition to aerial
+ Rising improvements:
+ Go to "rest legs"
+ Convergence rate = 0.2 + depth
+ Go to "rest legs", but leg against floor does less angle
+ Arm against floor helps body come up
+ Arm should open w/body rising up
+ Rethink other arm
+ Helps first
+ Rethink legs
+ Try quadratic arms raise
+ C_Aerial -> C_Falling: at this moment it makes for an ugly start, as we touch the floor and flip immediately
+ Do transition to falling only if there's some movement of H or F
+ Use only applicable velocity's *component along edge|H*
+ Need a transition to (somewhat) rising or else we'll never start walking
+ Go to knocked out if H|F on floor but no velocity?
+ @ Animation: check constrained-ness when we touch it
+ * Make UpdateNpcParticle_ConstrainedNonInertial return <opt?totalEdgeWalked, doContinue> as tuple
+ Fix mesh rendering: do not render all edges but just triangles'
! With repetitions is fine...
+ Do state transitions via func: TransitionState(newState, currentSimTime); func switch'es
+ Npcs::Update takes float currentSimulationTime
+ Uses union-structs internally for resetting & setting up (** below)
+ Resets TotalWhateverSinceWalking and StateStartSimulationTimestamp
+ HumanState."TotalWhateverSinceWalking" -> "TotalDistanceTraveledSinceStateStart"
! For swimming et al
+ Also for Free state (for swimming)
X @ transitions (i.e. "becoming") is kinda pointless, but try to do anyway
+ Also timestamp of state start
+ Reset at state transitions
+ Do state trans via func: TransitionState(newState, currentSimTime); func switch'es
+ Uses union-structs internally for resetting & setting up (** below)
+ Floor @ triangles
+ Code rearcs & integration - 1 (BL only):
! Goal: be able to copy folder of files seamlessly across FS and BL
+ LogMessage/LogDebug -> LogNpcDebug (w/local #ifdef)
+ Make Barylab branch
+ Renames (files and classes)
+ Mesh->Ship
+ And put in Physics namespace
+ Redo OppositeTriangles @ Triangles (array of 3)
+ ShipBuilder
+ Use it instead of other call: replace all calls to Edges::GetOppositeTriangle
+ And cleanup edge element indices if we don't need them
+ We need them to get opposite edge - store alsi this in OppositeTriangles
+ Edges->Springs
+ And put in Physics namespace
+ Vertices->Points
+ And put in Physics namespace
+ Fuse StructuralMaterial/Database into FS
+ Rename both to FS: Materials and MaterialDatabase
+ Split current StructuralMaterial into:
+ StructuralMaterial
+ NpcMaterial
+ Enum with all materials, as we'll be picking them in code based on NpcType
+ MaterialDatabase:
+ Loads two jsons (materials_structural.json and materials_npc.json)
- Split current json
+ MaterialDatabase: expose getters for both
+ The getters for StructuralMaterial will stay private to BaryLab
+ The getters for NpcMaterial will be copied to FS
+ NPCs takes DB
+ "EventDispatcher" -> "GameEventDispatcher"
+ All "BLab" -> "Game"
+ LabParameters
+ Rearc source files in NpcSimulation folder
! No namespace: some types are in GameTypes anyway...
! Not VS GUI
+ Npcs, Npcs_*, NpcParticles
+ Move what used to be GameCore files to GameCore lib
+ Then rearc all #include's for dual "section"
+ Merge NpcParticles: FS + BL => BL
+ Copy NpcParticles' "reservation" system from FS
+ Integrate into Npcs::Add(...)
+ See anything else in FS that is not in BL
+ Finalize NpcParticles:
+ Getter for # of free particles remaining (replaces current getter for # of particles)
+ ~Merge Npcs: FS + BL => BL
+ Kind hierarchy (@GameType):
+ NpcKind (Furniture, Human)
+ HumanNpcKind, FurnitureNpcKind
+ Reflected into union of state structs in Npc::StateType
! Independent from NpcMaterial::KindType
+ NPCs container from FS (main + index, ship &, etc.)
! Use cases:
! 1. Reaching all NPCs of a specific ship (e.g. because of ship-wide interactions, such as electrical tool, alarm, deleting ship, etc.)
! 2. Allow an NPC to move ships (e.g. the one "being placed")
! 3. Reaching an NPC by its ID
+ Design:
+ Single vector of (opt) StateType - null'ed when removed
+ Stable index
+ NpcId is shipless, and an index in the above
+ Separate vector (shipID-indexed) of opt containing vectors (indices in single vector of StateType)