Files
jak-project/goal_src/jak1/engine/entity/entity.gc
T
2026-07-28 12:00:35 -07:00

1215 lines
62 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
(bundles "ENGINE.CGO" "GAME.CGO")
(require "engine/entity/actor-link-h.gc")
(require "engine/entity/ambient.gc")
(require "engine/level/level.gc")
(require "engine/draw/drawable-actor-h.gc")
(require "engine/common-obs/process-drawable.gc")
(require "engine/entity/entity-table.gc")
;; DECOMP BEGINS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global entity settings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define *spawn-actors* #t)
(define *compact-actors* #t)
(define *vis-actors* #t)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; entity basic methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod mem-usage ((this drawable-actor) (usage memory-usage-block) (flags mem-usage-flags))
"Account for this drawable wrapper and delegate to its entity actor in the entity category."
(mem-usage-add! usage entity 1 (asize-of this))
;; Attribute the actor's resource lump and referenced data to the entity category.
(mem-usage (-> this actor) usage (logior flags (mem-usage-flags resource-entity)))
(the-as drawable-actor 0))
(defmethod mem-usage ((this drawable-inline-array-actor) (usage memory-usage-block) (flags mem-usage-flags))
"Account for the drawable-group header and every active inline actor wrapper."
(mem-usage-add-symbol! usage drawable-group 1 32)
(dotimes (i (-> this length))
(mem-usage (-> this data i) usage flags))
(the-as drawable-inline-array-actor 0))
(defmethod print ((this entity-links))
(format #t "#<entity-links :process ~A @ #x~X>" (-> this process) this)
this)
(defmethod print ((this entity-perm))
(format #t
"#<entity-perm :aid ~D :task ~D :status #x~X :data #x~X @ #x~X>"
(-> this aid)
(-> this task)
(-> this status)
(-> this user-uint64)
this)
this)
(defmethod birth! ((this entity))
"Default entity birth hook; subclasses create their live representation."
(format #t "birth ~A~%" this)
this)
(defmethod kill! ((this entity))
"Default entity kill hook; subclasses remove their live representation."
(format #t "kill ~A~%" this)
this)
(defmethod print ((this entity))
"print an entity, with its name from the res."
(format #t "#<~A :name ~S @ #x~X>" (-> this type) (res-lump-struct this 'name structure) this)
this)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; entity finding
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod get-level ((this entity))
"Return the active level heap containing this entity, or level-default when none contains it."
(dotimes (i (-> *level* length))
(let ((lev (-> *level* level i)))
(when (= (-> lev status) 'active)
(if (and (>= (the-as int this) (the-as int (-> lev heap base))) (< (the-as int this) (the-as int (-> lev heap top-base))))
(return lev)))))
(-> *level* level-default))
(defun entity-by-name ((name string))
"Return the first actor, ambient, or camera with name across active levels, or false."
(local-vars (i int))
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((actors (-> lev bsp actors)))
(when (nonzero? actors)
(set! i 0)
(while (< i (-> actors length))
(let ((actor (-> actors data i actor))) (if (name= (res-lump-struct actor 'name basic) name) (return actor)))
(+! i 1))))
(let ((ambients (-> lev bsp ambients)))
(when (nonzero? ambients)
(set! i 0)
(while (< i (-> ambients length))
(let ((ambient (-> ambients data i ambient))) (if (name= (res-lump-struct ambient 'name basic) name) (return ambient)))
(+! i 1))))
(let ((cameras (-> lev bsp cameras)))
(when (nonzero? cameras)
(set! i 0)
(while (< i (-> cameras length))
(let ((camera (-> cameras i))) (if (name= (res-lump-struct camera 'name basic) name) (return camera)))
(+! i 1)))))))
(the-as entity #f))
(defun entity-by-type ((entity-type type))
"Return the first active-level entity actor whose declared process type is exactly entity-type,
or false."
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((actors (-> lev bsp actors)))
(when (nonzero? actors)
(dotimes (i (-> actors length))
(let ((actor (-> actors data i actor)))
(if (and (type-type? (-> actor type) entity-actor) (= (-> actor etype) entity-type)) (return actor)))))))))
(the-as entity-actor #f))
(defun entity-by-aid ((aid uint))
"Binary-search active levels' actor-ID-sorted entity-link arrays and return the matching entity,
or false."
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev entity)))
(when (nonzero? links)
(let ((lo 0)
(hi (+ (-> links length) -1)))
0
(while (>= hi lo)
(let* ((mid (+ lo (/ (- hi lo) 2)))
(link (-> links data mid))
(mid-aid (-> link perm aid)))
(cond
((= mid-aid aid) (return (-> link entity)))
((< (the-as uint mid-aid) aid) (set! lo (+ mid 1)))
(else (set! hi (+ mid -1))))))))))))
(the-as entity #f))
(defun entity-by-meters ((x float) (y float) (z float))
"Return the first active-level actor whose fixed-point translation equals x, y, and z in meters,
or false."
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((actors (-> lev bsp actors)))
(when (nonzero? actors)
(dotimes (i (-> actors length))
(let* ((actor (-> actors data i actor))
(translation (-> actor extra trans)))
(if (and (= (the float (the int (-> translation x))) x)
(= (the float (the int (-> translation y))) y)
(= (the float (the int (-> translation z))) z))
(return actor)))))))))
(the-as entity-actor #f))
(defun process-by-ename ((name string))
"Return the live process connected to the named entity, or false."
(let ((ent (entity-by-name name))) (if ent (-> ent extra process))))
(defun entity-process-count ((mode symbol))
"Count active-level entity links with live processes, or visible links when mode is vis."
(let ((count 0))
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
(let ((ent (-> links data i entity)))
(case mode
(('vis) (if (is-object-visible? lev (-> ent extra vis-id)) (+! count 1)))
(else (if (-> ent extra process) (+! count 1))))))))))
count))
(defun entity-count ()
"Return the total number of entity links across active levels."
(let ((count 0))
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
(-> links data i entity) ;; value is unused.
(+! count 1))))))
count))
(defun entity-remap-names ((remaps pair))
"For each position/name record in remaps, locate the actor at the eighth-unit coordinates and
replace its name resource tag."
(let ((remap (car remaps)))
(while (not (null? remaps))
;; look up by the given position.
(let ((ent (entity-by-meters (the float (/ (the-as int (car (cdr remap))) 8))
(the float (/ (the-as int (car (cdr (cdr remap)))) 8))
(the float (/ (the-as int (car (cdr (cdr (cdr remap))))) 8)))))
(if ent
;; if we found an entity, modify its res.
(add-data! ent
(new 'static 'res-tag :name 'name :key-frame -1000000000.0 :elt-count #x1 :elt-type string)
(the-as pointer (car remap)))))
(set! remaps (cdr remaps))
(set! remap (car remaps))))
0
(none))
(defun-debug process-status-bits ((proc process) (stream symbol))
"Print three compact process-status characters to stream: logic-running, drawing, and current
draw LOD. Unsupported or inactive fields print spaces."
(let* ((status-proc proc)
(drawable-proc (the-as process-drawable
(if (and (nonzero? status-proc) (type-type? (-> status-proc type) process-drawable)) (the-as process-drawable status-proc)))))
(if (and (the-as process drawable-proc) (zero? (-> drawable-proc draw))) (set! drawable-proc (the-as process-drawable #f)))
;; first char is r or ' '. r for run.
;; second char is d or ' '. I think d is draw.
;; third char is a number 0-4 or a ' '. This is the lod.
(format stream
"~C~C~C"
(if (and proc (zero? (logand (-> *kernel-context* prevent-from-run) (-> proc mask))) (run-logic? proc))
#\r
#\\s ;; space
)
(if (and drawable-proc (logtest? (-> drawable-proc draw status) (draw-status was-drawn))) #\d #\\s)
(cond
((and drawable-proc (logtest? (-> drawable-proc draw status) (draw-status was-drawn)))
(case (-> drawable-proc draw cur-lod)
((0) #\0)
((1) #\1)
((2) #\2)
((3) #\3)
((4) #\4)))
(else #\\s))))
0
(none))
(defmethod print ((this process))
"Fancier print for process that can also print status of process drawables."
(format #t
"#<~A ~S ~A :state ~S :flags "
(-> this type)
(-> this name)
(-> this status)
(if (-> this state) (-> this state name)))
(process-status-bits this #t)
(format #t
" :stack ~D/~D :heap ~D/~D @ #x~X>"
(&- (-> this top-thread stack-top) (the-as uint (-> this top-thread sp)))
(-> this main-thread stack-size)
(- (-> this allocated-length) (&- (-> this heap-top) (the-as uint (-> this heap-cur))))
(-> this allocated-length)
this)
this)
(defmethod debug-print ((this entity-actor) (mode symbol) (expected-type type))
"Print one row of the entity debug table when this actor matches expected-type. mode controls
meter positions and the optional permanent-state detail row."
(let ((entity-type (-> this etype)))
(when (or (not expected-type) (and entity-type (valid? entity-type type #f #f 0) (type-type? entity-type expected-type)))
(format #t "~5D #x~8X ~-21S" (-> this extra vis-id) this (res-lump-struct this 'name structure))
(let ((level-name (-> this extra level nickname)))
(set! level-name
(cond
(level-name level-name)
(else (-> this extra level name))))
(format #t
"~8D ~3D ~-4S #x~4X"
(-> this extra perm aid)
(-> this extra perm task)
level-name
(-> this extra perm status)))
;; location
(if (= mode 'entity-meters)
(format #t " :trans ~14m ~14m ~14m " (-> this extra trans x) (-> this extra trans y) (-> this extra trans z))
(format #t " :trans ~14f ~14f ~14f " (-> this extra trans x) (-> this extra trans y) (-> this extra trans z)))
;; if we have an associated process, print info.
(let* ((proc (-> this extra process))
(drawable-proc (if (and (nonzero? proc) (type-type? (-> proc type) process-drawable)) proc)))
(format #t
":pr #x~8X ~-12S ~-21S ~-5S/~-5S "
(if (-> this extra process) (-> this extra process) 0)
(if (-> this extra process) (-> this extra process name) "")
(if (and (-> this extra process) (-> this extra process state)) (-> this extra process state name) "")
(if (-> this extra process)
(* (- (-> this extra process allocated-length)
(&- (-> this extra process heap-top) (the-as uint (-> this extra process heap-cur))))
8)
"")
(if (-> this extra process) (* (-> this extra process allocated-length) 8) ""))
(process-status-bits drawable-proc #t))
(format #t "~%")
(if (= mode 'entity-perm) (format #t " ~`entity-perm`P~%" (-> this extra perm)))))
(none))
(defmethod debug-print-entities ((this level-group) (mode symbol) (expected-type type))
"Print a table of active-level entities, optionally restricted to expected-type. mode selects
the ordinary table, meter-formatted positions, permanent-state details, or art-group names."
;; This table is wider than the debug screen.
(format #t
" id address name aid tsk lev status x y z address name state heap flags~%"
0
0
0)
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(case mode
(('art-group)
(format #t "level ~A~%" (-> lev name))
(dotimes (i (-> lev art-group art-group-array length))
(format #t "~T~2D ~S~%" i (-> lev art-group art-group-array i name))))
(else
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
(debug-print (the-as entity-actor (-> links data i entity)) mode expected-type))))))))
0
(none))
;;;;;;;;;;;;;;;;;;
;; entity setup
;;;;;;;;;;;;;;;;;;
(defmethod add-to-level! ((this entity) (lev-group level-group) (lev level) (aid actor-id))
"Append this entity to level's entity-link array, splice the link into the level group's
circular list, copy its transform, and initialize actor ID, task, level, and visibility data."
;; grab the first free link
(let ((level-link (-> lev entity data (-> lev entity length))))
(+! (-> lev entity length) 1)
;; attach the entity to the link
(set! (-> level-link process) #f)
(set! (-> level-link entity) this)
(set! (-> this extra) level-link)
(cond
((-> lev-group entity-link)
;; add to linked list of existing
(let* ((other-prev (-> lev-group entity-link))
(other-front (-> other-prev next-link)))
(set! (-> other-prev next-link) level-link)
(set! (-> level-link prev-link) other-prev)
(set! (-> level-link next-link) other-front)
(set! (-> other-front prev-link) level-link)))
(else
;; we're the first in the level.
(set! (-> level-link prev-link) level-link)
(set! (-> level-link next-link) level-link)))
;; remember the start of the list
(set! (-> lev-group entity-link) level-link)
;; update the trans.
(set! (-> level-link trans quad) (-> this trans quad)))
;; set us up
(set! (-> this extra perm aid) aid)
(set! (-> this extra level) lev)
(cond
((= (-> this type) entity-actor)
(set! (-> (the-as entity-actor this) extra perm task) (-> (the-as entity-actor this) task))
(set! (-> (the-as entity-actor this) extra vis-id) (-> (the-as entity-actor this) vis-id-signed)))
(else (set! (-> this extra perm task) (game-task none)) (set! (-> this extra vis-id) 0) 0))
(none))
(defmethod remove-from-level! ((this entity) (group level-group))
"Unlink this entity from the level group's circular entity list and return the entity."
(let ((link (-> this extra)))
(cond
((= (-> link next-link) link) (set! (-> group entity-link) #f))
(else
(set! (-> link next-link prev-link) (-> link prev-link))
(set! (-> link prev-link next-link) (-> link next-link))
(if (= (-> group entity-link) link) (set! (-> group entity-link) (-> link prev-link))))))
this)
;;;;;;;;;;;;;;;;;;;;;;;;;
;; visibility update
;;;;;;;;;;;;;;;;;;;;;;;;;
;; the visibility system is pretty simple and there is a single axis-aligned bounding box.
;; these methods are debug tools for updating these.
(defun update-actor-vis-box ((proc process-drawable) (min-pt vector) (max-pt vector))
"Expand min-point and max-point around proc's world-space draw-bounds sphere when it has draw
control."
(when (and proc (nonzero? (-> proc draw)))
;; add the draw origin offset.
(let ((world-bounds-origin (vector+! (new 'stack-no-clear 'vector) (-> proc draw origin) (-> proc draw bounds)))
(radius (-> proc draw bounds w)))
(set! (-> min-pt x) (fmin (-> min-pt x) (- (-> world-bounds-origin x) radius)))
(set! (-> min-pt y) (fmin (-> min-pt y) (- (-> world-bounds-origin y) radius)))
(set! (-> min-pt z) (fmin (-> min-pt z) (- (-> world-bounds-origin z) radius)))
(set! (-> max-pt x) (fmax (-> max-pt x) (+ (-> world-bounds-origin x) radius)))
(set! (-> max-pt y) (fmax (-> max-pt y) (+ (-> world-bounds-origin y) radius)))
(set! (-> max-pt z) (fmax (-> max-pt z) (+ (-> world-bounds-origin z) radius)))))
0
(none))
(defmethod update-vis-volumes ((this level-group))
"Expand each active entity's editable visibility box around the draw bounds of its live
process and drawable child processes. This development-only function contains a compiler
spill anomaly immediately before assigning the intended process."
(local-vars (spill-result symbol) (drawable-proc process))
(format 0 "call to update-vis-volumes, which may have a compiler bug.~%")
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
(let* ((ent (-> links data i entity))
(vis-volume (res-lump-data ent 'visvol (inline-array vector)))
(min-point (-> vis-volume 0))
(max-point (-> vis-volume 1)))
(let ((proc (-> ent extra process)))
;; I am pretty sure there is a GOAL compiler bug here.
;; the output makes zero sense, but I don't think it matters:
;; this function doesn't seem like it should ever be run outside of development
;; and the compiler bug has no effect?
(set! spill-result
(when (and (nonzero? proc) (type-type? (-> proc type) process-drawable))
;; i think it spills the wrong variable here
(set! drawable-proc (the-as process spill-result))
;; then immediate spills the right one.
(set! drawable-proc proc)
spill-result)))
(when drawable-proc
(update-actor-vis-box (the-as process-drawable drawable-proc) min-point max-point)
(let ((child-link (-> drawable-proc child)))
(while child-link
(let ((update-box update-actor-vis-box)
(child-proc (-> child-link 0)))
(update-box (the-as process-drawable (if (and (nonzero? child-proc) (type-type? (-> child-proc type) process-drawable)) child-proc))
min-point
max-point))
(set! child-link (-> child-link 0 brother)))))))))))
0
(none))
(defmethod update-vis-volumes-from-nav-mesh ((this level-group))
"Rebuild each active actor's editable visibility box from its navigation mesh, using a linked
nav-mesh actor when supplied and a six-meter box around the entity when no mesh is available."
;; loop over levels
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active) ;; only active levels
;; loop over entities
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
;; look up the bounding box.
(let* ((ent (-> links data i entity))
(vis-volume (res-lump-data ent 'visvol (inline-array vector)))
(min-point (-> vis-volume 0))
(max-point (-> vis-volume 1)))
(let ((translation (-> ent extra trans))
(nav-ent ent))
;; sometimes the nav-mesh may be in a different actor, I guess.
;; so try to look that up.
(let ((linked-nav-ent (entity-actor-lookup ent 'nav-mesh-actor 0)))
(when linked-nav-ent
(set! nav-ent linked-nav-ent)
(the-as entity-actor (the-as entity-actor nav-ent))))
(cond
((and (type-type? (-> nav-ent type) entity-actor) (nonzero? (-> (the-as entity-actor nav-ent) nav-mesh)))
;; we got a nav-mesh! compute the bounding box
(compute-bounding-box (-> (the-as entity-actor nav-ent) nav-mesh) min-point max-point))
(else
;; no nav-mesh found, just use the default position
(set! (-> min-point quad) (-> translation quad))
(set! (-> max-point quad) (-> translation quad)))))
;; add some padding to make a 6x6 meter box.
(let ((min-padding -12288.0)
(max-padding 12288.0))
(+! (-> min-point x) min-padding)
(+! (-> min-point y) min-padding)
(+! (-> min-point z) min-padding)
(+! (-> max-point x) max-padding)
(+! (-> max-point y) max-padding)
(+! (-> max-point z) max-padding))))))))
0
(none))
(define-extern money type)
(define-extern crate type)
(define-extern springbox type)
(define-extern fuel-cell type)
(defmethod print-volume-sizes ((this level-group))
"Print each ordinary active actor's visibility distance and box extents relative to its
origin, excluding money, crates, fuel cells, and springboxes."
(local-vars (entity-type type))
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
;; lookup volume and dist.
(let* ((ent (-> links data i entity))
(vis-volume (the-as object (res-lump-data ent 'visvol pointer)))
(vis-distance (res-lump-float ent 'vis-dist :default 409600.0))
(translation (-> ent extra trans)))
(set! entity-type
(cond
((type-type? (-> ent type) entity-actor) (set! entity-type (-> (the-as entity-actor ent) etype)) entity-type)
(else (the-as type #f))))
(let ((min-point (-> (the-as (inline-array vector) vis-volume) 0))
(max-point (-> (the-as (inline-array vector) vis-volume) 1)))
(when (not (or (name= entity-type money)
(or (name= entity-type crate) (name= entity-type fuel-cell) (name= entity-type springbox))))
(format #t "actor-vis ~S ~6,,1M " (res-lump-struct ent 'name basic) vis-distance)
(format #t
"~6,,1M ~6,,1M ~6,,1M ~6,,1M ~6,,1M ~6,,1M~%"
(- (-> min-point x) (-> translation x))
(- (-> min-point y) (-> translation y))
(- (-> min-point z) (-> translation z))
(- (-> max-point x) (-> translation x))
(- (-> max-point y) (-> translation y))
(- (-> max-point z) (-> translation z)))))))))))
0
(none))
(defun expand-vis-box-with-point ((ent entity) (point vector))
"Expand entity's editable visibility box to contain point when the entity has visvol data."
(let ((vis-volume (res-lump-data ent 'visvol (inline-array vector))))
(when vis-volume
(let ((min-point (-> vis-volume 0))
(max-point (-> vis-volume 1)))
(set! (-> min-point x) (fmin (-> min-point x) (-> point x)))
(set! (-> min-point y) (fmin (-> min-point y) (-> point y)))
(set! (-> min-point z) (fmin (-> min-point z) (-> point z)))
(set! (-> max-point x) (fmax (-> max-point x) (-> point x)))
(set! (-> max-point y) (fmax (-> max-point y) (-> point y)))
(set! (-> max-point z) (fmax (-> max-point z) (-> point z))))))
0
(none))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The Debug Draw Method
;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod debug-draw-actors ((this level-group) (mode symbol))
"Draw the enabled entity, process, visibility-volume, animation, navigation, path, and volume
diagnostics for active levels. mode selects process-only markers, all entities, or the
ordinary live-entity display."
(when (and mode (not (or (= *master-mode* 'menu) (= *master-mode* 'progress))))
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
(let* ((ent (-> links data i entity))
(translation (-> ent extra trans)))
(cond
((and (= mode 'process) (-> ent extra process) (type-type? (-> ent extra process type) process-drawable))
(let ((drawable-proc (the-as process-drawable (-> ent extra process))))
(add-debug-x #t
(bucket-id debug-no-zbuf)
(-> drawable-proc root trans)
(new 'static 'rgba :r #x80 :g #xff :b #x80 :a #x80))
(add-debug-text-3d #t
(bucket-id debug-no-zbuf)
(res-lump-struct ent 'name string)
(-> drawable-proc root trans)
(font-color white)
(new 'static 'vector2h :y 8))
(add-debug-text-3d #t
(bucket-id debug-no-zbuf)
(symbol->string (-> drawable-proc state name))
(-> drawable-proc root trans)
(font-color white)
(new 'static 'vector2h :y 16))
(let ((eco-info (res-lump-data (-> drawable-proc entity) 'eco-info (pointer int32) :time 0.0)))
(when eco-info
(let ((draw-text add-debug-text-3d)
(enabled #t)
(debug-bucket 68))
(format (clear *temp-string*) "~S ~D~%" (pickup-type->string (the-as pickup-type (-> eco-info 0))) (-> eco-info 1))
(draw-text enabled
(the-as bucket-id debug-bucket)
*temp-string*
(-> drawable-proc root trans)
(font-color white)
(new 'static 'vector2h :y 24)))))
(let ((art-name (res-lump-struct (-> drawable-proc entity) 'art-name symbol)))
(if (and (the-as structure art-name) (= (-> art-name type) symbol))
(add-debug-text-3d #t
(bucket-id debug-no-zbuf)
(symbol->string art-name)
(-> drawable-proc root trans)
(font-color white)
(new 'static 'vector2h :y 24))))))
((or (= mode 'full) (-> ent extra process))
(add-debug-x #t
(bucket-id debug-no-zbuf)
translation
(if (-> ent extra process) (new 'static 'rgba :r #x80 :g #xff :b #x80 :a #x80) (new 'static 'rgba :r #xff :a #x80)))
(let ((draw-text add-debug-text-3d)
(enabled #t)
(debug-bucket 68))
(draw-text enabled
(the-as bucket-id debug-bucket)
(res-lump-struct ent 'name string)
translation
(if (logtest? (-> ent extra perm status) (entity-perm-status birth-blocked error)) (font-color white) (font-color yellow))
(new 'static 'vector2h :y 8))))))))))))
(when (and *display-actor-vis* (not (or *display-actor-anim* *display-process-anim*)))
(let ((vis-mode *display-actor-vis*))
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(dotimes (i (-> links length))
(let ((ent (-> links data i entity)))
(let ((vis-volume (res-lump-data ent 'visvol pointer))
(vis-id (-> ent extra vis-id)))
(when (and vis-volume (or (= vis-mode #t) (= vis-mode 'box)))
(let ((draw-box add-debug-box)
(enabled #t)
(debug-bucket 68)
(min-point (&+ vis-volume 0))
(max-point (&+ vis-volume 16)))
(draw-box enabled
(the-as bucket-id debug-bucket)
(the-as vector min-point)
(the-as vector max-point)
(if (is-object-visible? lev vis-id) (new 'static 'rgba :g #x80 :b #x80 :a #x80) (new 'static 'rgba :r #x80 :b #x80 :a #x80))))))
(when (or (= vis-mode #t) (= vis-mode 'sphere))
(let ((proc (-> ent extra process)))
(when proc
(when (and (type-type? (-> proc type) process-drawable) (nonzero? (-> (the-as process-drawable proc) draw)))
(add-debug-x #t
(bucket-id debug-no-zbuf)
(-> (the-as process-drawable proc) root trans)
(new 'static 'rgba :r #xff :g #xff :b #xff :a #x80))
(add-debug-sphere #t
(bucket-id debug)
(vector+! (new 'stack-no-clear 'vector)
(-> (the-as process-drawable proc) draw origin)
(-> (the-as process-drawable proc) draw bounds))
(-> (the-as process-drawable proc) draw bounds w)
(new 'static 'rgba :r #x80 :a #x80))))))))))))))
(if *generate-actor-vis* (update-vis-volumes this))
(when (or *display-actor-anim* *display-process-anim*)
(let ((selected-proc (ppointer->process *display-process-anim*)))
(if (not selected-proc) (set! selected-proc (process-by-name *display-actor-anim* *active-pool*)))
(when (and selected-proc (type-type? (-> selected-proc type) process-drawable))
(let ((selected-ent (-> (the-as process-drawable selected-proc) entity))
(translation (-> (the-as process-drawable selected-proc) root trans)))
(when selected-ent
(add-debug-x #t
(bucket-id debug-no-zbuf)
translation
(if (-> selected-ent extra process) (new 'static 'rgba :r #x80 :g #xff :b #x80 :a #x80) (new 'static 'rgba :r #xff :a #x80)))
(add-debug-text-3d #t
(bucket-id debug-no-zbuf)
(res-lump-struct selected-ent 'name string)
translation
(if (logtest? (-> selected-ent extra perm status) (entity-perm-status birth-blocked error))
(font-color white)
(font-color white))
(new 'static 'vector2h :y 8))
(add-debug-text-3d #t
(bucket-id debug-no-zbuf)
(symbol->string (-> (the-as process-drawable selected-proc) state name))
translation
(font-color white)
(new 'static 'vector2h :y 16))))
(if (nonzero? (-> (the-as process-drawable selected-proc) skel))
(debug-print-channels (-> (the-as process-drawable selected-proc) skel) (the-as symbol *stdcon*)))
(if (nonzero? (-> (the-as process-drawable selected-proc) nav))
(debug-draw (-> (the-as process-drawable selected-proc) nav)))
(if (nonzero? (-> (the-as process-drawable selected-proc) path))
(debug-draw (-> (the-as process-drawable selected-proc) path)))
(if (nonzero? (-> (the-as process-drawable selected-proc) vol)) (init! (-> (the-as process-drawable selected-proc) vol))))
(if (and (the-as process-drawable selected-proc)
(type-type? (-> (the-as process-drawable selected-proc) type) process-drawable)
(nonzero? (-> (the-as process-drawable selected-proc) draw))
*display-actor-vis*)
(add-debug-sphere #t
(bucket-id debug)
(vector+! (new 'stack-no-clear 'vector)
(-> (the-as process-drawable selected-proc) draw origin)
(-> (the-as process-drawable selected-proc) draw bounds))
(-> (the-as process-drawable selected-proc) draw bounds w)
(new 'static 'rgba :r #x80 :a #x80))))
(when (and *display-actor-vis* *display-actor-anim*)
(let ((selected-ent (entity-by-name *display-actor-anim*)))
(when selected-ent
(let ((vis-volume (res-lump-data selected-ent 'visvol pointer))
(vis-id (-> selected-ent extra vis-id)))
(if vis-volume
(add-debug-box #t
(bucket-id debug-no-zbuf)
(the-as vector (&+ vis-volume 0))
(the-as vector (&+ vis-volume 16))
(if (is-object-visible? (-> selected-ent extra level) vis-id)
(new 'static 'rgba :g #x80 :b #x80 :a #x80)
(new 'static 'rgba :r #x80 :b #x80 :a #x80)))))))))
(if (and (or *display-nav-marks* *display-path-marks* *display-vol-marks*)
(not (or *display-actor-anim* *display-process-anim*)))
(iterate-process-tree *active-pool*
(lambda ((proc process-drawable))
(when (type-type? (-> proc type) process-drawable)
(if (nonzero? (-> proc nav)) (debug-draw (-> proc nav)))
(if (nonzero? (-> proc path)) (debug-draw (-> proc path)))
(if (nonzero? (-> proc vol)) (init! (-> proc vol))))
(none))
*null-kernel-context*))
#|
This is where the "actor graph" is drawn, but the plot functions don't do anything.
(when (and *display-actor-graph* (not (or (= *master-mode* 'menu) (= *master-mode* 'progress))))
(if (not (paused?))
(float-save-timeplot (if (< (the int (the float (mod (-> *display* base-frame-counter) 600))) 300)
1.0
0.0
)
)
)
(camera-plot-float-func 0.0 399.0 -81920.0 81920.0 float-lookup-redline (new 'static 'vector4w :x #xff :w #x80))
(camera-plot-float-func 0.0 399.0 -81920.0 81920.0 float-lookup-blueline (new 'static 'vector4w :z #xff :w #x80))
(camera-plot-float-func 0.0 399.0 -81920.0 81920.0 float-lookup-greenline (new 'static 'vector4w :y #xff :w #x80))
(camera-plot-float-func 0.0 399.0 0.0 409600.0 float-lookup-yellowline (new 'static 'vector4w :x #xff :y #xff :w #x80))
(camera-plot-float-func 0.0 399.0 0.0 1.0 float-lookup-timeplot (new 'static 'vector4w :x #x80 :y #x80 :z #x80 :w #x80))
)
|#
(when *display-split-boxes*
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(when (nonzero? (-> lev bsp boxes))
(let ((boxes (-> lev bsp boxes)))
(countdown (i (-> boxes length))
(add-debug-box #t
(bucket-id debug)
(-> boxes data i min)
(the-as vector (+ (the-as uint (-> boxes data 0 max)) (* i 32)))
(if (zero? (-> lev index)) (new 'static 'rgba :g #x80 :b #x80 :a #x80) (new 'static 'rgba :r #xff :g #x80 :b #x80 :a #x80))))))))))
(when (or *display-ambient-hint-marks*
*display-ambient-sound-marks*
*display-ambient-poi-marks*
*display-ambient-light-marks*
*display-ambient-dark-marks*
*display-ambient-weather-off-marks*
*display-ambient-ocean-off-marks*
*display-ambient-ocean-near-off-marks*
*display-ambient-music-marks*)
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(let ((ambients (-> lev bsp ambients)))
(when (nonzero? ambients)
(dotimes (i (-> ambients length))
(draw-debug (-> ambients data i ambient)))))))))
0
(none))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Camera Birthing
;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod birth! ((this entity-camera))
"Connect this camera entity to the camera engine."
(add-connection *camera-engine* *camera* nothing this #f #f)
this)
(defmethod kill! ((this entity-camera))
"Remove this camera entity's connection from the camera engine."
(remove-by-param1 *camera-engine* this)
this)
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Actor Birthing
;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro birth-log (str &rest args)
"Debug print to stdout of runtime for debugging actor inits."
`(format 0 ,(string-append "[BIRTH] " str) ,@args)
;;`(empty)
)
(defun init-entity ((proc process) (ent entity-actor) (entity-type type))
"Activate proc in the entity pool, connect it to actor, and run its init-from-entity! method
immediately before the process enters ordinary scheduling. Callers pass proc's installed type as
entity-type, although this implementation does not read it."
;;(birth-log "(init-entity ~A)~%" ent)
;; activate the process. It goes in the entity-pool, which is a child of the main active-pool.
(activate proc *entity-pool* (res-lump-struct ent 'name basic) (the-as pointer #x70004000))
;; link the entity and the process
(set! (-> proc entity) ent)
(set! (-> ent extra process) proc)
;;(birth-log "activated: ~A ~A, now doing init ~A~%" proc ent (method-of-object proc init-from-entity!))
;; run the initializer
(run-now-in-process proc (method-of-object proc init-from-entity!) proc ent)
(none))
;; Fallback for displaying art whose declared process type is unavailable.
(define-extern birth-viewer
"Use the viewer fallback for an entity whose declared process type is unavailable. Retype proc as
viewer, initialize it through the ordinary entity path, and return true."
(function process entity-actor symbol))
(defmacro this-etype? (&rest types)
`(or ,@(apply (lambda (x) `(begin (define-extern ,x type) (type-type? (-> this etype) ,x))) types)))
(defmethod birth! ((this entity-actor))
"Obtain a process of this actor's declared type and initialize it from the entity. Mark birth
blocked when neither the normal initializer nor the viewer fallback accepts the process."
;; temp
;; (when (or (not (this-etype? process))
;; ;disallowed types
;; ;(this-etype?)
;; (zero? (-> this etype)))
;; (when (nonzero? (-> this etype))
;; (birth-log "rejecting etype ~A birth~%" (-> this etype))
;; )
;; (logior! (-> this extra perm status) (entity-perm-status birth-blocked))
;; (return this)
;; )
;;(birth-log "call to birth! on ~A~%" this)
(let* ((entity-type (-> this etype))
(info (entity-info-lookup entity-type))
(entity-process (get-process *default-dead-pool* entity-type (if info (-> info heap-size) #x4000))))
(cond
((not entity-process) (birth-log "could not birth because there is no process.~%"))
((begin
(set! (-> entity-process type) entity-type)
(and entity-type
(valid? entity-type type #f #f 0)
(valid? (method-of-object entity-process init-from-entity!) function #f #f 0)))
(init-entity entity-process this entity-type))
(else
(when (not (birth-viewer entity-process this))
(format 0 "ERROR: no proper process type named ~A exists in the code, could not start ~A~%" entity-type this)
(logior! (-> this extra perm status) (entity-perm-status birth-blocked))))))
this)
(defun entity-deactivate-handler ((proc process) (actor entity-actor))
"When proc is still actor's live process, clear its error and no-kill status and disconnect it
from the entity."
(when (= proc (-> actor extra process))
(logclear! (-> actor extra perm status) (entity-perm-status error no-kill))
(set! (-> actor extra process) #f))
(none))
(defmethod kill! ((this entity-actor))
"Deactivate this actor's live process, or clear its stale entity link when no process exists."
(let ((proc (-> this extra process))) (if proc (deactivate proc) (entity-deactivate-handler proc this)))
this)
(defmethod birth ((this bsp-header))
"Allocate or validate the level's entity and ambient tables, add actors in birth-order,
initialize ambient payloads, and connect every camera. Actor process creation is deferred
across later frames."
(local-vars (birth-start-cycles int) (birth-end-cycles int))
(#unless PC_PORT
(m birth-start-cycles Count))
;; how many actors do we need?
(let ((actor-count (if (nonzero? (-> this actors)) (-> this actors length) 0)))
(cond
((not (-> this level entity))
;; we don't have an array of entity-links. allocate one.
(set! (-> this level entity) (new 'loading-level 'entity-links-array actor-count)))
((< (-> this level entity allocated-length) actor-count)
;; we do, but it's not big enough. Complain.
(format 0
"ERROR: Attempting to rebirth level ~A with incorrect entity table size ~D/~D~%"
(-> this level)
actor-count
(-> this level entity allocated-length)))))
;; reset our entity links array to 0.
(set! (-> this level entity length) 0)
;; NOTE: we don't actually birth the actors. It is too slow.
;; so it gets spread over multiple frames later.
(when (nonzero? (-> this actors))
(dotimes (birth-idx (-> this actors length))
(let* ((idx-to-birth (-> this actor-birth-order birth-idx))
(actor-to-birth (-> this actors data (logand idx-to-birth #xffff) actor)))
(add-to-level! actor-to-birth *level* (-> this level) (the-as actor-id (-> actor-to-birth aid))))))
(let ((existing-amb-count (if (nonzero? (-> this ambients)) (-> this ambients length) 0)))
(cond
((not (-> this level ambient))
(set! (-> this level ambient) (new 'loading-level 'entity-ambient-data-array existing-amb-count)))
((< (-> this level ambient allocated-length) existing-amb-count)
(format 0
"ERROR: Attempting to rebirth level ~A with incorrect ambient table size ~D/~D~%"
(-> this level)
existing-amb-count
(-> this level ambient allocated-length)))))
(set! (-> this level ambient length) 0)
0
(let ((amb-array (-> this level ambient))
(bsp-ambs (-> this ambients)))
(when (nonzero? bsp-ambs)
(dotimes (i (-> bsp-ambs length))
(let ((amb-to-birth (-> bsp-ambs data i ambient)))
(set! (-> amb-to-birth ambient-data) (-> amb-array data (-> amb-array length)))
(birth-ambient! amb-to-birth))
(+! (-> amb-array length) 1))))
(let ((cams (-> this cameras))) (when (nonzero? cams) (dotimes (i (-> cams length)) (birth! (-> cams i)))))
(#unless PC_PORT
(m birth-end-cycles Count)
(format 0 "Done ~S in ~D~%" "birth" (- birth-end-cycles birth-start-cycles)))
(none))
(defmethod deactivate-entities ((this bsp-header))
"Kill and unlink this BSP's actors and cameras, then deactivate any remaining entity,
particle, or drawable process that still references the level heap being released."
(let ((actors (-> this actors)))
(when (nonzero? actors)
(dotimes (i (-> actors length))
(let ((actor (-> actors data i actor))) (kill! actor) (remove-from-level! actor *level*)))))
(let ((cameras (-> this cameras))) (when (nonzero? cameras) (dotimes (i (-> cameras length)) (kill! (-> cameras i)))))
(let ((child-link (-> *entity-pool* child))
(heap-base (-> this level heap base))
(heap-end (-> this level heap top-base)))
(while child-link
(let ((proc (ppointer->process child-link)))
(set! child-link (-> child-link 0 brother))
(cond
((-> (the-as process proc) entity)
(when (= (-> (the-as process proc) entity extra level) (-> this level))
(format #t "NOTICE: rogue level entity ~A~% still alive~%" proc)
(deactivate proc)))
((= (-> proc type) part-tracker)
(let ((tracker (the-as part-tracker proc)))
(if (and (nonzero? (-> tracker part))
(>= (the-as int (-> tracker part group)) (the-as int heap-base))
(< (the-as int (-> tracker part group)) (the-as int heap-end)))
(deactivate proc))))
(else
(let* ((candidate proc)
(drawable-proc (if (and (nonzero? candidate) (type-type? (-> candidate type) process-drawable)) candidate)))
(when drawable-proc
(cond
((and (nonzero? (-> (the-as process-drawable drawable-proc) part))
(>= (the-as int (-> (the-as process-drawable drawable-proc) part group)) (the-as int heap-base))
(< (the-as int (-> (the-as process-drawable drawable-proc) part group)) (the-as int heap-end)))
(format #t
"NOTICE: rogue null level entity (using part ~A) ~A~% still alive~%"
(-> (the-as process-drawable (-> (the-as process-drawable drawable-proc) part)) brother)
proc)
(deactivate proc))
((and (nonzero? (-> (the-as process-drawable drawable-proc) draw))
(>= (the-as int (-> (the-as process-drawable drawable-proc) draw art-group)) (the-as int heap-base))
(< (the-as int (-> (the-as process-drawable drawable-proc) draw art-group)) (the-as int heap-end)))
(format #t
"NOTICE: rogue null level entity (using art ~A) ~A~% still alive~%"
(-> (the-as process-drawable (-> (the-as process-drawable drawable-proc) draw)) mask)
proc)
(deactivate proc))))))))))
(none))
(defun process-drawable-from-entity! ((proc process-drawable) (actor entity-actor))
"Mark proc as actor-pausable and initialize its root translation, rotation, and unit scale from
actor."
(logior! (-> proc mask) (process-mask actor-pause))
(vector-copy! (-> proc root trans) (-> actor extra trans))
(quaternion-copy! (-> proc root quat) (-> actor quat))
(vector-identity! (-> proc root scale))
(none))
(defmethod update-perm! ((this entity-perm) (mode symbol) (clear-mask entity-perm-status))
"Clear permanent status for reset mode. game clears clear-mask. Other task records always
clear birth-blocked, error, and suppress-birth; non-task records clear clear-mask. A record
with respawn-on-reload also clears dead, no-kill, and suppress-birth. Clear user data unless
it was explicitly supplied by a cstage."
(cond
((= mode 'game) (logclear! (-> this status) clear-mask))
((nonzero? (-> this task))
(logclear! (-> this status) (logior (if (logtest? (-> this status) (entity-perm-status respawn-on-reload)) 524 0) 515)))
(else
(logclear! (-> this status)
(logior clear-mask (if (logtest? (-> this status) (entity-perm-status respawn-on-reload)) 524 0)))))
(when (not (logtest? (-> this status) (entity-perm-status user-set-from-cstage)))
(set! (-> this user-uint64) (the-as uint 0))
0)
this)
(defun reset-actors ((mode symbol))
"Kill active actors, clear reset-specific permanent status and user data in level and game-state
permission tables, deactivate remaining entity-pool processes, and restore the actor birth
budget. game also resets task control."
(local-vars (i int))
(set! *display-process-anim* (the-as (pointer process) #f))
(let* ((reset-mode mode)
(clear-mask (cond
((or (= reset-mode 'life) (= reset-mode 'debug)) 623)
((= reset-mode 'try) 623)
((= reset-mode 'game) 1919)
(else 1663)))
(game-state *game-info*))
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((links (-> lev bsp level entity)))
(set! i 0)
(while (< i (-> links length))
(let ((ent (-> links data i entity)))
(kill! ent)
(update-perm! (-> ent extra perm) mode (the-as entity-perm-status clear-mask)))
(+! i 1))))))
(let ((task-perms (-> game-state task-perm-list)))
(set! i 0)
(while (< i (-> task-perms length))
(update-perm! (-> task-perms data i) mode (the-as entity-perm-status clear-mask))
(+! i 1))
(logior! (-> task-perms data 1 status) (entity-perm-status real-complete)))
(let ((perms (-> game-state perm-list)))
(set! i 0)
(while (< i (-> perms length))
(update-perm! (-> perms data i) mode (the-as entity-perm-status clear-mask))
(+! i 1))))
(iterate-process-tree *entity-pool* (lambda ((proc process-drawable)) (deactivate proc) (none)) *null-kernel-context*)
(if (= mode 'game) (task-control-reset mode))
(set! (-> *ACTOR-bank* birth-max) 1000)
0
(none))
(defun reset-cameras ()
"Remove every camera-engine connection, then reconnect all cameras from active levels."
(remove-all *camera-engine*)
(dotimes (level-index (-> *level* length))
(let ((lev (-> *level* level level-index)))
(when (= (-> lev status) 'active)
(let ((cameras (-> lev bsp cameras)))
(when (nonzero? cameras)
(dotimes (i (-> cameras length))
(birth! (-> cameras i))))))))
0
(none))
(defmethod run-logic? ((this process-drawable))
"Return whether this actor should run logic despite actor-pause: it is close enough to the
camera, changing skeleton channels, or explicitly requesting draw updates without skeleton
updates."
(or (not (logtest? (-> this mask) (process-mask actor-pause)))
(or (>= (+ (-> *ACTOR-bank* pause-dist) (-> this root pause-adjust-distance))
(vector-vector-distance (-> this root trans) (math-camera-pos)))
(and (nonzero? (-> this skel)) (!= (-> this skel root-channel 0) (-> this skel channel)))
(and (nonzero? (-> this draw)) (logtest? (-> this draw status) (draw-status no-skeleton-update))))))
(defmethod birth? ((this entity-links) (camera-position vector))
"Return true when this entity is not birth-blocked or dead and lies within the actor birth
distance of camera-position."
(and (not (logtest? (-> this perm status) (entity-perm-status birth-blocked dead)))
(< (vector-vector-distance (-> this trans) camera-position) (-> *ACTOR-bank* birth-dist))))
(defmethod actors-update ((this level-group))
"Compact actor process pools, adapt the pause distance and per-frame birth budget to frame
time, then birth or kill active-level actors according to each level's display mode,
visibility, distance, permanent status, and available actor memory."
(when *compact-actors*
(if (and (= *compact-actors* 'debug) (= (-> *nk-dead-pool* alive-list prev) (-> *nk-dead-pool* first-gap)))
(churn *nk-dead-pool* 1))
(if (nonzero? *debug-dead-pool*) (compact *debug-dead-pool* 10))
(compact *nk-dead-pool*
(the int (lerp-scale 8.0 1.0 (the float (-> *display* frames (-> *display* last-screen) frame run-time)) 2000.0 8000.0))))
(when (not (paused?))
(let ((frame-time (-> *display* frames (-> *display* last-screen) frame run-time)))
(let ((target-pause-distance (fmax 327680.0 (fmin (+ 327680.0 (* 204.8 (the float (- 7000 frame-time)))) (-> *ACTOR-bank* birth-dist)))))
(seek! (-> *ACTOR-bank* pause-dist) target-pause-distance (* 81920.0 (seconds-per-frame))))
(seekl! (-> *ACTOR-bank* birth-max) (the int (lerp-scale 25.0 1.0 (the float frame-time) 2000.0 7000.0)) 10))
(if (movie?) (set! (-> *ACTOR-bank* birth-max) 1000)))
(when *spawn-actors*
(let ((camera-position (camera-pos))
(actor-changes 0))
(dotimes (level-index (-> this length))
(let ((lev (-> this level level-index)))
(when (= (-> lev status) 'active)
(cond
((= (-> lev display?) 'special)
(let* ((links (-> lev entity))
(link-count (-> links length)))
(dotimes (i link-count)
(let ((link (-> links data i)))
(cond
((logtest? (-> link perm status) (entity-perm-status force-birth))
(when (not (or (-> link process) (logtest? (-> link perm status) (entity-perm-status birth-blocked dead))))
(birth! (-> link entity))
(+! actor-changes 1)
(if (>= actor-changes (-> *ACTOR-bank* birth-max)) (return (the-as object #f)))))
(else
(if (and (-> link process) (not (logtest? (-> link perm status) (entity-perm-status no-kill)))) (kill! (-> link entity)))))))))
((= (-> lev display?) 'special-vis)
(let* ((links (-> lev entity))
(link-count (-> links length)))
(dotimes (i link-count)
(let ((link (-> links data i)))
(cond
((and (logtest? (-> link perm status) (entity-perm-status force-birth)) (is-object-visible? lev (-> link vis-id)))
(when (not (or (-> link process) (logtest? (-> link perm status) (entity-perm-status birth-blocked dead))))
(birth! (-> link entity))
(+! actor-changes 1)))
(else
(when (and (-> link process) (not (logtest? (-> link perm status) (entity-perm-status no-kill))))
(kill! (-> link entity))
(+! actor-changes 1)))))
(if (>= actor-changes (-> *ACTOR-bank* birth-max)) (return (the-as object #f))))))
((= (-> lev display?) 'actor)
(let* ((links (-> lev entity))
(link-count (-> links length)))
(dotimes (i link-count)
(let ((link (-> links data i)))
(cond
(#t
(when (not (or (-> link process) (logtest? (-> link perm status) (entity-perm-status birth-blocked dead))))
(birth! (-> link entity))
(+! actor-changes 1)
(if (>= actor-changes (-> *ACTOR-bank* birth-max)) (return (the-as object #f)))))
(else
(if (and (-> link process) (not (logtest? (-> link perm status) (entity-perm-status no-kill)))) (kill! (-> link entity)))))))))
((not *vis-actors*)
(let* ((links (-> lev entity))
(link-count (-> links length)))
(dotimes (i link-count)
(let ((link (-> links data i)))
(cond
((and (< (vector-vector-distance (-> link trans) camera-position) (-> *ACTOR-bank* birth-dist))
(not (logtest? (-> link perm status) (entity-perm-status suppress-birth suppress-birth-2))))
(when (not (or (-> link process) (logtest? (-> link perm status) (entity-perm-status birth-blocked dead))))
(birth! (-> link entity))
(+! actor-changes 1)
(if (>= actor-changes (-> *ACTOR-bank* birth-max)) (return (the-as object #f)))))
(else
(if (and (-> link process) (not (logtest? (-> link perm status) (entity-perm-status no-kill)))) (kill! (-> link entity)))))))))
(*vis-actors*
(when (not (and (-> lev vis-info 0) (-> lev all-visible?)))
(let* ((links (-> lev entity))
(link-count (-> links length))
(low-memory? #f))
(dotimes (i link-count)
(let ((link (-> links data i)))
(cond
;; og:preserve-this check for actor culling setting
((and (#if PC_PORT
(or (with-pc (not (-> *pc-settings* ps2-actor-vis?))) (is-object-visible? lev (-> link vis-id)))
(is-object-visible? lev (-> link vis-id)))
(not (logtest? (-> link perm status) (entity-perm-status suppress-birth suppress-birth-2))))
(when (not (or (-> link process) (logtest? (-> link perm status) (entity-perm-status birth-blocked dead)) low-memory?))
(birth! (-> link entity))
(+! actor-changes 1)
(when (< (/ (the float (memory-free *nk-dead-pool*)) (the float (memory-total *nk-dead-pool*))) 0.1)
(format 0
"WARNING: low actor memory, no birth triggered!!! ~D/~D~%"
(memory-free *nk-dead-pool*)
(memory-total *nk-dead-pool*))
(set! low-memory? #t))))
(else
(when (and (-> link process) (not (logtest? (-> link perm status) (entity-perm-status no-kill))))
(kill! (-> link entity))
(+! actor-changes 1)))))
(if (>= actor-changes (-> *ACTOR-bank* birth-max)) (return (the-as object #f)))))))))))))
0)
(defun entity-birth-no-kill ((ent entity))
"Set no-kill on entity, birth it unless blocked, dead, or already live, and leave the resulting
process connected."
(let ((link (-> ent extra)))
(logior! (-> link perm status) (entity-perm-status no-kill))
(if (not (or (-> link process) (logtest? (-> link perm status) (entity-perm-status birth-blocked dead))))
(birth! (-> link entity)))
(-> link process))
(none))
(defun entity-task-complete-on ((ent entity))
"Set real-complete in entity's task-permission record when the entity has a nonzero task."
(let ((link (-> ent extra)))
(if (nonzero? (-> link perm task))
(logior! (-> *game-info* task-perm-list data (-> link perm task) status) (entity-perm-status real-complete))))
0
(none))
(defun entity-task-complete-off ((ent entity))
"Clear real-complete in entity's task-permission record unless the task is the complete sentinel."
(let ((link (-> ent extra)))
(if (!= (-> link perm task) (game-task complete))
(logclear! (-> *game-info* task-perm-list data (-> link perm task) status) (entity-perm-status real-complete))))
0
(none))
(defmethod set-or-clear-status! ((this entity-actor) (status-mask entity-perm-status) (enabled? symbol))
"Set or clear status-mask in this actor's permanent status. enabled? selects the operation."
(let ((link (-> this extra)))
(if enabled? (logior! (-> link perm status) status-mask) (logclear! (-> link perm status) status-mask))
(-> link perm status))
(none))
(defun process-entity-status! ((proc process) (status-mask entity-perm-status) (enabled? symbol))
"Set or clear status-mask on proc's permanent entity link when proc is still that entity's live
process. Return the resulting status, or zero when the connection is stale."
(cond
((and (-> proc entity) (= proc (-> proc entity extra process)))
(let ((link (-> proc entity extra)))
(if enabled? (logior! (-> link perm status) status-mask) (logclear! (-> link perm status) status-mask))
(the-as int (-> link perm status))))
(else 0)))
(#unless PC_PORT
(defun-debug entity-speed-test ((entity-name string))
"Disable actor spawning, reset actors, time the named entity's birth with the EE Count register
while interrupts are disabled, print the result and process, then kill it."
(local-vars (birth-cycles int))
(let ((ent (entity-by-name entity-name)))
(when ent
(set! *spawn-actors* #f)
(reset-actors 'debug)
0
(disable-irq)
(m Count 0)
(sync.p)
(birth! ent)
(m birth-cycles Count)
(enable-irq)
(format #t "~D spawn ~A ~A ~%" birth-cycles entity-name (-> ent extra process))
(kill! ent)))
(none)))