XML:TRAM: Difference between revisions

m
phone view compatible
mNo edit summary
m (phone view compatible)
 
(32 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{XML_File_Header | prev=TRAC | type=TRAM | next=TRAS | name=Totoro Animation}}
{{XML_File_Header | prev=TRAC | type=TRAM | next=TRAS | name=Totoro Animation}}
{{update}}
{{update|This article contains links to the now defunct Oni Central Forum. Sections: List of Tags near <Attack>, Editing 3D Data}}
{{TOCfloat|side=right}}
{{TOCfloat|side=right}}
==General information==
==General information==
TRAM files — animation data for characters — are basically made of three chunks:
TRAM files — animation data for characters — are basically made of three chunks:
* Metadata or "header": animation type, state, flags, particle, sounds, etc.
* Metadata or "header": animation type, state, flags, particle, sounds, etc.
* Animation data: pelvis heights, pelvis velocities, bone rotations
* Animation data: '''pelvis heights, pelvis velocities (root motion)''', bone rotations
* Attack data: damage, self-damage, extents (danger zones for AI awareness) and throws
* Attack data: damage, self-damage, extents (danger zones for AI awareness) and throws
Root motion: a character moves by changing the position of its root bone (pelvis). The animated legs just create the '''illusion for the player''' that they are responsible for the motion.
Bones: Each character has '''19 separate body parts''' which get animated (rotated) relative to each other. Compared to modern games Oni's bone system is [[wp:Deprecation|deprecated]]. The body doesn't get deformed by following animated bones. Oni modders often use the terms mesh and bone interchangeably. Only the pelvis has '''translation''' data (heights and velocities) besides rotations. The hierarchy of the body parts (or bones) is determined by [[XML:TRBS#Standard_TRIA_hierarchy|TRIA]].


The term '''animation''' is often shortened to '''anim''' by modders, following the naming of commands in BSL: chr_wait_animstate, chr_wait_animtype, env_anim, etc.
The term '''animation''' is often shortened to '''anim''' by modders, following the naming of commands in BSL: chr_wait_animstate, chr_wait_animtype, env_anim, etc.


TRAMs used in Oni follow a naming pattern. A list of all combat TRAMs in-game and their naming can be found [[Combat moves|here.]]
Oni's code and debugging messages tend to spell the word variant as "varient". Blame the programmers for that one. Typically we use the correct spelling here, but OniSplit's XML follows Oni's misspelling, so you may see "varient" creep into the wiki here and there.
 
TRAMs used in Oni follow a naming pattern. A list of all combat TRAMs in-game and their naming can be found [[Combat moves|here]].


==Decision path of animation lookup==
==Open questions==
* If the never-used Thrown type can be applied as type 18 the table looks much more complete. "Restoring" forward tackles (face-to-face, src run + tgt run) can be omitted as the AI would most often stop movement for actual combat?
 
* All important header data of STRCOMrun_thw_fw_p and STRCOMrun_thw_fw_pl is identical. That means the engine picks the correct TRAM by using an unknown context, possibly this is similar or part of the distinction of "forward" (face-to-face) and "backward" (face-to-back).
: '''Maybe the algorithm checks always tgt's rotation and relative position to src at the same time but for most cases only tgt's rotation (facing) is relevant.'''
 
* Also, STRCOMrun_thw_fw_p(l) routing to Thrown5 and Thrown6 shows that Thrown anim types might be used however we like. A src-tgt-anim-type-pairing might be simply a ''convention''. Well, someone could check the source code...
 
* The existence of STRCOMrun_thw_fw_p(_tgt) and STRCOMrun_thw_fw_pl(_t) means that we need extra research to understand the full extent of the throw system.
* The variant pickup system is not clear yet: is a RIFCOM idle and a RIFNAT idle both possible?
** Or do rightpistol, leftpistol, rightrifle and leftrifle variants count as subsets of combat?
 
==Summary of animation lookup logic==
* First the game builds a pool of anims a character can use. They are loaded from the TRAC file registered in [[ONCC]], including parent TRACs.
* First the game builds a pool of anims a character can use. They are loaded from the TRAC file registered in [[ONCC]], including parent TRACs.
* Anims from a child TRAC override those of the parent if their combination of state, type and variant is exactly the same.
* Anims from a child TRAC override those of the parent if their combination of state, type and variant is exactly the same.
Line 19: Line 38:
* Here is the full decision path:
* Here is the full decision path:
** anim '''state''' check
** anim '''state''' check
** event (user input, or engine input for the AI) -> [[XML_talk:StNA|context check]] -> anim '''type''' check
** event (user input, or engine input for the AI) -> [[XML talk:StNA|context check]] -> anim '''type''' check
** anim '''variant''' check
** anim '''variant''' check
** TRAC '''weight''' check
** TRAC '''weight''' check


Detailed example: Let's say that we shapeshifted to a Ninja ONCC, we are in the state Standing, and we hit the action key to perform a taunt. The engine recognizes the context (a hostile character to interact with) and looks up anims of type Taunt. Since we are in combat mode, the next anim must be of variant Combat if possible. These lookups boil down to two valid possible anims: NINCOMtaunt1 and NINCOMtaunt2. If there are multiple anims available at this point, the engine picks one of them randomly.
===Elaboration===
Animations never stop – they flow from one to another. (See the concept of [[wp:Finite-state machine|state machines]] in gaming.) The default case is a character getting spawned and then just standing there: he idles forever. This brings us to the core attributes of every animation: '''type, state, variant'''.
 
Now then, the idle animation does not link to a specific follow-up animation – it rather links to a follow-up ''state''.
: Ironically all anims have a <FromState> and a <ToState> field, but no obvious <CurrentState> which can cause confusion on what the current anim's state actually is. This gives us two options of how to think about the situation: either consider <FromState> as usually being the <CurrentState>, or states exist only ''between'' anims and serve as ''transition rules''.
 
Linking to a follow-up state allows for multiple choices of what specific anim comes next. An anim is picked up randomly from a pool of valid anims. Their probability or <Weight> is determined by the anim listing in the TRAC – the anim collection (see {{SectionLink||Weights}} for an explanation of exactly how <Weight> is handled).
 
Anyway, to let an character flow from one anim state to another, the <ToState> is used. Let's say an SBG grenade hits Konoko. The particle's '''damage type''' is '''blownup'''; this and the direction of the incoming damage force an anim of type '''Blownup''' or '''BlownupBehind''' to play, such as TRAMKONOKOblownup1.
 
Thus, Konoko falls to her back, going into the FallenBack state. The engine picks up the next anim which has <FromState>FallenBack. Also, receiving damage puts a character into combat mode, so further anims will be of variant Combat. But if the player gives no input, Konoko will remain on the ground, as <ToState>FallenBack creates a loop.
 
So here's a question: why isn't a getup anim like KONCOMgetup_lt played automatically since it also has <FromState>FallenBack and <Varient>Combat? Answer: it differs in its <Type>. Most [[XML_talk:StNA|anim types are bound to user input]] and therefore are excluded from the automatically created pool of valid anims which can follow.
 
Now let's consider what happens upon user input. A leftward movement of the mouse tells the engine to use an anim of type StandingTurnLeft. There are two anims with that type, KONOKOcomb_turnlt and KONCOMgetup_lt, but only the latter matches the current <FromState> condition.
: In fact, KONOKOcomb_turnlt has <Varient> Combat – the "comb" here stands for "combat", not "combo". Following Oni's naming convention, the file ''should'' have been named KONCOMturn_lt.
 
Knowing this behavior, we can deduce that anim types registered to user input will make the engine ignore all the automatic anims inside the pool, therefore breaking the loop of KONCOMfallen_back. And since KONCOMgetup_lt has <ToState>Standing and Konoko is still in combat mode, the following idle anim must also have <Varient>Combat. So the next anim played will be KON'''COM'''idle1 or KON'''COM'''idle2.
 
Konoko will eventually calm down when her ONCC <FightModeTimer> runs down, and then KON'''OKO'''idle1 or KON'''OKO'''idle2 will be played instead. (Actually, the COMidle will continue until a non-combat anim such as running breaks that loop. You won't ever see Konoko drop from combat readiness to her regular idle animation. Instead she will go from combat idle to running, to stopping, then standing at ease.)
 
Note the fallback behavior of anim lookup: if the engine cannot find an anim for the active variant (or "mode") of the character, it will choose the normal (non-variant-matching) version. Anims can be in the following variants: Combat, LeftPistol, LeftRifle, Panic, RightPistol, RightRifle and Sprint.
 
As for combinations of variants, it's possible, yes, but rarely and only Sprint and one weapon variant at a time.
 
==Weights==
Let's say that we're playing as a Ninja, we are in the state Standing, and we hit the action key to perform a taunt. The engine recognizes the context (no console to interact with), places the character in combat mode, and looks up anims of type Taunt. Since we are in combat mode, the next anim must be of variant Combat if possible. These lookups boil down to two valid possible anims: NINCOMtaunt1 (a spin) and NINCOMtaunt2 (the [[wp:Moonwalk (dance)|moon walk]] Easter egg). The engine picks one of them randomly.
 
However, NINCOMtaunt1 has a TRAC Weight (probability) of 100 while NINCOMtaunt2 has a TRAC Weight value of just 5. So the chances are quite high that we will not see the moon walk taunt.
 
            <TRACAnimation>
                <Weight>100</Weight>
                <Animation>TRAMNINCOMtaunt1</Animation>
            </TRACAnimation>
            <TRACAnimation>
                <Weight>5</Weight>
                <Animation>TRAMNINCOMtaunt2</Animation>
            </TRACAnimation>
 
If there is only one anim to choose from, it will always play no matter what its Weight, even if it is zero. When it comes to multiple anims being a valid choice, the calculation is: weight of anim / sum of all valid anim weights. For NINCOMtaunt2, this means 5 / 105 = 0.047, or a 4.7% chance of a moon walk.


However, NINCOMtaunt1 has a TRAC Weight (probability) of 100 while NINCOMtaunt2 has a TRAC Weight value of just 5. So the chances are quite high that we will not see the Moon Walk taunt.
==Idle timer==
There's a semi-famous Easter egg where [https://www.youtube.com/watch?v=qbo2Q-VD6dc Konoko will sneeze] if she stands still for a long time. The timer for this is so long that most players never see it unless they step away from the game and leave it running without hitting F1. You might think that the way the sneeze is triggered is to set a low weight for it so that it's very unlikely a repeating idle cycle will choose the anim. But here's the sneeze anim in the TRAC with a Weight of 100:


Keep in mind that if the engine cannot find an anim of a specific variant, it will fall back to a non-variant anim instead.
            <TRACAnimation>
                <Weight>100</Weight>
                <Animation>TRAMKONOKOidle_spec3</Animation>
            </TRACAnimation>


Confused by how Weights work? Apparently, the Weight value is only one part of the equation — otherwise NINCOMtaunt2 would never execute. If there is only one anim to choose from, it will always play no matter what its Weight, even if it is zero. When it comes to multiple anims being a valid choice, the calculation seems to be: weight / sum of all weights. For NINCOMtaunt2, this means 5 / 105 = 0.047 (4.7%).
What's going on here? All regular idle anims are in fact of '''type Stand''', while Konoko's idle_spec3 anim above is of '''type Idle'''. Anims of '''type Idle''' are played every time the idle animation timer expires. This is found in ONCC as <IdleAnimation1Timer> and <IdleAnimation2Timer>. These timers are set to 30,000 for every character. The number is in ticks, so if we convert to minutes – 30,000 / 60 / 60 = 8.3̅ – we find that the secret idle anim for Konoko (or any character) will play every 8⅓ minutes.


==List of tags, types, and flags==
==List of tags, types, and flags==
Line 132: Line 194:
::(This bit is not stored on disk; it is used at runtime to mark that the animation was loaded.)
::(This bit is not stored on disk; it is used at runtime to mark that the animation was loaded.)
:Invulnerable
:Invulnerable
::While playing this animation, the player is invulnerable to melee damage and also cannot be thrown. Damage from particles and fall damage still hurt.
::While playing this animation, the player is invulnerable to melee damage and also cannot be thrown, but can still take damage from particles and falls.
:BlockHigh
:BlockHigh
::While playing this animation, the player is invulnerable to high or undefined attacks within some arc in front of him. If you set this flag on an animation where the player character is spinning, then the character can still be kicked in the back or thrown.
::While playing this animation, the player can block high or undefined-height attacks within some arc in front of him. The height of an attack is found under the <Attack> section. If you set this flag on an animation where the player character is spinning, then the character can still be kicked in the back or thrown.
:BlockLow
:BlockLow
::Same as above, only it can block low or undefined attacks.
::Same as above, only it can block low or undefined-height attacks.
:Attack
:Attack
::Animations with an attack part have this turned on. It's unclear what it does, but it may enable the melee soft-lock (where the character turns a bit during the animation to face a nearby enemy).
::Unused by the engine.
:DropWeapon
:DropWeapon
::If the player is armed, he drops his weapon when this animation plays.
::If the player is armed, he drops his weapon when this animation plays.
:InAir
:InAir
::Something to do with jumps; not investigated.
::Unused by the engine.
:Atomic
:Atomic
::The whole animation must be played; player cannot interrupt it once it starts.
::Unused by the engine. Only the Start and End frames of the Atomic field below matter.
:NoTurn
:NoTurn
:::Player cannot turn by mouse while performing this animation.
:::Player cannot turn while performing this animation.
:AttackForward
:AttackForward
::Unknown, but it looks like this is a hint for the AI about where an attack is aiming, from the player's point of view.
::Unused by the engine.
:AttackLeft
:AttackLeft
::Same as above.
::Same as above.
Line 160: Line 222:
::Unknown, but maybe it has something to do with {x,y,z} velocities and the fact that directional jumps, for example, take information about their direction vector from the {x,z} velocity part of the TRAM (the vertical Y component is in the ONCC).
::Unknown, but maybe it has something to do with {x,y,z} velocities and the fact that directional jumps, for example, take information about their direction vector from the {x,z} velocity part of the TRAM (the vertical Y component is in the ONCC).
:ThrowSource
:ThrowSource
::Unknown, but throws use it.
::Oni expects this flag on all throw source animations.
::: Probably a developer relic. Throws work in animation pairs: whenever a throw source is played, the other character must perform the throw target animation. See throw(n) types:
:::Throws work in animation pairs: whenever a throw source is played, the other character must perform the throw target animation. See throw(n) types:
:::: TRAM <TargetType>
::::TRAM <TargetType> from [[XML:StNA#Animation types|StNA]] (first <u>used</u> throw starts at #96).
:::: [[XML:StNA#Animation_types|StNA]] (First <u>used</u> throw starts at #96.)
:::Characters have only their own pool of animations available. And since enemy's TRAC might not contain the necessary animation the information must be provided by the throw source TRAC.  
::: Characters have only their own pool of animations available. And since enemy's TRAC might not contain the necessary animation the information must be provided by the throw source TRAC.  
::: Since every throw animation must have a throw animation type the ThrowSource flag is actually redundant.
::: Since every throw animation must have a throw animation type the ThrowSource flag is actually redundant.
:ThrowTarget
:ThrowTarget
Line 170: Line 231:
:::If you set the first attack part to be able to deal damage from the 1st to the 100th frame of the TRAM, and the second attack part to deal damage within that range (e.g. from the 25th to the 41st frame), then the first attack part will deal damage from the 1st to the 100th frame (as usual), but even if it hits, that second attack part can hurt the enemy as well during frames 25–41. Note that the second attack part must be executed within the first attack part's active "window", otherwise it won't work.
:::If you set the first attack part to be able to deal damage from the 1st to the 100th frame of the TRAM, and the second attack part to deal damage within that range (e.g. from the 25th to the 41st frame), then the first attack part will deal damage from the 1st to the 100th frame (as usual), but even if it hits, that second attack part can hurt the enemy as well during frames 25–41. Note that the second attack part must be executed within the first attack part's active "window", otherwise it won't work.
:RealWorld
:RealWorld
::It appears this flag was used to create a TRAM and an OBAN from one animation source. The OBAN is supposed to store pelvis rotations and positions, allowing a character to move over obstacles/gaps. The vast majority of them are used in cutscenes.
::Unused by the engine. It appears this flag was used during the authoring process to create a TRAM and an OBAN from one animation source. The OBAN is supposed to store pelvis rotations and positions, allowing a character to move over obstacles/gaps. The vast majority of TRAMs with this flag are used in cutscenes.
:DoAim
:DoAim
::Applies the aiming animation overlay (PIS/RIF) if the player has a weapon.
::Applies the aiming animation overlay (PIS/RIF) if the player has a weapon.
Line 178: Line 239:
::Player can pick up an item during this animation if he intersects with one during his movement.
::Player can pick up an item during this animation if he intersects with one during his movement.
:Aim360
:Aim360
::Unknown.
::Unused by the engine.
:DisableShield
:DisableShield
::If the player has an active supershield (chr_super "name" 1), this forces him to disable it (chr_super "name" 0)
::If the player has an active [[supershield]], this disables it (the flag is placed on four Mutant Muro attacks: his two supers, the claw-stuck-into-ground move and the sliding "[[wikt:kancho|kancho]]" maneuver; it is also hardcoded to be removed when the Zeus Thunderbolt attack is used).
:NoAIPickup
:NoAIPickup
::AIs are not permitted to pick up items with this animation.
::AIs are not permitted to pick up items with this animation.
Line 186: Line 247:
| <Atomic>
| <Atomic>
| parent tag
| parent tag
| Makes animation particle atomic?
| Animation cannot be interrupted by player between the Start and End frames.
|-
|-
| <Start>
| <Start>
Line 198: Line 259:
| <Invulnerable>
| <Invulnerable>
| parent tag
| parent tag
| Character will not take melee damage during a time frame defined by the Start and End frames.
| Character will not take melee damage and cannot be thrown between the Start and End frames.
|-
|-
| <Start>
| <Start>
Line 307: Line 368:
|valign="top"| <FinalRotation>
|valign="top"| <FinalRotation>
|valign="top"| float
|valign="top"| float
|valign="top"| Ending rotation in degrees. (During an animation, the camera is detached rotation-wise. If this value matches the body's final rotation, it will prevent a "glitchy" re-attaching.)
|valign="top"| Ending rotation in degrees. (During an animation on the player character, the camera is detached rotation-wise. If this value matches the body's final rotation, it will prevent a "glitchy" re-attaching.)
|-
|-
|valign="top"| <Direction>
|valign="top"| <Direction>
|valign="top"|
|valign="top"|
| Used by [[AI#Melee_combat_behaviors|AI melee system]].
| Used by [[AI#Melee combat behaviors|AI melee system]].
:None
:None
:Forward
:Forward
Line 320: Line 381:
| <Vocalization>
| <Vocalization>
| int
| int
| ID of one of the [[XML:SNDD#Step_1:_Preparing_the_TRAM|SoundConstants in ONCC]]
| ID of one of the [[XML:SNDD#Step 1: Preparing the TRAM|SoundConstants in ONCC]]
|-
|-
| <ActionFrame>
| <ActionFrame>
Line 374: Line 435:
| <MotionBlur>
| <MotionBlur>
| parent tag
| parent tag
|
|  
|-
|-
| <MotionBlur>
| <MotionBlur>
Line 404: Line 465:
| <Start>
| <Start>
| int16
| int16
|
| Start frame of motion blur sequence
|-
|-
| <End>
| <End>
| int16
| int16
|
| End frame of motion blur sequence
|-
|-
| <Lifetime>
| <Lifetime>
| int8
| int8
|
| Lifetime of each "ghost" in frames.
|-
|-
| <Alpha>
| <Alpha>
| int8
| int8
|
| Transparency of "ghosts".
|-
|-
| <Interval>
| <Interval>
| int8
| int8
|
| Frame interval between each rendered "ghost".
|-
|-
| <Footsteps>
| <Footsteps>
Line 528: Line 589:
|valign="top"| <Position>
|valign="top"| <Position>
|valign="top"| 3 * float
|valign="top"| 3 * float
| Contains XYZ values, which position the target character:
| Contains XYZ values, which position the target character at the start of the animation:
*X - Side axis. Negative values move the target to the right and positive values move the target to the left.
*X - Side axis. Negative values move the target to the right and positive values move the target to the left.
*Y - Height axis. Negative values move the target downwards, but cannot make the target go below the floor. Positive values make the target go upwards if the value is too big, the target will teleport upward and then immediately start falling, interrupting the target animation.
*Y - Height axis. Negative values move the target downwards, but cannot make the target go below the floor. Positive values make the target go upwards; this will lift the target's collision sphere off the ground as if they jumped. However if the value is too big, the target will teleport upward and then immediately start falling, interrupting the target animation.
*Z - Forward axis. Negative values move the target backwards and positive values move the target forward.
*Z - Forward axis. Negative values move the target backwards and positive values move the target forward.
|-
|-
Line 539: Line 600:
| <Distance>
| <Distance>
| float
| float
| Activation distance. The throw can be triggered if it is within this range. However the value must be greater than the equivalent TRAM in the parent TRAC.
| Activation distance. The throw can be triggered if it is within this range. However the value must be greater than the equivalent TRAM in the parent TRAC or else the equivalent TRAM will play when the throw takes place outside of this TRAM's range and within that TRAM's range.
|-
|-
|valign="top"| <TargetType>
|valign="top"| <TargetType>
Line 600: Line 661:
|valign="top"| <Attack>
|valign="top"| <Attack>
|valign="top"| parent tag
|valign="top"| parent tag
| Only 2 attack parts per file are allowed. Normally the target gets only one hit. But if the attack frame ranges of both attack parts are overlapping, then the target can be [http://oni.bungie.org/forum/viewtopic.php?pid=39787#p39787 hit by both of them].
| Only 2 attack parts per file are allowed. Normally the target gets only one hit. But if the attack frame ranges of both attack parts are <span style="font-weight: bold; color: red;">overlapping</span>, then the target can be hit by both of them.
 
Total frames: ==============================
Attack 1:    ---#########<span style="font-weight: bold; color: red;">#####</span>-------------
Attack 2:    ------------<span style="font-weight: bold; color: red;">#####</span>#########----
 
|-
|-
| <Start>
| <Start>
Line 620: Line 686:
: Low - Target of attack needs to crouch in order to block this attack.
: Low - Target of attack needs to crouch in order to block this attack.
: High - Blocker needs to stand; if both Low and High are set, blocker can block from both standing and crouching positions.
: High - Blocker needs to stand; if both Low and High are set, blocker can block from both standing and crouching positions.
: HalfDamage - Blocker receives half of the normal damage.
: HalfDamage - Blocker still receives half of the normal damage.
|-
|-
| <Knockback>
| <Knockback>
| float
| float
| Target gets knocked back by this amount.
| Target gets knocked back by this amount if attack is successful.
|-
|-
| <HitPoints>
| <HitPoints>
Line 632: Line 698:
| <HitType>
| <HitType>
| flag
| flag
| Animation type for opponent's animation when the attack isn't blocked. The flags are part of the [[XML:StNA#Animation_types|animation type list]].
| Animation type for opponent's animation when the attack isn't blocked. The flags are part of the [[XML:StNA#Animation types|animation type list]].
|-
|-
| <HitLength>
| <HitLength>
| int16
| int16
| Number of frames that the target should remain in his hit animation state when he gets hit.
| Number of frames that the target should remain in his hit animation state when he gets hit. If ≥ 20, Oni automatically upgrades the character to a stagger animation.
|-
|-
| <StunLength>
| <StunLength>
| int16
| int16
| Number of frames that the target should remain in his blocking animation state when he blocks the attack.
| Number of frames that the target should remain in his blocking animation state when he blocks the attack. If ≥ 15, Oni automatically upgrades the character to a block stun.
|-
|-
| <StaggerLength>
| <StaggerLength>
Line 652: Line 718:
| <Extent>
| <Extent>
| parent tag
| parent tag
| One tag per attack frame. Number of <Extent> tags = <Attack><End> - <Attack><Start> + 1
| One tag per attack frame. Number of <Extent> tags = <Attack><End> - <Attack><Start> + 1.
|-
|-
| <Angle>
| <Angle>
Line 716: Line 782:


===Via Simple OniSplit GUI===
===Via Simple OniSplit GUI===
There was a long-standing problem with combined ONCC/TRAM files where the textures would be missing.<!--[Iritscen: I don't understand this sentence; is it important to keep this historical note?] With an older method you couldn't export non-native TRAM which meant to the TRAM had to be registered in the TRAC the ONCC is using.-->
There was a long-standing problem with combined ONCC/TRAM files where the textures would be missing.<!--[Iritscen: I don't understand this sentence; is it important to keep this historical note?][Paradox: Geyser still didn't release a new OniSplit containing that fix, right?] With an older method you couldn't export non-native TRAM which meant to the TRAM had to be registered in the TRAC the ONCC is using.-->


'''[http://www.paradox.oni2.net/programs/Simple_OniSplit_GUI.zip Simple OniSplit GUI]''' post-edits the DAE to fix missing textures. The character-related .oni files must be all in one folder. Usually the level0_Final folder does the trick.
'''[http://www.paradox.oni2.net/programs/Simple_OniSplit_GUI.zip Simple OniSplit GUI]''' post-edits the DAE to fix missing textures. The character-related .oni files must be all in one folder. Usually the level0_Final folder does the trick.
Line 726: Line 792:
==Editing 3D data==
==Editing 3D data==
===Blender===
===Blender===
See the [[Blender]] article for a general tutorial on using the free program to create animations for Oni. It also contains code snippets for automating certain tasks, troubleshooting advice, and links to an IK rig that makes animation much easier.
See the [[Blender]] article for a general tutorial on using the free program to create animations for Oni. It also contains a Blender addon for automating certain tasks, troubleshooting advice, and links to an animation rig that makes animation much easier.


===XSI===
===XSI===
Line 752: Line 818:
===Attacks===
===Attacks===
====Extents and XML====
====Extents and XML====
'''As a regular modder you don't need to know how the attack ring and the extents get calculated.''' If you just want to create an attack animation then add an appropriate <Attacks> code block to your XML as seen in the [[#Using OniSplit to calculate extents|section below]].
'''As a regular modder you don't need to know how the attack ring and the extents get calculated.''' If you just want to create an attack animation then add an appropriate <Attacks> code block to your XML as seen under {{SectionLink||Using OniSplit to calculate extents}}.


'''How attack ring (horizontal extents) and extents get calculated'''  
'''How attack ring (horizontal extents) and extents get calculated'''  
Line 766: Line 832:
:Length, MinY and MaxY serve to create an invisible area in space which is "dangerous to be in". If an AI intersects with this area and notices it (refer to the Notice field in [[MELE]]), it will attempt to block or dodge according to its modifiers in its MELE profile.
:Length, MinY and MaxY serve to create an invisible area in space which is "dangerous to be in". If an AI intersects with this area and notices it (refer to the Notice field in [[MELE]]), it will attempt to block or dodge according to its modifiers in its MELE profile.
::The number of <Extent>s is equal to the number of attack frames: <End> minus <Start> plus one (because the start frame counts too). For example, for TRAMKONCOMkick_low1: <End>30</End> minus <Start>22</Start> plus 1 = 9 <Extent>s.
::The number of <Extent>s is equal to the number of attack frames: <End> minus <Start> plus one (because the start frame counts too). For example, for TRAMKONCOMkick_low1: <End>30</End> minus <Start>22</Start> plus 1 = 9 <Extent>s.
:These extents are pretty impossible to guess, so leave them alone until Neo comes up with extent computation (probably from attack bones and bone rotations). However, always add at least one set of component tags, because those Length, MinY and MaxY components will be taken as the longest extent, the lowest Y value, and the highest Y value. Without those, a character won't react to this attack.


*'''<AttackRing> (formerly known as <HorizontalExtents>''' stores this info:
*'''<AttackRing> (formerly known as <HorizontalExtents>''' stores this info:
Line 797: Line 861:


===Combos===
===Combos===
The type and order of player input for triggering a given animation type (such as PPK) is hardcoded, and new animation types cannot be created. To assign an animation type to a specific combo attack, you set a corresponding value in <Lookup><Type>.
The type and order of player input for triggering a given animation type (such as PPK) is hardcoded, and new animation types cannot be created. That being said, Oni has unused combo animation types, such as PKP, PKK, KPK, KPP, etc. that work perfectly fine and can be used to create new combos. To assign an animation type to a specific combo attack, you set a corresponding value in <Lookup><Type>.


Setting a value in the <Link> of '''<DirectAnimations>''' will do two things:
Setting a value in the <Link> of '''<DirectAnimations>''' will do two things:
Line 822: Line 886:


===Throws===
===Throws===
: Todo: Add here throw pair table from talk page when it is complete.
;States
The first anim state of source (src) and target (tgt) is the "primary" <FromState>. The others <FromState> entries are located in <Shortcut>.
 
;Types
Throw target animations are registered in the TRAC of the throw initiator (AKA the throw source). Target animations are forced onto the other character. Throw target (TRAM*tgt) animations can only cover up to 256 frames (0-255).
 
The game identifies which Target animation is it supposed to play through the ''ThrownX'' animation types. Each throw, together with its corresponding Target animation, uses one of the 17 available ''ThrownX'' animation types. These types are used in three tags:
 
* <TargetType> tag in the Source animation,
* <Type> and <AimingType> tags in the Target animation.
 
For example, Konoko's forward punch throw / ''KONCOMthrow_fw_p'' uses Thrown1 as its TargetType tag:
 
            <TargetType>Thrown1</TargetType>
 
And the corresponding target animation, ''KONCOMthrow_fw_p'', uses Thrown1 in the Type and AimingType tags:
 
            <Type>Thrown1</Type>
            <AimingType>Thrown1</AimingType>
 
The ''ThrownX'' anim types work like slots - if you want to add a new throw to the game, you have to assign both the Source and the Target animations the same ThrownX type in the tags listed above. While these "pairs" are organized within vanilla animations according to the Throw Table below, this is nothing more than a convention, and as a result ''ThrownX'' types can be used freely. To give an example, while all Forward Punch Throws in the game seem to use the Thrown1 type, you can swap those types with another throw and they will work just as fine.
 
The fact that the game has only 17 ''ThrownX'' types is a major limitation - this effectively means that each character can have no more than 17 throws - out of which only 4 are unused by any character in the game. This prevents modders from creating a significant number of throws despite the game allowing great flexibility in creating new throws by mixing Animation States, Animation Types and <Varient> tags.
 
{{divhide|Throw table for vanilla Oni}}
{| class="wikitable" width=100%
|-
! rowspan=2 width=240 | names
! rowspan=2 width=40 | key combo
! colspan=4 | context
! colspan=2 style="background: #FF0;" | anim type
! rowspan=2 width=400 | image
|-
<!--context-->
! width=40 | varient
! width=60 | src state
! width=60 | tgt state
! width=85 | facing setup<!--tgt position only?(or pelvis rotation?)-->
<!--facing setup tgt rotation relative to src pelvis (or tgt pelvis position?)-->
 
<!--anim type-->
! width=40 style="background: #FF0;" | src
! width=40 style="background: #FF0;" | tgt
|-
! colspan=9 | normal throws a.k.a. static throws
|- style="vertical-align:top;"
|<!--names--> static throw forward punch
: TRAMKONCOMthrow_fw_p
: TRAMKONCOMthrow_fw_p_tgt
|<!--key combo--> P + ↑
|<!--varient--> COM
|<!--src state--> '''Standing'''
 
RunStart
|<!--tgt state--> '''Standing'''
 
Crouch<br>
Stunned
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''ThrowForwardPunch'''
|<!--anim type tgt--> '''Thrown1'''
|<!--image--> [[Image:KONCOMthrow_fw_p.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> static throw forward kick
: TRAMKONCOMthrow_fw_k
: TRAMKONCOMthrow_fw_k_tgt
|<!--key combo--> K + ↑
|<!--varient--> COM
|<!--src state--> '''Standing'''
 
RunStart
|<!--tgt state--> '''Standing'''
 
Crouch<br>
Stunned
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''ThrowForwardKick'''
|<!--anim type tgt--> '''Thrown2'''
|<!--image--> [[Image:KONCOMthrow_fw_k.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> static throw backward punch
: TRAMKONCOMthrow_bk
: TRAMKONCOMthrow_bk_tgt
|<!--key combo--> P + ↑
|<!--varient--> COM
|<!--src state--> '''Standing'''
 
RunStart
|<!--tgt state--> '''Standing'''
 
Crouch<br>
WalkingLeftDown<br>
WalkingRightDown<br>
Stunned
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''ThrowBackwardPunch'''
|<!--anim type tgt--> '''Thrown3'''
|<!--image--> [[Image:KONCOMthrow_bk.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> static throw backward punch
: TRAMKONCOMthrow_bk_k
: TRAMKONCOMthrow_bk_k_tgt
|<!--key combo--> K + ↑
|<!--varient--> COM
|<!--src state--> '''Standing'''
 
RunStart
|<!--tgt state--> '''Standing'''
 
Crouch<br>
WalkingLeftDown<br>
WalkingRightDown<br>
Stunned
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''ThrowBackwardKick'''
|<!--anim type tgt--> '''Thrown4'''
|<!--image--> [[Image:KONCOMthrow_bk_k.jpg|200px]]
|-style="vertical-align:top;"
! colspan=9 | run throws
|-style="vertical-align:top;"
|<!--names--> run throw forward punch
: (forward "lariat")
: TRAMKONCOMrun_throw_fw
: TRAMKONCOMrun_throw_fw_tgt
|<!--key combo--> P + ↑(↑)
|<!--varient--> COM
|<!--src state--> '''<span style="color: silver;">None</span>'''
 
RunningLeftDown<br>
RunningRightDown
|<!--tgt state--> '''Standing'''
 
RunningLeftDown<br>
RunningRightDown<br>
Stunned<br>
WalkingLeftDown<br>
WalkingRightDown<br>
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''RunThrowForwardPunch'''
|<!--anim type tgt--> '''Thrown5'''
|<!--image--> [[Image:KONCOMrun_throw_fw.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> run throw forward kick
: TRAMKONCOMrun_thw_fw_k
: TRAMKONCOMrun_thw_fw_k_tgt
|<!--key combo--> K + ↑(↑)
|<!--varient--> COM
|<!--src state-->'''<span style="color: silver;">None</span>'''
 
RunningLeftDown<br>
RunningRightDown
|<!--tgt state-->'''Standing'''
 
RunningLeftDown<br>
RunningRightDown<br>
Stunned<br>
WalkingLeftDown<br>
WalkingRightDown
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''RunThrowForwardKick'''
|<!--anim type tgt--> '''Thrown6'''
|<!--image--> [[Image:KONCOMrun_thw_fw_k.jpg|200px]]
<!--
identical headers but
STRCOMrun_thw_fw_p (face-to-face, with righty angle) links to Thrown5
STRCOMrun_thw_fw_pl (face-to-face, with lefty angle) links to Thrown6
 
 
 
 
-->
|-style="vertical-align:top;"
|<!--names--> run throw backward punch
: (backward "lariat")
: TRAMKONCOMrun_throw_bk
: TRAMKONCOMrun_throw_bk_tgt
|<!--key combo--> P + ↑(↑)
|<!--varient--> COM
|<!--src state-->'''<span style="color: silver;">None</span>'''
 
RunningLeftDown<br>
RunningRightDown
|<!--tgt state-->'''Standing'''
 
RunningBackLeftDown<br>
RunningBackRightDown<br>
WalkingRightDown<br>
WalkingLeftDown<br>
Stunned
 
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''RunThrowBackwardPunch'''
|<!--anim type tgt--> '''Thrown7'''
|<!--image-->
|-style="vertical-align:top;"
|<!--names--> run throw backward kick
: TRAMREDCOMrun_thw_bk_k
: TRAMREDCOMrun_thw_bk_k_tgt
|<!--key combo--> K + ↑(↑)
|<!--varient--> COM
|<!--src state-->'''<span style="color: silver;">None</span>'''
 
RunningLeftDown<br>
RunningRightDown
|<!--tgt state-->'''Standing'''


Throw target animations are registered in the TRAC of the throw initiator (AKA the throw source). Target animations are forced onto the other character, removing the need to have that animation in the target's TRAC. Throw target (TRAM*tgt) animations can only cover up to 256 frames (0-255).
RunningBackLeftDown<br>
RunningBackRightDown<br>
Stunned
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''RunThrowBackwardKick'''
|<!--anim type tgt--> '''Thrown8'''
|<!--image--> [[Image:REDCOMrun_thw_bk_k.jpg|200px]]
|-style="vertical-align:top;"
! colspan=9 | catching throws a.k.a. tackle throws
|-style="vertical-align:top;"
|<!--names--> tackle throw backward kick?<br>(never used)
|<!--key combo-->
|<!--varient-->
|<!--src state-->
|<!--tgt state-->
|<!--facing setup-->
|<!--anim type src-->
|<!--anim type tgt--> Thrown9
|<!--image-->
|-style="vertical-align:top;"
|<!--names--> tackle throw backward punch
: TRAMKONCOMrun_tkl_bk_p
: TRAMKONCOMrun_tkl_bk_p_tgt
|<!--key combo--> P + ↑(↑)
|<!--varient--> COM
|<!--src state-->'''<span style="color: silver;">None</span>'''


====Forward throws====
RunningLeftDown<br>
Scenario: You load two characters into Mod Tool and rotate (+/-180°) the throw target character because you need them to stand face to face as you work on an animation. When you are done animating, the target animation would need to be reversed again. This means multiplying the velocities by -1; the rotation also needs correcting. So it looks like you need *(-1) for the x rotation and -/+180° (depending on your initial change) for the Y rotation.
RunningRightDown
|<!--tgt state-->'''<span style="color: silver;">None</span>'''
 
RunningLeftDown<br>
RunningRightDown
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''RunThrowBackwardPunch'''
|<!--anim type tgt--> '''Thrown10'''
|<!--image--> [[Image:KONCOMrun_tkl_bk_p.jpg|200px]]
|-style="vertical-align:top;"
! colspan=9 | disarm throws
|-style="vertical-align:top;"
|<!--names--> pistol disarm throw forward punch
: TRAMKONPISthrow_fw_p
: TRAMKONPISthrow_fw_p_tgt
|<!--key combo--> P + ↑
|<!--varient--> PIS
|<!--src state-->'''Standing'''
 
RunStart
|<!--tgt state-->'''Standing'''
 
Crouch<br>
Stunned
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''ThrowForwardPunch'''
|<!--anim type tgt--> '''Thrown11'''
|<!--image--> [[Image:KONPISthrow_fw_p.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> pistol disarm throw forward kick
: TRAMKONPISthrow_fw_k
: TRAMKONPISthrow_fw_k_tgt
|<!--key combo--> K + ↑
|<!--varient--> PIS
|<!--src state-->'''Standing'''
 
RunStart
|<!--tgt state-->'''Standing'''
 
Crouch<br>
Stunned
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''ThrowForwardKick'''
|<!--anim type tgt--> '''Thrown12'''
|<!--image--> [[Image:KONPISthrow_fw_k.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> pistol disarm throw backward punch
: TRAMKONPISthrow_bk
: TRAMKONPISthrow_bk_tgt
|<!--key combo--> P +
|<!--varient--> PIS
|<!--src state-->'''Standing'''
 
RunStart
|<!--tgt state-->'''Standing'''
 
Crouch<br>
WalkingLeftDown<br>
WalkingRightDown<br>
Stunned
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''ThrowBackwardPunch'''
|<!--anim type tgt--> '''Thrown13'''
|<!--image--> [[Image:KONPISthrow_bk.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> (never used)
|<!--key combo-->
|<!--varient-->
|<!--src state-->
|<!--tgt state-->
|<!--facing setup-->
|<!--anim type src-->
|<!--anim type tgt--> Thrown14
|<!--image-->
|-style="vertical-align:top;"
|<!--names--> rifle disarm throw forward punch
: TRAMKONRIFthrow_fw_p
: TRAMKONRIFthrow_fw_p_tgt
|<!--key combo--> P + ↑
|<!--varient--> RIF
|<!--src state-->'''Standing'''
 
RunStart
|<!--tgt state-->'''Standing'''
 
Crouch<br>
Stunned
|<!--facing setup--> face-to-'''face'''
|<!--anim type src--> '''ThrowForwardPunch'''
|<!--anim type tgt--> '''Thrown15'''
|<!--image--> [[Image:KONRIFthrow_fw_p.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> rifle disarm throw backward punch
: TRAMKONRIFthrow_bk_p
: TRAMKONRIFthrow_bk_p_tgt
|<!--key combo--> P + ↑
|<!--varient--> RIF
|<!--src state-->'''Standing'''
 
RunStart
|<!--tgt state-->'''Standing'''
 
Crouch<br>
Stunned
|<!--facing setup--> face-to-'''back'''
|<!--anim type src--> '''ThrowBackwardPunch'''
|<!--anim type tgt--> '''Thrown16'''
|<!--image--> [[Image:KONRIFthrow_bk_p.jpg|200px]]
|-style="vertical-align:top;"
|<!--names--> (never used)
|<!--key combo-->
|<!--varient-->
|<!--src state-->
|<!--tgt state-->
|<!--facing setup-->
|<!--anim type src-->
|<!--anim type tgt--> Thrown17
|<!--image-->
|}
{{divhide|end}}


=====BlenderOni Throw Adjust=====
There is a Blender script implemented within the BlenderOni addon for rotating and adding PositionOffset to a forward throw Target animation. It is capable of adjusting both forward and back throws. The script's old version can be accessed [[Blender/Obsolete_scripts#Script_for_adjusting_forward_throw_targets|HERE.]]


====Excel macro====
;Running disarms
[[Image:Animation_macro_v4.png|right|thumb]]
Oni offers support for running disarms (originally noticed [[OBD:BINA/OBJC/MELE/MoveList/Throw|HERE]]), which was taken advantage of by Delano's mod [http://mods.oni2.net/node/353 54000 New Combat Moves for Konoko].
For all TRAMs except overlays. [https://www.youtube.com/watch?v=vDTPYfvMf4M Demo vid here].


This Excel macro should be rewritten as .NET framework or .NET app because Excel is not freeware.
;Forward throws
: There is a problem though. '''That tool was made with XSI in mind''' for getting (animation) pair based information.
Scenario: You load two characters into Mod Tool and rotate (+/-180°) the throw target character because you need them to stand face to face as you work on an animation. When you are done animating, the target animation would need to be reversed again. This means multiplying the velocities by -1; the rotation also needs correcting. So it looks like you need *(-1) for the x rotation and -/+180° (depending on your initial change) for the Y rotation.
: As XSI is out of date research for doing the same with blender will be needed.
: There was some development for an '''TRAM setup assistant''' in 2016 but drifted into inactivity since the community showed no sign of demand for an updated tool.
: By now there is a Blender script to create throw target adjustment data. So, instead of writing a new stand-alone app, at least the creation of header data might be better ported to the Blender script.


'''Usage:'''
There is a Blender script implemented within the BlenderOni addon for rotating and adding PositionOffset to a forward throw Target animation. It is capable of adjusting both forward and back throws. The script's old version can be accessed [[Blender/Obsolete scripts#Script for adjusting forward throw targets|HERE]].
* Put your files into the "input_and_output" folder.
* Disable macro security if you don't want to have to click on the "Enable Content" button every time.
* Close other worksheets before you run the macro.
* If you are not afraid of VBA code, you can enter the dev environment by hitting Alt+F11. If you want to extend the attack library with more screenshots and settings, search for "LibraryThrows", " LibraryAttack", "Picture", and "CBAttackHelp.AddItem".


===Run animations===
===Run animations===
Line 876: Line 1,278:


==List of unused animations==
==List of unused animations==
Here are all the known unused animations. These could be recycled in a new mod.
Here are all the known unused animations, with links to videos of them where applicable. These could be recycled in a new mod.


'''From the original game'''
'''From the original game'''
* KONOKOconsole_punch: What the name says (the engine can use that animation depending on the console's configuration, see [[OBD:BINA/OBJC/CONS|CONS]]) .
* KONOKOconsole_punch: What the name says (the engine can use that animation depending on the console's configuration, see [[OBD:BINA/OBJC/CONS|CONS]]) .
* KONOKOlev3_intro: Looks like she's placing something (bomb?) and then running away.
* KONOKOlev3_intro: Looks like she's placing something (bomb?) and then running away.
* KONOKOlev4_undress: From an aborted clothes-changing cutscene.
* [https://www.youtube.com/watch?v=dszw2vmlmzw KONOKOlev4_undress]: From an aborted clothes-changing cutscene.
* KONOKOlev16_bomb: Planting a bomb.
* KONOKOlev16_bomb: Planting a bomb.
* KONCOMsuper_kick: Now used in [[OTA]] as a spawn event, and by the fan-made character [[Shinatama Evolved]].
* KONCOMsuper_kick: Now used in [[OTA]] as a spawn event, and by the fan-made character [[Shinatama Evolved]].
* KONCOMsuper_punch: KONCOMpunch_heavy but without the shouted attack name, has HalfDamage flag, and does 10 less damage in its first attack part.
* KONCOMsuper_punch: KONCOMpunch_heavy but without the shouted attack name, has HalfDamage flag, and does 10 less damage in its first attack part.
* COMPISidle_special1: Comguy checking environment and his gun; meant for a feature that would visually depict an elevation in the AI's level of alertness after, say, hearing a noise.
* [https://www.youtube.com/watch?v=CRuZq_uYOQk COMPISidle_special1]: Comguy checking environment and his gun; meant for a feature that would visually depict an elevation in the AI's level of alertness after, say, hearing a noise.
* STRPISidle_special1: Striker checking his his gun and communicating with an ally (another unused alertness-elevation animation).
* STRPISidle_special1 (see above link): Striker checking his his gun and communicating with an ally (another unused alertness-elevation animation).
* THUGlev1_direct: Thug probably directing a truck driver.
* THUGlev1_direct: Thug probably directing a truck driver.


'''From modders'''
'''From modders'''


[[Image:female_stun.jpg|right|thumb|Char A and Char B performing stun animations.]]
[[Image:Female stun.jpg|right|thumb|Char A and Char B performing stun animations.]]


* http://mods.oni2.net/node/376
* http://mods.oni2.net/node/376
Line 955: Line 1,357:
===Motion blur===
===Motion blur===
[[Image:XML_TRAM_willow_kick_with_motion_blur.jpg|thumb|200px]]
[[Image:XML_TRAM_willow_kick_with_motion_blur.jpg|thumb|200px]]
Motion blur in Oni works much like ghosting/onion skinning features in any animation software: In the frame range specified by <Start> and <End> an additional instance of body parts specified in <Bones> are rendered for additional extra frames specified by <Lifetime>, with transparency set by <Alpha>, and every <Interval> frames within the frame range.
Example snippet from KONCOMcomb_p_p_k:
        <MotionBlur>
            <MotionBlur>
                <Bones>RightThigh RightCalf RightFoot</Bones>
                <Start>20</Start>
                <End>28</End>
                <Lifetime>4</Lifetime>
                <Alpha>127</Alpha>
                <Interval>1</Interval>
            </MotionBlur>
        </MotionBlur>
The one noteworthy thing is that you can have multiple motion blur sequences. If you wanted to add an extra motion blur to the above animation, you could do something like this:
        <MotionBlur>
            <MotionBlur>
                <Bones>LeftThigh LeftCalf LeftFoot</Bones>
                <Start>10</Start>
                <End>18</End>
                <Lifetime>4</Lifetime>
                <Alpha>127</Alpha>
                <Interval>1</Interval>
            </MotionBlur>
            <MotionBlur>
                <Bones>RightThigh RightCalf RightFoot</Bones>
                <Start>20</Start>
                <End>28</End>
                <Lifetime>4</Lifetime>
                <Alpha>127</Alpha>
                <Interval>1</Interval>
            </MotionBlur>
        </MotionBlur>


===Impact effect===
===Impact effect===
Impact effects – mostly particle – are chosen based on logic written within [[XML:BINA/ONIE#Visual guides: weapon, melee, environment|ONIE]]. Furthermore particles must be registered in [[XML:ONCC|ONCC]]: You probably want to look at ONIA instead of ONCP.
[[Image:XML_TRAM_KONCOMkick_fw_with_ninflash1.jpg|thumb|200px]]
[[Image:XML_TRAM_KONCOMkick_fw_with_ninflash1.jpg|thumb|200px]]
See also [[XML:ONCC]].


{{XML}}
{{XML}}
8,288

edits