jak-project/goal_src/jak2/engine/process-drawable/process-taskable.gc
ManDude 18ddd1613c
Jak 2 pc subtitle support (#2672)
Adds support for adding custom subtitles to Jak 2 audio. Comes with a
new editor for the new system and format. Compared to the Jak 1 system,
this is much simpler to make an editor for.

Comes with a few subtitles already made as an example.
Cutscenes are not officially supported but you can technically subtitle
those with editor, so please don't right now.

This new system supports multiple subtitles playing at once (even from a
single source!) and will smartly push the subtitles up if there's a
message already playing:

![image](https://github.com/open-goal/jak-project/assets/7569514/033e6374-a05a-4c31-b029-51868153a932)

![image](https://github.com/open-goal/jak-project/assets/7569514/5298aa6d-a183-446e-bdb6-61c4682df917)

Unlike in Jak 1, it will not hide the bottom HUD when subtitles are
active:

![image](https://github.com/open-goal/jak-project/assets/7569514/d466bfc0-55d0-4689-a6e1-b7784b9fff59)

Sadly this leaves us with not much space for the subtitle region (and
the subtitles are shrunk when the minimap is enabled) but when you have
guards and citizens talking all the time, hiding the HUD every time
anyone spoke would get really frustrating.

The subtitle speaker is also color-coded now, because I thought that
would be fun to do.

TODO:
- [x] proper cutscene support.
- [x] merge mode for cutscenes so we don't have to rewrite the script?

---------

Co-authored-by: Hat Kid <6624576+Hat-Kid@users.noreply.github.com>
2023-06-08 01:04:16 +01:00

484 lines
18 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: process-taskable.gc
;; name in dgo: process-taskable
;; dgos: GAME, COMMON
;; DECOMP BEGINS
;; WARN: Return type mismatch process-focusable vs process-taskable.
(defmethod relocate process-taskable ((obj process-taskable) (arg0 int))
(if (nonzero? (-> obj task))
(&+! (-> obj task) arg0)
)
(the-as process-taskable ((method-of-type process-focusable relocate) obj arg0))
)
(defbehavior process-taskable-anim-loop process-taskable ((arg0 (function process-taskable object)))
"Takes in a function and loops as long as it's return value is truthy
Seen take in - `true-func` which takes no args TODO - seems fishy
- a `(process-taskable process) lambda"
(while (arg0 self)
(let ((s5-0 (get-art-elem self)))
(when (!= (ja-group) s5-0)
(ja-channel-push! 1 0)
(set! (-> self skel root-channel 0 frame-group) (the-as art-joint-anim s5-0))
)
)
(suspend)
(if (ja-group)
(ja :num! (loop!))
)
(process-taskable-method-36 self)
)
0
(none)
)
(defmethod process-taskable-method-34 process-taskable ((obj process-taskable))
#t
)
;; WARN: Return type mismatch art-joint-anim vs art-element.
(defmethod get-art-elem process-taskable ((obj process-taskable))
"Checks various things such the current actor, task status, etc to determine the right art-group data to use
@returns the appropriate [[art-element]] for the given NPC"
(the-as art-element (if (> (-> obj skel active-channels) 0)
(-> obj skel root-channel 0 frame-group)
)
)
)
(defmethod process-taskable-method-36 process-taskable ((obj process-taskable))
0
(none)
)
(defmethod process-taskable-method-37 process-taskable ((obj process-taskable))
(let ((v1-1 (-> obj draw shadow-ctrl)))
(cond
((and (-> obj draw shadow)
(zero? (-> obj draw cur-lod))
(logtest? (-> obj draw status) (draw-control-status on-screen))
)
(shadow-control-method-13 v1-1 (-> obj draw origin) -4096.0 4096.0 32768.0)
)
(else
(logior! (-> v1-1 settings flags) (shadow-flags disable-draw))
0
)
)
)
(none)
)
(defstate hide (process-taskable)
:virtual #t
:event (behavior ((proc process) (arg1 int) (event-type symbol) (event event-message-block))
(the-as object (case event-type
(('say)
(let ((v0-0 (current-time)))
(set! (-> self want-to-say) v0-0)
v0-0
)
)
)
)
)
:enter (behavior ()
(set! (-> self state-time) (current-time))
(logior! (-> self draw status) (draw-control-status no-draw-bounds))
(let ((v1-6 (-> self root-override root-prim)))
(set! (-> v1-6 prim-core collide-as) (collide-spec))
(set! (-> v1-6 prim-core collide-with) (collide-spec))
)
0
(none)
)
:exit (behavior ()
(logclear! (-> self draw status) (draw-control-status no-draw-bounds))
(let ((v1-3 (-> self root-override root-prim)))
(set! (-> v1-3 prim-core collide-as) (-> self root-override backup-collide-as))
(set! (-> v1-3 prim-core collide-with) (-> self root-override backup-collide-with))
)
(none)
)
:trans (behavior ()
(let ((v1-1 (get-current-task-event (-> self task))))
(if (and (nonzero? (-> v1-1 action)) (or (not (logtest? (-> self draw status) (draw-control-status on-screen)))
(logtest? (-> v1-1 flags) (game-task-flags gatflag-01))
(< (- (current-time) (-> self birth-time)) (seconds 0.1))
)
)
(go-virtual idle)
)
)
(none)
)
:code (the-as (function none :behavior process-taskable) sleep-code)
)
(defstate idle (process-taskable)
:virtual #t
:event (behavior ((proc process) (arg1 int) (event-type symbol) (event event-message-block))
(case event-type
(('attack)
(when (-> self bounce-away)
(let ((a1-3 (new 'stack-no-clear 'event-message-block)))
(set! (-> a1-3 from) (process->ppointer self))
(set! (-> a1-3 num-params) 2)
(set! (-> a1-3 message) 'shove)
(set! (-> a1-3 param 0) (the-as uint #f))
(let ((v1-5 (new 'static 'attack-info :mask (attack-info-mask shove-back shove-up id))))
(let* ((a2-2 *game-info*)
(a3-2 (+ (-> a2-2 attack-id) 1))
)
(set! (-> a2-2 attack-id) a3-2)
(set! (-> v1-5 id) a3-2)
)
(set! (-> v1-5 shove-back) 12288.0)
(set! (-> v1-5 shove-up) 4096.0)
(set! (-> a1-3 param 1) (the-as uint v1-5))
)
(send-event-function proc a1-3)
)
)
)
(('touch)
(send-shoves
(-> self root-override)
proc
(the-as touching-shapes-entry (-> event param 0))
0.7
6144.0
16384.0
)
)
(('say)
(let ((v0-0 (the-as object (current-time))))
(set! (-> self want-to-say) (the-as time-frame v0-0))
v0-0
)
)
)
)
:enter (behavior ()
(set! (-> self state-time) (current-time))
(none)
)
:exit (behavior ()
(logclear! (-> self draw status) (draw-control-status no-draw))
(none)
)
:trans (behavior ()
(let ((gp-0 (get-current-task-event (-> self task))))
(cond
((= (-> gp-0 action) (game-task-action hide))
(if (or (not (logtest? (-> self draw status) (draw-control-status on-screen)))
(logtest? (-> gp-0 flags) (game-task-flags gatflag-01))
)
(go-virtual hide)
)
)
((or (not *target*) *scene-player*)
)
((not (-> self will-talk))
(if (>= (- (-> *display* game-clock frame-counter) (-> self last-talk)) (seconds 5))
(set! (-> self will-talk) #t)
)
)
((and (-> gp-0 scene)
(begin
(if (not (or (demo?)
(= (-> gp-0 action) (game-task-action play))
(-> *setting-control* user-current movie-name)
(name= (-> gp-0 scene) (-> *setting-control* user-current movie-name))
)
)
(gui-control-method-12
*gui-control*
self
(gui-channel art-load)
(gui-action queue)
(the-as string (-> gp-0 scene))
0
-99.0
(new 'static 'sound-id)
)
)
(and (not (logtest? (focus-status dead in-air in-head pole flut tube pilot mech dark) (-> *target* focus-status)))
(and (or (and (< (-> (target-pos 0) y) (+ (-> self root-override root-prim prim-core world-sphere y) (-> self talk-height)))
(let ((s5-0 (get-trans self 2))
(f30-0 (if (= (-> gp-0 distance) 0.0)
(-> self talk-distance)
(-> gp-0 distance)
)
)
)
;; PC PORT NOTE : added check so we don't use the wrong position for the distance check
(and (not (logtest? (-> self draw status) (draw-control-status uninited)))
(< (vector-vector-distance (target-pos 0) s5-0) f30-0))
)
)
(< (- (current-time) (-> self want-to-say)) (seconds 4))
)
(or (not (load-in-progress? *level*)) (= (-> gp-0 action) (game-task-action say)))
(and (not (movie?))
(not (talker-displayed?))
(none-reserved? *art-control*)
(not *progress-process*)
(not (-> *setting-control* user-current movie))
)
)
)
)
)
(when (and (or (= (-> gp-0 action) (game-task-action say))
(= (-> gp-0 action) (game-task-action talk))
(= (-> gp-0 action) (game-task-action trade))
(= (-> gp-0 action) (game-task-action play))
)
(process-taskable-method-34 self)
)
(when (or (= (-> gp-0 action) (game-task-action say)) (< (- (current-time) (-> self want-to-say)) (seconds 4)))
(case (-> gp-0 action)
(((game-task-action play))
(go-virtual play-game gp-0)
)
(else
(go-virtual active gp-0)
)
)
)
(kill-current-talker (the-as symbol '()) '(daxter voicebox ambient) 'exit)
(talker-surpress!)
(when (can-display-query? self (the-as string #f) -99.0)
(let ((s5-1
(new 'stack 'font-context *font-default-matrix* 32 280 0.0 (font-color default) (font-flags shadow kerning))
)
)
(let ((v1-84 s5-1))
(set! (-> v1-84 width) (the float 340))
)
(let ((v1-85 s5-1))
(set! (-> v1-85 height) (the float 80))
)
(let ((v1-86 s5-1))
(set! (-> v1-86 scale) 0.9)
)
(set! (-> s5-1 flags) (font-flags shadow kerning left large))
(print-game-text (lookup-text! *common-text* (-> self talk-message) #f) s5-1 #f 44 (bucket-id progress))
)
(when (cpad-pressed? 0 triangle)
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons triangle))
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons triangle))
(case (-> gp-0 action)
(((game-task-action play))
(go-virtual play-game gp-0)
)
(else
(go-virtual active gp-0)
)
)
)
)
)
)
)
)
(process-taskable-method-37 self)
(none)
)
:code (behavior ()
(process-taskable-anim-loop (the-as (function process-taskable object) true-func))
(none)
)
:post (behavior ()
(if (and (-> self hide-during-movie) (movie?))
(logior! (-> self draw status) (draw-control-status no-draw))
(logclear! (-> self draw status) (draw-control-status no-draw))
)
(when (-> self look-at-me)
(target-look-at-me! :trans (get-trans self 2))
)
(transform-post)
(none)
)
)
(defstate active (process-taskable)
:virtual #t
:event (-> (method-of-type process-taskable hide) event)
:enter (behavior ((arg0 game-task-event))
(set! (-> self want-to-say) 0)
(process-entity-status! self (entity-perm-status no-kill) #t)
(logclear! (-> self mask) (process-mask actor-pause))
(none)
)
:exit (behavior ()
(set! (-> self last-talk) (-> *display* game-clock frame-counter))
(if (< (- (current-time) (-> self want-to-say)) (seconds 4))
(set! (-> self will-talk) #t)
(set! (-> self will-talk) #f)
)
(process-entity-status! self (entity-perm-status no-kill) #f)
(logior! (-> self mask) (process-mask actor-pause))
(logclear! (-> self draw status) (draw-control-status no-draw))
(let ((v1-13 (-> self root-override root-prim)))
(set! (-> v1-13 prim-core collide-as) (-> self root-override backup-collide-as))
(set! (-> v1-13 prim-core collide-with) (-> self root-override backup-collide-with))
)
(none)
)
:code (behavior ((arg0 game-task-event))
(when (-> arg0 scene)
(let ((gp-1 (ppointer->handle (process-spawn scene-player :init scene-player-init (-> arg0 scene) #t #f))))
(while (and (handle->process (the-as handle gp-1)) (not (movie?)))
(suspend)
)
(logior! (-> self draw status) (draw-control-status no-draw))
(let ((v1-17 (-> self root-override root-prim)))
(set! (-> v1-17 prim-core collide-as) (collide-spec))
(set! (-> v1-17 prim-core collide-with) (collide-spec))
)
0
(while (handle->process (the-as handle gp-1))
(suspend)
)
)
)
(go-virtual hide)
(none)
)
)
(defstate play-game (process-taskable)
:virtual #t
:enter (-> (method-of-type process-taskable active) enter)
:exit (-> (method-of-type process-taskable active) exit)
:code (behavior ((arg0 game-task-event))
(let ((gp-0 (current-time)))
(until (>= (- (current-time) gp-0) (seconds 0.5))
(suspend)
)
)
(go-virtual hide)
(none)
)
:post (-> (method-of-type process-taskable idle) post)
)
(defmethod process-taskable-method-31 process-taskable ((obj process-taskable))
(let ((s5-0 (new 'process 'collide-shape obj (collide-list-enum usually-hit-by-player))))
(let ((s4-0 (new 'process 'collide-shape-prim-group s5-0 (the-as uint 3) 0)))
(set! (-> s5-0 total-prims) (the-as uint 4))
(set! (-> s4-0 prim-core collide-as) (collide-spec civilian))
(set! (-> s4-0 prim-core action) (collide-action solid no-standon))
(set! (-> s4-0 transform-index) 0)
(set-vector! (-> s4-0 local-sphere) 0.0 -1024.0 0.0 7372.8)
(set! (-> s5-0 root-prim) s4-0)
)
(let ((v1-7 (new 'process 'collide-shape-prim-sphere s5-0 (the-as uint 0))))
(set! (-> v1-7 prim-core collide-as) (collide-spec civilian))
(set! (-> v1-7 prim-core action) (collide-action solid))
(set! (-> v1-7 transform-index) 0)
(set-vector! (-> v1-7 local-sphere) 0.0 -4096.0 0.0 4096.0)
)
(let ((v1-9 (new 'process 'collide-shape-prim-sphere s5-0 (the-as uint 0))))
(set! (-> v1-9 prim-core collide-as) (collide-spec civilian))
(set! (-> v1-9 prim-core action) (collide-action solid))
(set! (-> v1-9 transform-index) 0)
(set-vector! (-> v1-9 local-sphere) 0.0 -1024.0 0.0 4096.0)
)
(let ((v1-11 (new 'process 'collide-shape-prim-sphere s5-0 (the-as uint 0))))
(set! (-> v1-11 prim-core collide-as) (collide-spec civilian))
(set! (-> v1-11 prim-core action) (collide-action solid no-standon))
(set! (-> v1-11 transform-index) 0)
(set-vector! (-> v1-11 local-sphere) 0.0 2048.0 0.0 4096.0)
)
(set! (-> s5-0 nav-radius) (* 0.75 (-> s5-0 root-prim local-sphere w)))
(let ((v1-14 (-> s5-0 root-prim)))
(set! (-> s5-0 backup-collide-as) (-> v1-14 prim-core collide-as))
(set! (-> s5-0 backup-collide-with) (-> v1-14 prim-core collide-with))
)
(set! (-> obj root-override) s5-0)
)
0
(none)
)
(defmethod process-taskable-method-32 process-taskable ((obj process-taskable))
(logior! (-> obj skel status) (joint-control-status eye-anim))
(set! (-> obj talk-message) (text-id press-triangle-to-talk))
(set! (-> obj bounce-away) #t)
(set! (-> obj will-talk) #t)
(set! (-> obj look-at-me) #t)
(set! (-> obj hide-during-movie) #t)
(set! (-> obj neck-joint-index) -1)
(set! (-> obj talk-distance) (res-lump-float (-> obj entity) 'distance :default 32768.0))
(set! (-> obj talk-height) (res-lump-float (-> obj entity) 'height :default 8192.0))
(set! (-> obj slave) (the-as handle #f))
(set! (-> obj draw shadow-ctrl)
(new 'process 'shadow-control 0.0 0.0 614400.0 (shadow-flags shdf02 shdf03 shdf04 disable-draw) 245760.0)
)
(let ((s5-0 0))
(let ((v1-15 (the-as joint (get-art-by-name-method (-> obj draw jgeo) "main" (the-as type #f)))))
(if v1-15
(set! s5-0 (+ (-> v1-15 number) 1))
)
)
(let ((v1-19 (the-as collide-shape-prim-group (-> obj root-override root-prim))))
(set! (-> v1-19 transform-index) s5-0)
(dotimes (a0-7 (the-as int (-> v1-19 num-children)))
(set! (-> v1-19 child a0-7 transform-index) s5-0)
)
)
)
0
(none)
)
(defmethod init-art! process-taskable ((obj process-taskable))
"@see [[initialize-skeleton]]"
0
(none)
)
;; WARN: Return type mismatch object vs none.
(defmethod init-from-entity! process-taskable ((obj process-taskable) (arg0 entity-actor))
"Typically the method that does the initial setup on the process, potentially using the [[entity-actor]] provided as part of that.
This commonly includes things such as:
- stack size
- collision information
- loading the skeleton group / bones
- sounds"
(stack-size-set! (-> obj main-thread) 512)
(process-taskable-method-31 obj)
(process-drawable-from-entity! obj arg0)
(set! (-> obj task)
(new 'process 'game-task-control (res-lump-value arg0 'task-actor game-task-actor :time -1000000000.0))
)
(init-art! obj)
(process-taskable-method-32 obj)
(when (logtest? #x1000000 (res-lump-value arg0 'options uint128 :time -1000000000.0))
(let* ((s5-1 *level*)
(s4-2 (method-of-object s5-1 art-group-get-by-name))
)
(format (clear *temp-string*) "skel-~S" (-> obj draw art-group name))
(let ((s4-3 (s4-2 s5-1 *temp-string* (the-as (pointer uint32) #f))))
(when s4-3
(let ((s5-2 (process-spawn manipy :init manipy-init (-> obj root-override trans) (-> obj entity) s4-3 #f 0 :to obj))
)
(send-event (ppointer->process s5-2) 'anim-mode 'mirror)
(send-event (ppointer->process s5-2) 'mirror #t)
)
)
)
)
)
(set! (-> obj event-hook) (-> (method-of-object obj idle) event))
(go (method-of-object obj hide))
(none)
)