Part 2 – Code Foundation and Groundwork
My thoughts:
This is not the first ‘large’ project that I have started, and with each of the past projects, I would eventually reach a point where I realize that I could have started it off better. Learning from these past endeavors, keeping in mind what worked and what didn’t, and most importantly why, the first thing that I decided when I thought about how to begin laying out the code on this new project, was that I wanted a modular framework that would be easily extensible as the project progressed. I also wanted it to be generic and flexible enough that it could easily be reused as the groundwork for future projects.
Some modules like character data handling would by nature be largely game specific and need to be heavily modified from game to game, but the framework itself, and certain common utilities like math or xml function libraries should be more universal.
For extensibility and flexibility, the core system shouldn’t need to have anything hard coded about what is loaded, or what update functions to call, or what it is handling specifically. All program actions should be directed dynamically by what has been loaded into the framework.
As each module is loaded, it will add its update handlers to an automated update queue for the core system to work with.
Each module is responsible for constructing its own data handlers, configuring and adding its update handlers to the queue, and loading any other modules it depends on if these required modules are not already loaded.
This allows me to break up a project into small, basic components to build, test, and work with individually. I no longer need to tackle an entire massive project to feel that I’ve completed something, or to see concrete results; I only need to knock out the next module.
The results:
I have a very simple and short main loop, indeed main source file, which becomes a basic template that can be carried to any project I start in the future. This is accompanied with the core framework file with some basic configuration that can be easily modified to adapt to any given new project.
Progress:
The framework core is complete and able to incorporate new modules as they are completed.
Completed and/or functional modules include:
-Math Library –
this won’t ever really be ‘complete’ as it is more a collection of useful functions than an actual engine component, and new useful functions can always be added, but it is in place and fully functional.
-Player Controls –
This handles player input control of the camera and of the player’s character, as well as setting character state/status as a result of the input.
-Character Management –
This module handles the data, state, and status of the characters present. This module handles the updating of all character positions, rotations etc including the player’s character. This module also works with the 3d animation module to control and direct the current animation based on the current character state.
-3d Animation –
This manages the loading and playing of animations for 3d objects.
-Combat –
This handles the checks and calculations involved in combat. Also loads and handles base data for weapons, armor, combat abilities/styles, style-chains etc.
-User Interface –
This handles GUI elements and events, as well as input controls which don’t directly involve controlling the character. This module implements a GUI system based on HTML/CSS layout and styling specifications and is built around Model-View-Controller design principles.
-System –
This manages screen and file output of debugging and program information and metrics. Each component has its own debugging stream (like debug_animation or debug_framework) which can be directed, enabled, or disabled as needed.
-XML Parser –
This is a lightweight function library for loading xml data into various components. It is primarily used by the GUI system for defining and loading views, but is also used for a variety of configuration and data files.
-Evolved’s Advanced Lighting –
I am using his newer version of the system, with a few modifications.
Modules yet to be implemented include management utilities for things like levels, AI, special effects, story dialogue and events and so on.
Psuedo OOP:
While DBpro is not OOP, it can still make use of some basic OOP and namespace concepts to help organize and manage code.
Due to the procedural nature of DBpro, any grouping of variables and functions into a 'class' will *always and only ever* have a single instantiated 'instance' of that class with effectively only static 'members, properties and methods'.
Any refernce to a 'class' refers to this singular instance static class.
These are 'partial' classes which may be extended by other modules. Such extension primarily occurs when a module adds update handlers to the framework.
Any sort of class, namespace or object grouping is by *intent and convention only*: anything 'belonging' to a class is not truly linked in any internal or formal manner, anything notated as private is not truly protected or restricted in any way.
Function Naming Conventions:
Public functions are prefixed with a module or group ('class') identifier, followed by an underscore, then the function name in camelCase.
Public functions are those which are intended to be callable from any arbitrary point within the program, and are typically 'access points' to a class.
Public functions often call additional private functions internally.
Private functions are prefixed with an underscore, followed by a module or class identifier, then an underscore, then the function name in camelCase.
Private functions are those which are intended to be callable only internally within the module, class or namespace they belong to.
For example:
gui_alert(text$) is a 'public' function belonging to the gui 'class' and is intended to be called from anywhere at any time.
_gui_closeAlert(alertID) is a 'private' function belonging to the gui 'class' and is intended to only ever be called by the alert 'object' itself.
_gui_makeElement(tag$, id$, name$, parent$, class$) is a 'private' function belonging to the gui 'class' typically called by public functions like gui_alert(text$) or gui_loadDocument(doc$)
While you might want to create an element at some arbitrary point outside of the module (publicly), it is intended for elements to be managed (privately) through the definition of gui documents and the use of controlled public interfaces.
Update handlers intended to be used by the auto update queue extend the update subclass of the framework class new with public functions.
For example:
framework_update_gui()
framework_update_arcs()
Variable Naming Conventions:
GLOBAL variables and CONSTANTS are in all capitals and spaced with underscore.
For example:
INPUT_READY = TRUE
MY_CHAR = 1
MATH_MODULE_LOADED = FALSE
They may be prefixed for public/private class use.
GUI_ACTIVE_DOC could be a global public variable 'ACTIVE_DOC' belonging to the gui 'class' and refer to a data representation of the current gui layout.
IO_ACTIVE_DOC could be a global public variable 'ACTIVE_DOC' belonging to the file IO 'class' and refer to an actual file open for read/write.
This, creates an effective namespace allowing variable (and function) names to be reused across modules/classes without conflict or confusion.
non-global variables follow camelCase conventions.
For example:
mousePosX = 256
backgroundImage = "My Pic.jpg"
these are effectively local transient throw aways.
Array Naming Conventions:
To distinguish an array() from a funtion(), arrays will use PascalCasing.
They may be noted as public or _private as normal.
for example:
dim math_Arcs() as arcData
dim _math_SomePrivateArray(100)
Lastly, here is a preview of some of the code:
(edit: the tab formatting got wonky in the copy/paste sorry.)
Main.dba
`Initialize General
gosub process_commandLine
set bitmap format 21
sync on : sync rate 0
if SCREEN_WIDTH = 0
load dll "user32.dll",1
sw = call dll(1,"GetSystemMetrics",0) : SCREEN_WIDTH = sw
sh = call dll(1,"GetSystemMetrics",1) : SCREEN_HEIGHT = sh
delete dll 1
endif
if IS_WINDOWED then set window on
set display mode SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 4, 0
hide mouse
autocam off
hide light 0
disable escapekey
Randomize Timer()
`Initialize Framework
#INCLUDE "modules\framework.dba"
gosub init_framework
WRLOG.main, "all initialization finished, enter main loop"
WRLOG.main, " "
DO
`grab loop start time
SYSTEM.timing.loopMark = hitimer()
WRLOG.main, "starting main loop"
`handle update callbacks
for i = 0 to array count(framework_UpdateQueue())
`check for gameplay pause -> run if not paused or if update ignores pause
if UI.status.gamePaused <> 1 or framework_UpdateQueue(i).ignorePause = TRUE
WRLOG.main, "get next update process from queue"
now = timer()
if now - framework_UpdateQueue(i).mark >= framework_UpdateQueue(i).cycle
framework_UpdateQueue(i).mark = now
call function ptr framework_UpdateQueue(i).ptr
endif
endif
next i
AdvLighting_Update()
SYSTEM.timing.loopTime = hitimer() - SYSTEM.timing.loopMark
inc SYSTEM.timing.loopCount
WRLOG.main, "final loop time " + str$(SYSTEM.timing.loopTime)
WRLOG.main, " "
LOOP
END
`==================================================================================================================================
process_commandLine:
type keyValueData
key as string
value as string
endtype
dim Args() as keyValueData
args$ = CL$()
if args$ > ""
array insert at bottom Args()
a$ = first token$(args$, " ")
split string a$, "="
Args().key = get split word$(1)
Args().value = get split word$(2)
repeat
a$ = next token$(" ")
if a$ > ""
array insert at bottom Args()
split string a$, "="
Args().key = get split word$(1)
Args().value = get split word$(2)
endif
until a$ = ""
endif
`valid args
GLOBAL SCREEN_WIDTH = 0
GLOBAL SCREEN_HEIGHT = 0
GLOBAL IS_WINDOWED = 0
array index to top Args()
while array index valid(Args())
select lower$(Args().key)
case "sw": SCREEN_WIDTH = intval(Args().value) : endcase
case "sh": SCREEN_HEIGHT = intval(Args().value) : endcase
case "win": IS_WINDOWED = intval(Args().value) : endcase
endselect
next array index Args()
endwhile
return
framework.dba
REMSTART
-------------------------------------------------------------------------------------
Framework Configuration Module - $Revision: 73 $
-------------------------------------------------------------------------------------
Author: Josh Kirklin (Ortu)
-------------------------------------------------------------------------------------
Description:
Provides the primary handling for loading and managing the framework modules.
*******************************************
Plugin Dependancies:
Matrix1Util_20.dll (IanM)
*******************************************
Classes:
Defines framework
-------------------------------------------------------------------------------------
*******************************************
Provides Public Functions:
framework_require(module as string)
- This will check if the specified module has already been included for the complier, if not, it will do so.
*******************************************
Provides Public Data:
framework_UpdateQueue()
- This array stores pointers to the various update functions required by the loaded modules, as well as timing and cycle data for calling them.
- This array is a final version of framework_UpdateList() sorted by priority.
framework_UpdateList()
- This array stores pointers to the various update functions required by the loaded modules, as well as timing and cycle data for calling them.
- Auto update handlers should initially be written to this array. They will be copied to the final framework_UpdateQueue array for use by the main loop.
* this can probably be removed by writing directly to framework_UpdateQueue and using the mat1 typed array sort functions.
-------------------------------------------------------------------------------------
*******************************************
Provides Private Functions:
*******************************************
Provides Private Data:
*******************************************
Provides Automatic Update Handlers:
- These will be added to the automatic update queue, the settings for each may be configured in the function declarations below.
*******************************************
Notes:
To add a new module, simply add the #INCLUDE and a call to an initialization routine under the appropriate block, or under a new block, within the framework_require() function.
Includes should be wrapped within a check against a loaded flag to prevent duplication of nested #INCLUDE across modules.
For detailed information about what a module does or how to use it, open the module's source file. Each one contains a header with everything you need to know about that module.
REMEND
#CONSTANT TRUE 1
#CONSTANT FALSE 0
#CONSTANT NEWLINE chr$(13)+chr$(10)
#CONSTANT WRLOG write string DEBUG.file
init_framework:
`Setup the auto-update queue ===============================================================================================================
type moduleUpdateData
ignorePause as boolean `if true, update will continue to run when gameplay is paused.
ptr as integer `pointer to function
mark as integer `timestamp of last update call
cycle as integer `ms after last timestamp before calling an update again. used to determine which updates get processed in a given loop.
priority as integer `used to determine the order in which the updates being processed this loop are handled.
`note: Be carefull when using cycle delays and dependant priorities that the settings for linked updates work with each other as expected.
` for example updateA (priority 2, cycle 0) needs the results of updateB (priority 1, cycle 5000), updateA will fire every loop but will only get new data every 5 seconds due to reliance on the slower updateB
` thus updateA will effectively have an apparent cycle of 5 sec, but it will continue to tie up much more processing time, reusing old data every loop.
endtype
dim framework_UpdateList() as moduleUpdateData `used for temp unsorted holding while loading
dim framework_UpdateQueue() as moduleUpdateData `final sorted used while running
`Load the primary modules =====================================================================================================================
WRLOG.main, "starting framework module loader"
framework_require("system")
framework_require("media")
framework_require("user_interface")
framework_require("characters")
framework_require("player_control")
framework_require("combat")
WRLOG.main, "framework module loading finished"
`Sort the update queue by priority and cleanup =================================================================================================================
p=1
while array count(framework_UpdateList()) > -1
array index to top framework_UpdateList()
while array index valid(framework_UpdateList())
if framework_UpdateList().priority = p
array insert at bottom framework_UpdateQueue()
framework_UpdateQueue().ignorePause = framework_UpdateList().ignorePause
framework_UpdateQueue().ptr = framework_UpdateList().ptr
framework_UpdateQueue().mark = framework_UpdateList().mark
framework_UpdateQueue().cycle = framework_UpdateList().cycle
framework_UpdateQueue().priority = framework_UpdateList().priority
array delete element framework_UpdateList()
else
next array index framework_UpdateList()
endif
endwhile
inc p
endwhile
undim framework_UpdateList()
return
function framework_require(module as string)
select module
`//////////////////////////////////////////////////////////////////////////////////////////////////////////
`// //
`// The following modules are general utilities and should never require game-specific modifications. //
`// //
`//////////////////////////////////////////////////////////////////////////////////////////////////////////
case "math_utils":
if MATH_UTILS_LOADED = 0
#INCLUDE "modules\math_utils.dba"
gosub init_math_utils
endif
endcase
case "media":
if MEDIA_MODULE_LOADED = 0
#INCLUDE "modules\media.dba"
gosub init_media_module
endif
endcase
case "xml_utils":
if XML_UTILS_LOADED = 0
#INCLUDE "modules\xml_utils.dba"
gosub init_xml_utils
endif
endcase
`//////////////////////////////////////////////////////////////////////////////////////////////////////////
`// //
`// The following modules are common components and may require minor game-specific modifications. //
`// //
`//////////////////////////////////////////////////////////////////////////////////////////////////////////
case "system":
if SYSTEM_UTILS_LOADED = 0
#INCLUDE "modules\system.dba"
gosub init_debug_utils
endif
endcase
case "animate_3d":
if ANIM3D_MODULE_LOADED = 0
#INCLUDE "modules\animation_3d.dba"
gosub init_animate_3d_utils
endif
endcase
case "user_interface":
if UI_MODULE_LOADED = 0
#INCLUDE "modules\user_interface.dba"
gosub init_ui_utils
endif
endcase
`//////////////////////////////////////////////////////////////////////////////////////////////////////////
`// //
`// The following modules are unique components and are almost entirely game-specific. //
`// //
`//////////////////////////////////////////////////////////////////////////////////////////////////////////
case "characters":
if CHARACTERS_MODULE_LOADED = 0
#INCLUDE "modules\characters.dba"
gosub init_characters_module
endif
endcase
case "player_control":
if PLAYER_CONTROLS_LOADED = 0
#INCLUDE "modules\player_control.dba"
gosub init_player_control_module
endif
endcase
case "combat":
if COMBAT_MODULE_LOADED = 0
#INCLUDE "modules\combat.dba"
gosub init_combat_module
endif
endcase
endselect
endfunction
animation_3d.dba
REMSTART
-------------------------------------------------------------------------------------
3d Object Animation Module - $Revision: 69 $
-------------------------------------------------------------------------------------
Author: Josh Kirklin (Ortu)
-------------------------------------------------------------------------------------
Description:
Provides various 3d animation loading and handling functions and processes.
*******************************************
Plugin Dependancies:
Matrix1Util_05.dll (IanM)
Matrix1Util_20.dll (IanM)
*******************************************
Classes:
Extends framework
Defines anim3d
-------------------------------------------------------------------------------------
*******************************************
Provides Public Functions:
anim3d_loadObjectAnimation(object_file as string, append as int)
+ Returns the id as int of the animation set created.
- This will open the .anim file associated with the object specified and add to the _anim3d_AnimationSets() array for calling by sequence name.
- This is generally called immediately following the loading of the object, and additionally as needed for appending further animations later.
- Use an existing set id for the append argument to append the new animations to that set, or use 0 to start a new set. -> this could use -1 to start new, then 0 doesn't need to be forced in as null.
- The return value should be captured and linked to the object id so that the animation data can be accessed by that object later.
- Character objects, for instance, provide Chars(this_char).handle.myAnimationSet for storing this return.
anim3d_setActiveAnimation(object as int, animation_set as int, key_name as string, action as string)
+ Returns the id as int of animation created.
- This creates a new animation handler using the set and key provided and adds it to the array "anim3d_ActiveAnimations()" management list.
- Valid actions are "play", "loop", or another key_name to follow with after one play through.
- A followup key will default to loop when it begins.
*******************************************
Provides Public Data:
anim3d_ActiveAnimations()
- This array holds data about the currently playing animations for all animated objects.
-------------------------------------------------------------------------------------
*******************************************
Provides Private Functions:
*******************************************
Provides Private Data:
_anim3d_AnimationSets()
- This array holds data about all animation cycles as 'key names' associated with an object and the range of frames that each contains.
- This is an 'internal' structure whose contents should not be accessed or modified directly.
_anim3d_AnimationVariants()
- This array holds data about alternate animation cycles for certain variable animation keys. ex: idle0, idle1, idle2
- This is an 'internal' structure whose contents should not be accessed or modified directly.
*******************************************
Provides Automatic Update Handlers:
- These will be added to the automatic update queue, the settings for each may be configured in the function declarations below.
framework_update_anim3d()
- This will advance all animations within the 3d animations management list based on the elapsed time since the last update call.
- This update will check for end conditions and terminate the animation as needed, removing it from the management list.
- Upon ending, if a follow up key was specified, that key will begin looping.
*******************************************
Notes:
REMEND
init_animate_3d_utils:
WRLOG.main,"init animate 3d"
`Include Dependent Modules =======================================================================
`Constants =======================================================================================
#CONSTANT KEYED_FPS 45.0
`Types ===========================================================================================
type animSet3dData
set as integer `group this key belongs to, associates with the proper object skeleton
key as string `sequence name
start as integer `this key first frame
stop as integer `this key last frame
allowVariants as integer
endtype
dim _anim3d_AnimationSets() as animSet3dData
type anim3dData
object as integer `object id being handled
set as integer `set current key belongs to
key as string `current key being played
frame as float `current frame
start as integer `first frame
stop as integer `last frame
time as integer `time of last update, for calculating elapsed
action as string `handling flag, 'loop' to loop current key or another key to loop after current key plays once
variantCount as integer `number of variants of this key for randomization of long loops
endtype
dim anim3d_ActiveAnimations() as anim3dData
type variantData
set as integer `group this count belongs to
baseKey as string `key of variant 0 sans id# ie 'idle0' = 'idle'
baseLen as integer `length of key sans id# ie 'idle0' = 4
variantCount as integer `number of variants in specified key/set
endtype
dim _anim3d_AnimationVariants() as variantData
`Globals =========================================================================================
`Set Update Callbacks ============================================================================
framework_update_anim3d() `this adds the update function to the automatic queue
`Finish ==========================================================================================
GLOBAL ANIM3D_MODULE_LOADED = 1
WRLOG.main, "animate 3d loaded"
return
`Functions ===========================================================================================
function framework_update_anim3d()
`Initialize Updates
if ANIM3D_MODULE_LOADED = 0
WRLOG.main, "adding 3d animation handler to update queue"
array insert at bottom framework_UpdateList()
framework_UpdateList().ptr = get ptr to this function()
framework_UpdateList().mark = timer()
framework_UpdateList().cycle = 0
framework_UpdateList().priority = 3
else
WRLOG.main, "starting 3d animation updates"
`handle animations
array index to top anim3d_ActiveAnimations()
while array index valid(anim3d_ActiveAnimations())
`check that this handles a valid object
if object exist(anim3d_ActiveAnimations().object)
`get elapsed time
elapsed# = timer() - anim3d_ActiveAnimations().time
dec elapsed#, UI.status.pauseElapsed
`get new frame
newFrame# = anim3d_ActiveAnimations().frame + ((KEYED_FPS / 1000.0) * elapsed#)
if newFrame# < anim3d_ActiveAnimations().start
newFrame# = anim3d_ActiveAnimations().start
endif
`handle looping
if int(newFrame#) >= anim3d_ActiveAnimations().stop
if anim3d_ActiveAnimations().action = "loop"
`this loops, wrap it
while int(newFrame#) > anim3d_ActiveAnimations().stop
`get new wrapped frame
overFlow = newFrame# - anim3d_ActiveAnimations().stop
newFrame# = anim3d_ActiveAnimations().start + overFlow - 1
endwhile
`set to new frame
anim3d_ActiveAnimations().frame = newFrame#
set object frame anim3d_ActiveAnimations().object, int(anim3d_ActiveAnimations().frame)
anim3d_ActiveAnimations().time = timer()
next array index anim3d_ActiveAnimations()
else
`end it then execute next action
newFrame# = anim3d_ActiveAnimations().stop
anim3d_ActiveAnimations().frame = newFrame#
set object frame anim3d_ActiveAnimations().object, int(anim3d_ActiveAnimations().frame)
anim3d_ActiveAnimations().time = timer()
`setup next by action
anim3d_ActiveAnimations().key = anim3d_ActiveAnimations().action
`get new key info
array index to top _anim3d_AnimationSets()
while array index valid(_anim3d_AnimationSets())
if _anim3d_AnimationSets().set = anim3d_ActiveAnimations().set
if _anim3d_AnimationSets().key = anim3d_ActiveAnimations().key
anim3d_ActiveAnimations().start = _anim3d_AnimationSets().start
anim3d_ActiveAnimations().stop = _anim3d_AnimationSets().stop
anim3d_ActiveAnimations().time = timer()
anim3d_ActiveAnimations().action = "loop"
`TODO: check for variants same as in char module
exit
else
next array index _anim3d_AnimationSets()
endif
else
next array index _anim3d_AnimationSets()
endif
endwhile
endif
else
`not at end yet, set to newFrame#
anim3d_ActiveAnimations().frame = newFrame#
set object frame anim3d_ActiveAnimations().object, int(anim3d_ActiveAnimations().frame)
anim3d_ActiveAnimations().time = timer()
next array index anim3d_ActiveAnimations()
endif
endif
endwhile
WRLOG.main, "finished 3d animation updates"
endif
endfunction
function anim3d_loadObjectAnimation(file$, appendTo)
WRLOG.anim3d, "opening animation file: "+file$
WRLOG.anim3d, "append index: "+str$(appendTo)
if file exist(file$) = 1
`because we are using append = 0 to indicate make a new set, can't use index 0 as a set. if empty, add a null -> this could use -1 to start new, then 0 doesn't need to be forced in as null.
if array count(_anim3d_AnimationSets()) < 0
array insert at bottom _anim3d_AnimationSets()
_anim3d_AnimationSets().key = "null"
endif
`get ID
if appendTo = 0
animID = array count(_anim3d_AnimationSets()) + 1
else
animID = appendTo
endif
WRLOG.anim3d, "animID " + str$(animID)
`handle variant counts
dim Temp() as variantData
`handle the file
f = find free file()
open to read f, file$
while file end(f) = 0
`read and parse data
read string f,a$
seqName$ = first token$(a$,",")
startFrame = int(val(next token$(",")))
endFrame = int(val(next token$(",")))
allowVar$ = next token$(",")
if allowVar$ = "t"
WRLOG.anim3d, "key "+seqName$
WRLOG.anim3d, "variant allowed for this key"
allowVariants = 1
`check for existing count
addNew = 1
WRLOG.anim3d, "check for existing count"
array index to top Temp()
while array index valid(Temp())
if Temp().baseKey = seqName$
WRLOG.anim3d, "count found, inc"
WRLOG.anim3d, "old count "+str$(Temp().variantCount)
`add to count
addNew = 0
Temp().variantCount = Temp().variantCount + 1
seqName$ = seqName$ + str$(Temp().variantCount)
WRLOG.anim3d, "new count "+str$(Temp().variantCount)
exit
else
next array index Temp()
endif
endwhile
if addNew = 1
WRLOG.anim3d, "no count found, add new"
`start new count
array insert at bottom Temp()
Temp().baseKey = seqName$
Temp().baseLen = len(seqName$)
Temp().variantCount = 0
seqName$ = seqName$ + str$(Temp().variantCount)
endif
else
allowVariants = 0
endif
`add to sets array
array insert at bottom _anim3d_AnimationSets()
_anim3d_AnimationSets().set = animID
_anim3d_AnimationSets().key = seqName$
_anim3d_AnimationSets().start = startFrame
_anim3d_AnimationSets().stop = endFrame
_anim3d_AnimationSets().allowVariants = allowVariants
endwhile
close file f
`link variants
array index to top _anim3d_AnimationVariants()
array index to top Temp()
while array index valid(Temp())
array insert at bottom _anim3d_AnimationVariants()
_anim3d_AnimationVariants().set = animID
_anim3d_AnimationVariants().baseKey = Temp().baseKey
_anim3d_AnimationVariants().baseLen = Temp().baseLen
_anim3d_AnimationVariants().variantCount = Temp().variantCount
next array index Temp()
endwhile
endif
endfunction animID
function anim3d_setActiveAnimation(object, set, key$, action$)
`get set and key data
array index to top _anim3d_AnimationSets()
while array index valid(_anim3d_AnimationSets())
WRLOG.anim3d, "checking myset "+str$(set)+" against animset "+str$(_anim3d_AnimationSets().set)
if _anim3d_AnimationSets().set = set
WRLOG.anim3d, "checking key "+key$+" against animset key "+_anim3d_AnimationSets().key
if _anim3d_AnimationSets().key = key$
WRLOG.anim3d, "set and key matched, add to anims3d array"
`add to animation handler
array insert at bottom anim3d_ActiveAnimations()
anim3d_ActiveAnimations().object = object
anim3d_ActiveAnimations().set = set
anim3d_ActiveAnimations().key = key$
anim3d_ActiveAnimations().frame = _anim3d_AnimationSets().start
anim3d_ActiveAnimations().start = _anim3d_AnimationSets().start
anim3d_ActiveAnimations().stop = _anim3d_AnimationSets().stop
anim3d_ActiveAnimations().time = timer()
anim3d_ActiveAnimations().action = action$
animID = array count(anim3d_ActiveAnimations())
WRLOG.anim3d, "animID " + str$(animID)
`check for variants
WRLOG.anim3d, "checking for variants on new anim setup for key " + _anim3d_AnimationSets().key
if _anim3d_AnimationSets().allowVariants = 1
WRLOG.anim3d, "variants allowed, get count"
array index to top _anim3d_AnimationVariants()
while array index valid(_anim3d_AnimationVariants())
WRLOG.anim3d, "checking varSet " + str$(_anim3d_AnimationVariants().set) + " against my set " + str$(set)
if _anim3d_AnimationVariants().set = set
WRLOG.anim3d, "checking baseKey " + _anim3d_AnimationVariants().baseKey + " against my key " + left$(key$,_anim3d_AnimationVariants().baseLen)
if _anim3d_AnimationVariants().baseKey = left$(key$,_anim3d_AnimationVariants().baseLen)
WRLOG.anim3d, "set and key matched, count = " + str$(_anim3d_AnimationVariants().variantCount)
anim3d_ActiveAnimations().variantCount = _anim3d_AnimationVariants().variantCount
exit
else
next array index _anim3d_AnimationVariants()
endif
else
next array index _anim3d_AnimationVariants()
endif
endwhile
endif
exitfunction animID
else
next array index _anim3d_AnimationSets()
endif
else
next array index _anim3d_AnimationSets()
endif
endwhile
endfunction animID
New progress this week:
Not a lot tbh. I was out of town with family for the long holiday weekend, and most of my work is done on weekends.
I have started modeling out the roof and added window holes to the cottage walls. I'll stick up a screenshot once I have these textured.