1# DataHelper contains subroutines useful for loading a character's 2# frames, and creating his states. 3 4use strict; 5 6 7require 'FighterStats.pl'; 8 9 10 11=comment 12 13 14SITUATIONS ARE: 15 16Ready, Stand, Crouch, (Midair = any + character is flying), Falling 17 18 19 20SITUATION DEPENDENT EVENTS ARE: 21 22Highhit, Uppercut, Hit, Groinhit, Leghit, Fall 23 24 25 26STANDBY EVENTS ARE: 27 28Won, Hurt, Threat, Fun, Turn 29 30 31 32 33________|___Ready___________Block___Stand___________Crouch______________Midair______Falling 34Highhit | HighPunched - HighPunched KneelingPunched (...) (...) 35Uppercut| Falling - Falling KneelingPunched (...) (...) 36Hit | LowPunched - LowPunched KneelingKicked 37Groinhit| GroinKicked - GroinKicked KneelingKicked 38Leghit | Swept - Swept KneelingKicked 39Fall | Falling - Falling KneelingPunched 40 41 42 43FRAME MEMBER DESCRIPTION IS: 44 45x int X coordinate offset of the image relative to the character's anchor. 46y int Y coordinate offset of the image relative to the character's anchor. 47w int The width of the image. 48h int The height of the image. 49head array The coordinates of a polygon marking the head within the image, relative to the anchor. 50body array The coordinates of a polygon marking the body within the image, relative to the anchor. 51legs array The coordinates of a polygon marking the legs within the image, relative to the anchor. 52hit array The coordinates of a polygon marking the hit within the image, relative to the anchor. 53 54 55STATE MEMBER DESCRIPTION IS: 56 57F int The number of the visible frame. 58SITU string The situation associated with this state (Ready, Stand, Crouch, Falling) 59DEL int The delay before moving to the next state. 60NEXTST string The name of the state which follows this state, if none of the CONs is used. 61CON hash Connections from this state. The keys are either events or keyboard input. 62HIT ? The hit delivered at the beginning of this state. 63BLOCK int If true, the character is blocking in his current state. 64MOVE int The character's anchor should continously move this much during this state. 65DELTAX int The character's anchor should immediately change by this much after this state. 66PUSHX int The character is pushed, with this much momentum upon entering this state. 67TURN int If true, the character's facing direction changes after this state. 68JUMP int The character leaps into the air, with this much initial momentum upon entering this state. 69DOODAD string A doodad is created at the beginning of this state. The string contains the doodad's type and speed. 70SOUND string The sound effect associated with this state (if any); 71HITSND string The sound effect if the HIT is successful. 72MISSSND string The sound effect if the HIT fails. 73CODE string This code will be evaled at the beginning of this state. 74LAYER int The "priority" of the graphics. 5: hurt; 10: block; 15: kneeling; 20: normal; 25: attack 75 76 77=cut 78 79 80 81 82# Loads the frame data (x, y, w, h) from the given datafile. 83# The path to the datafile is inserted automatically. The frame data will 84# be shifted by (-PivotX,-PivotY). 85# 86# Returns an array of frame data. The first element in the array is 87# a dummy entry, the second is the first real entry. This is because 88# the first thing in the datafile is a PAL entry. 89# 90# Example: LoadFrames( "ZOLIDATA.DAT.txt" ); 91sub LoadFrames ($$$) 92{ 93 my ($DataName, $PivotX, $PivotY) = @_; 94 my (@Frames, $data, $frame, $DatName); 95 96 # Make sure that Whatever.dat also exists. 97 $DatName = $DataName; 98 $DatName =~ s/\.txt$//; 99 open DATFILE, "../characters/$DatName" || die ("Couldn't open ../characters/$DatName"); 100 close DATFILE; 101 102 open DATAFILE, "../characters/$DataName" || die ("Couldn't open ../characters/$DataName"); 103 $data = ''; 104 while ( read DATAFILE, $data, 16384, length($data) ) 105 { 106 } 107 close DATAFILE; 108 109 print "$DataName file is ", length($data), " bytes long.\n"; 110 111 # Insert a dummy first row for the palette entry 112 eval ("\@Frames = ( {}, $data);"); 113 die $@ if $@; 114 115 foreach $frame (@Frames) 116 { 117 OffsetFrame( $frame, -$PivotX, -$PivotY ); 118 } 119 print "$DataName loaded, ", scalar @Frames, " frames.\n"; 120 121 return @Frames; 122} 123 124 125# Creates a frame lookup from a descriptor array. 126# The first parameter is the expected number of frames. 127# The descriptor array should have frame base names in even positions, 128# and lengths at odd. positions. For example: 129# ("start", 6, "stand", 4, ... ) 130# 131# The routine will return a lookup which will contain the frame's logical 132# name as a key, and its physical index as a value. The logical names are 133# simply the basename plus a number. The example above would return: 134# ("start1"=>1, "start2"=>2, ..., "start6"=>6, "stand1"=>6, "stand2"=>7, ...) 135sub CreateFrameLookup 136{ 137 my ($ExpectedCount, @FrameDesc) = @_; 138 139 my ($FrameName, $NumFrames); 140 my ($i, $j); 141 my (%FrameLookup); 142 143 for ( $i=0; $i<scalar @FrameDesc; $i +=2 ) 144 { 145 $FrameName = $FrameDesc[$i]; 146 $NumFrames = $FrameDesc[$i+1]; 147 148 for ( $j = 1; $j<=$NumFrames; ++$j ) 149 { 150 # print "Frame ", (scalar keys %FrameLookup) + 1, " is now called $FrameName$j\n"; 151 print "Name redefinition: $FrameName!\n" if defined $FrameLookup{ "$FrameName$j" }; 152 $FrameLookup{ "$FrameName$j" } = (scalar keys %FrameLookup) + 1; 153 } 154 } 155 156 if ( $ExpectedCount != scalar keys( %FrameLookup ) ) 157 { 158 die( "Expected number of frames ($ExpectedCount) doesn't equal the actual number of frames: ". 159 scalar keys(%FrameLookup) ); 160 } 161 162 return %FrameLookup; 163} 164 165 166 167# Helper function. Finds the last frame with a given name in a frame 168# lookup structure. Return the index of the last frame (1-based), or 169# 0 if none with the given name were found. 170# 171# Example: If there are 6 "highpunch" frames (highpunch1 to highpunch6), 172# FindLastFrame(\%FrameLookup, "highpunch") returns 6. 173sub FindLastFrame($$) { 174 my ($FrameLookup, $FrameName) = @_; 175 my ($i) = (1); 176 while ( exists ${$FrameLookup}{"$FrameName$i"} ) { $i++; } 177 return $i-1; 178} 179 180 181 182sub OffsetPolygon($$$) 183{ 184 my ($poly, $dx, $dy) = @_; 185 my ($i, $n); 186 187 $n = scalar @{$poly}; 188 189 for ( $i=0; $i < $n; $i+=2 ) 190 { 191 $poly->[$i] += $dx; 192 $poly->[$i+1] += $dy; 193 } 194} 195 196 197 198sub MirrorPolygon($) 199{ 200 my ($poly) = @_; 201 my ($i, $n); 202 203 $n = scalar @{$poly}; 204 205 for ( $i=0; $i < $n; $i+=2 ) 206 { 207 $poly->[$i] = - $poly->[$i]; 208 } 209} 210 211 212sub GetPolygonCenter($) 213{ 214 my ($poly) = @_; 215 216 my ($i, $n, $x, $y); 217 218 $n = scalar @{$poly}; 219 $x = $y = 0; 220 for ( $i=0; $i < $n; $i+=2 ) 221 { 222 $x += $poly->[$i]; 223 $y += $poly->[$i+1]; 224 } 225 226 return ( $x*2/$n, $y*2/$n ); 227} 228 229 230 231sub OffsetFrame($$$) { 232 my ($frame, $dx, $dy) = @_; 233 234 $frame->{'x'} += $dx; 235 $frame->{'y'} += $dy; 236 237 OffsetPolygon( $frame->{head}, $dx, $dy ) if defined ($frame->{head}); 238 OffsetPolygon( $frame->{body}, $dx, $dy ) if defined ($frame->{body}); 239 OffsetPolygon( $frame->{legs}, $dx, $dy ) if defined ($frame->{legs}); 240 OffsetPolygon( $frame->{hit}, $dx, $dy ) if defined ($frame->{hit}); 241} 242 243 244 245# FindLastState returns the last index of a given state. 246# For example, if Punch4 is the last in Punch, FindLastState("Punch") is 4. 247sub FindLastState($$) { 248 my ( $States, $StateName ) = @_; 249 my ( $i ) = ( 1 ); 250 251 while ( exists ${$States}{ "$StateName $i" } ) { $i++; } 252 return $i-1; 253} 254 255 256# Translates an abbreviated sequence to a full sequence. 257# "-punch" is every punch frame backwards. 258# "_punch" is every punch frame except the last one backwards. 259# "+punch" is every punch frame forwards. 260sub TranslateSequence($$) { 261 my ($FrameLookup, $Sequence) = @_; 262 263 my ($pre, $frame) = $Sequence =~ /^([+-_]{0,1})(\w+)/; 264 my ($LastFrame) = (FindLastFrame( $FrameLookup, $frame ) ); 265 #$LastFrame = (FindLastFrame( $FrameLookup, "$pre$frame" ) ) if $LastFrame == 0; 266 #print "Last frame of $frame is $LastFrame.\n"; 267 268 return "$frame 1-$LastFrame" if ( $pre eq '+' ); 269 return "$frame $LastFrame-1" if ( $pre eq '-' ); 270 return "$frame " . ($LastFrame-1) . "-1" if ( $pre eq '_' ); 271 272 $Sequence =~ s/\sn(-{0,1})/ $LastFrame$1/; # Replace n- with last frame 273 $Sequence =~ s/-n/-$LastFrame/; # Replace -n with last frame 274 return $Sequence; 275} 276 277 278 279sub SetStateData($$$) 280{ 281 my ($state, $FrameDesc, $suffix) = @_; 282 283 $state->{DEL} = $FrameDesc->{"DEL$suffix"} if defined $FrameDesc->{"DEL$suffix"}; 284 $state->{HIT} = $FrameDesc->{"HIT$suffix"} if defined $FrameDesc->{"HIT$suffix"}; 285 $state->{CON} = $FrameDesc->{"CON$suffix"} if defined $FrameDesc->{"CON$suffix"}; 286 $state->{BLOCK} = $FrameDesc->{"BLOCK$suffix"} if defined $FrameDesc->{"BLOCK$suffix"}; 287 $state->{NEXTST} = $FrameDesc->{"NEXTST$suffix"} if defined $FrameDesc->{"NEXTST$suffix"}; 288 $state->{MOVE} = $FrameDesc->{"MOVE$suffix"} if defined $FrameDesc->{"MOVE$suffix"}; 289 $state->{DELTAX} = $FrameDesc->{"DELTAX$suffix"} if defined $FrameDesc->{"DELTAX$suffix"}; 290 $state->{PUSHX} = $FrameDesc->{"PUSHX$suffix"} if defined $FrameDesc->{"PUSHX$suffix"}; 291 $state->{TURN} = $FrameDesc->{"TURN$suffix"} if defined $FrameDesc->{"TURN$suffix"}; 292 $state->{JUMP} = $FrameDesc->{"JUMP$suffix"} if defined $FrameDesc->{"JUMP$suffix"}; 293 $state->{SITU} = $FrameDesc->{"SITU$suffix"} if defined $FrameDesc->{"SITU$suffix"}; 294 $state->{DOODAD} = $FrameDesc->{"DOODAD$suffix"} if defined $FrameDesc->{"DOODAD$suffix"}; 295 $state->{SOUND} = $FrameDesc->{"SOUND$suffix"} if defined $FrameDesc->{"SOUND$suffix"}; 296 $state->{CODE} = $FrameDesc->{"CODE$suffix"} if defined $FrameDesc->{"CODE$suffix"}; 297} 298 299 300 301# Adds a sequence to the end of a state 302# Sequences are: e.g. "throw 10-14, throw 16, throw 14-10" 303# Each piece of the sequence will have $Delay delay. 304sub AddStates($$$) { 305 my ( $States, $Frames, $FrameDesc ) = @_; 306 my ( $StateName, $SequenceString, $LastState, $i, $sloop, $s, @Sequences ); 307 my ( $from, $to, $frame, $state ); 308 309 $StateName = $FrameDesc->{'N'}; 310 $SequenceString = $FrameDesc->{'S'}; 311 $FrameDesc->{SITU} = 'Stand' unless defined $FrameDesc->{SITU}; 312 $LastState = FindLastState($States,$StateName)+1; 313 314 @Sequences = split ( /\s*,\s*/, $SequenceString ); 315 for ( $sloop = 0; $sloop < scalar @Sequences; ++$sloop ) 316 { 317 $s = TranslateSequence( $Frames, $Sequences[$sloop] ); 318 #print "Sequence is $s\n"; 319 320 if ( $s =~ /^\s*(\w+)\s+(\d+)-(\d+)\s*$/ ) 321 { 322 # Sequence is '<frame> <from>-<to>' 323 $frame = $1; 324 $from = $2; 325 $to = $3; 326 } 327 elsif ( $s =~ /^\s*(\w+)\s+(\d+)\s*$/ ) 328 { 329 # Sequence is '<frame> <number>' 330 $frame = $1; 331 $from = $to = $2; 332 } 333 else 334 { 335 die "Sequence '$s' incorrect.\n"; 336 } 337 338 $i = $from; 339 while (1) 340 { 341 die "Error: Frame $frame$i doesn't exist.\n" 342 unless defined ${$Frames}{"$frame$i"}; 343 344 $state = { 'F'=>"$frame$i" }; 345 SetStateData( $state, $FrameDesc, '' ); 346 SetStateData( $state, $FrameDesc, $LastState ); 347 if ( ( $sloop == scalar @Sequences -1 ) and ( $i == $to ) ) 348 { 349 SetStateData( $state, $FrameDesc, 'N' ); 350 } 351 352 $States->{"$StateName $LastState"} = $state; 353 # print "Added state '$StateName $LastState' as frame '$frame$i', delay $Delay\n"; 354 355 $LastState++; 356 if ( $from < $to ) 357 { 358 $i++; 359 last if $i > $to; 360 } 361 else 362 { 363 $i--; 364 last if $i < $to; 365 } 366 } 367 368 } 369} 370 371 372sub BlockStates($$) 373{ 374 my ( $frames, $del) = @_; 375 my ( $retval, $i ); 376 377 # We need to make sure that blocking is the same speed for every character. 378 # Typical is 5 frames, +- 1 frame 379 $del = int( 25 / $frames ); # 1/1 380 381 $retval = { 'N'=>'Block', 'DEL'=>$del, 'S'=>'+block', }; 382 for ($i = 1; $i <= $frames; ++$i ) 383 { 384 $retval->{"NEXTST$i"} = "Block " . ($i-1); 385 $retval->{"CON$i"} = { 'block'=> "Block " . ($i+1) }; 386 $retval->{"BLOCK$i"} = 1 if $i*$del > 10; 387 } 388 389 $retval->{'NEXTST1'} = 'Stand'; 390 $retval->{"CON$frames"} = { 'block'=> "Block " . $frames }; 391 392 return $retval; 393} 394 395 396sub KneelingStates($$$$) 397{ 398 my ( $frames, $frames2, $del, $con ) = @_; 399 my ( $retval, $retval2, $i, $j ); 400 401 $retval = { 'N'=>'Kneeling', 'DEL'=> $del, 'S' => '+kneeling', 'SITU'=>'Crouch' }; 402 for ( $i = 1; $i <= $frames; ++$i ) 403 { 404 $retval->{"NEXTST$i"} = "Kneeling " . ($i-1); 405 $retval->{"CON$i"} = { 'down' => "Kneeling " . ($i+1) }; 406 } 407 $retval->{'NEXTST1'} = 'Stand'; 408 $retval->{"CON$frames"} = { 'down' => "Onknees" }; 409 410 $retval2 = { 'N'=>'Onknees', 'DEL'=>$del, 'S' => '+onknees,-onknees', 'SITU'=>'Crouch', 411 'NEXTST' => "Kneeling $frames" }; 412 $frames2 *= 2; 413 for ( $i = 1; $i <= $frames2; ++$i ) 414 { 415 $j = ($i % $frames2) + 1; 416 $retval2->{"CON$i"} = { %{$con}, 'down'=>"Onknees $j", 'forw'=>"Onknees $j", 'back'=>"Onknees $j" }; 417 } 418 419 return ($retval, $retval2); 420} 421 422 423 424=comment 425JumpStates is for generating the Jump, JumpFW, JumpBW, JumpFly, 426JumpStart, JumpKick, JumpPunch states for a state description list. 427 428Parameters: 429$frames hash The frame lookup hash. 430$con hash Connections during jumping (usually, JumpKick and JumpPunch only) 431$framenames hash [optional] If the standard frame names (kneeling, 432 onknees, kneelingkick, kneelingpunch) are not good, this has should 433 contain replacement names (e.g. 'kneelingkick' => 'sweep') 434=cut 435 436sub JumpStates 437{ 438 my ( $frames, $con, $framenames ) = @_; 439 my ( $kneelingframes, $onkneesframes, 440 $kickframes, $punchframes ) = ( 441 FindLastFrame( $frames, 'kneeling' ), 442 FindLastFrame( $frames, 'onknees' ), 443 FindLastFrame( $frames, 'kneelingkick' ), 444 FindLastFrame( $frames, 'kneelingpunch' ) ); 445 my ( $jumpheight ) = 120; 446 447 my ( $i, $j, $statestotal, $statesdown, $statesknees, $deldown, 448 $jump, $jumpfw, $jumpbw, $flying, $flyingsequence, $flyingstart, $jumpkick, $jumppunch ); 449 450 # The jump's first part is going down on knees, second part is 451 # on knees, third part is getting up. 452 453 if ( $::DELMULTIPLIER ) 454 { 455 $statestotal = $jumpheight * 2 / 3 / $::DELMULTIPLIER; # 1/1 456 } 457 else 458 { 459 $statestotal = $jumpheight * 2 / 3; 460 } 461 $statesdown = $statestotal / 4; 462 463 $deldown = int($statesdown / $kneelingframes + 0.1); # 1/1 464 $statesdown = $deldown * $kneelingframes; 465 $statesknees = $statestotal - $statesdown * 2; 466 467 $jump = { 'N'=>'Jump', 'DEL'=> $deldown, 'S'=>'kneeling 1-2, kneeling 1', 468 'JUMPN'=>$jumpheight, NEXTSTN=>'JumpFly', 'SOUND1'=>'PLAYER_JUMPS', }; 469 $jumpfw = { %{$jump}, 'N'=>'JumpFW', 'PUSHX3'=>18*16 }; 470 $jumpbw = { %{$jump}, 'N'=>'JumpBW', 'PUSHX3'=>-9*16 }; 471 472 $flyingsequence = ''; 473 $flying = {}; 474 for ( $i = 0; $i < $statesknees / $deldown; ++$i ) #1/1 475 { 476 $j = $i + $statesdown / $deldown; #1/1 477 $flyingsequence .= 'onknees 1,'; 478 $flying->{"CON$j"} = $con; 479 # $flying->{"DEL$j"} = 1; 480 } 481 $flyingsequence = "+kneeling, $flyingsequence -kneeling"; 482 483 $flying = { %{$flying}, 'N'=>'JumpFly', 'DEL'=> $deldown, 'S'=>$flyingsequence, 484 'DELN'=>100 }; 485 $flyingstart = { 'N'=>'JumpStart', 'JUMP2'=>$jumpheight, 'PUSHX2'=>9*16, 'DEL1'=>1, 486 'DEL'=> $deldown, 'S'=>"stand 1,$flyingsequence", 'DELN'=>100 }; 487 488 print join( ',', %{$flying}), "\n"; 489 490 $jumpkick = { 'N'=>'JumpKick', 'HIT'=>'Fall', 491 'DEL'=> int( $statestotal * 2 / 3 / ( $kickframes + $kneelingframes*2 + 3 ) ), # 1/1 492 'S'=> '+kneelingkick,kneelingkick n, kneelingkick n, kneelingkick n,-kneelingkick,-kneeling', 493 'HIT'=>'Fall', 'DELN'=>100 }; 494 495 $jumppunch = { 'N'=>'JumpPunch', 'HIT'=>'Highhit', 496 'DEL'=> int( $statestotal * 2 / 3 / ( $punchframes + $kneelingframes*2 + 3 ) ), # 1/1 497 'S'=> '+kneelingpunch,kneelingpunch n, kneelingpunch n, kneelingpunch n,-kneelingpunch,-kneeling', 498 'HIT'=>'Fall', 'DELN'=>100 }; 499 500 return ($jump, $jumpfw, $jumpbw, $flying, $flyingstart, $jumpkick, $jumppunch); 501} 502 503 504 505sub WalkingFrames($$$$$) 506{ 507 my ( $frameLookup, $frameArray, $preFrames, $distance, $con ) = @_; 508 509 my ( $walkFrames, $totalFrames, $seq, $seq2, $distPerFrame, 510 $walk, $back, $walkNextst, $backNextst, 511 $i, $j, ); 512 513 $totalFrames = FindLastFrame( $frameLookup, 'walk' ); 514 $walkFrames = $totalFrames - $preFrames; 515 516 if ( $preFrames > 0 ) { 517 $seq = "+walk, walk $preFrames-1"; 518 $seq2 = "walk 1-$preFrames, -walk"; 519 } else { 520 $seq = "+walk"; 521 $seq2 = "-walk"; 522 } 523 524 $walk = { 'N'=>'Walk', 'S'=>$seq, 'DEL'=>5, 'CON'=>$con }; 525 $back = { 'N'=>'Back', 'S'=>$seq2, 'DEL'=>5, }; 526 527 # Add attributes for the 'pre' states. 528 529 for ( $i=1; $i <= $preFrames; ++$i ) 530 { 531 $j = $i + 1; 532 $walk->{"CON$i"} = { %{$con}, 'forw' => "Walk $j" }; 533 $walk->{"NEXTST$i"} = 'Stand'; 534 $back->{"CON$i"} = { %{$con}, 'back' => "Back $j" }; 535 $back->{"NEXTST$i"} = 'Stand'; 536 } 537 538 # Add attributes for the 'walk' states. 539 540 $walkNextst = $preFrames ? 'Walk ' . ($totalFrames+1) : 'Stand'; 541 $backNextst = $preFrames ? 'Back ' . ($totalFrames+1) : 'Stand'; 542 $distPerFrame = $distance / $walkFrames; # 1/1 543 544 print "*** $preFrames $walkFrames $totalFrames $walkNextst $backNextst\n"; 545 546 for ( $i=$preFrames+1; $i <= $totalFrames; ++$i ) 547 { 548 $j = ($i == $totalFrames) ? $preFrames+1 : $i+1; 549 $walk->{"MOVE$i"} = 4; 550 $walk->{"NEXTST$i"} = $walkNextst; 551 $walk->{"CON$i"} = { %{$con}, 'forw' => "Walk $j" }; 552 $back->{"MOVE$i"} = -4; 553 $back->{"NEXTST$i"} = $backNextst; 554 $back->{"CON$i"} = { %{$con}, 'back' => "Back $j" }; 555 556 OffsetFrame( $frameArray->[$frameLookup->{"walk$i"}], 557 - ($i-$preFrames-1) * $distPerFrame, 0 ); 558 } 559 560 return ( $walk, $back ); 561} 562 563 564 565sub TravelingStates( $$$$$$ ) 566{ 567 my ( $frameLookup, $frameArray, $states, $frameName, $from, $to ) = @_; 568 569 $from = 1 unless $from; 570 unless ( $to ) 571 { 572 $to = FindLastFrame( $frameLookup, $frameName ); 573 $to += 1 if $frameName eq 'falling'; 574 } 575 576 my ( $fromIndex, $toIndex, $fromFrame, $toFrame, $fromOffset, $toOffset, 577 $deltax, $i, $state, $nextst ); 578 579 # 1. Calculate the 'deltax' and 'fromOffset'. 580 581 $fromIndex = $frameLookup->{"$frameName$from"}; 582 die "couldn't find frame $frameName$from" unless defined $fromIndex; 583 $toIndex = $fromIndex - $from + $to; 584 585 $fromFrame = $frameArray-> [ $fromIndex ]; 586 $toFrame = $frameArray-> [ $toIndex ]; 587 588 $fromOffset = $fromFrame->{x} + ($fromFrame->{w} >> 1); 589 $toOffset = $toFrame->{x} + ($toFrame->{w} >> 1); 590 $deltax = ( $toOffset - $fromOffset ) / ( $to - $from ); #1/1 591 592 # print "Offsets: $fromOffset $toOffset $deltax\n"; 593 594 # 2. Offset every relevant frame. 595 for ( $i=$fromIndex; $i<=$toIndex; ++$i ) 596 { 597 # print "Offsetting frame $i by ", - $fromOffset - $deltax * ($i-$fromIndex), "\n"; 598 OffsetFrame( $frameArray->[$i], 599 - $fromOffset - $deltax * ($i-$fromIndex), 0 ); 600 } 601 602 # 3. Apply deltax to every relevant state. 603 while ( ($i, $state) = each %{$states} ) 604 { 605 if ( $state->{F} >= $fromIndex and $state->{F} <= $toIndex ) 606 { 607 $nextst = $states->{$state->{NEXTST}}; 608 if ( defined($nextst) and $nextst->{F} >= $fromIndex and $nextst->{F} <= $toIndex ) 609 { 610 $state->{DELTAX} = $deltax * ($nextst->{F} - $state->{F}); 611 # print "Fixing state $i : deltax = ", $state->{DELTAX}, "\n"; 612 } 613 } 614 } 615} 616 617 618 619sub FixStates($$) 620{ 621 my ( $frameLookup, $states ) = @_; 622 623 my ( $framename, $st, $lastchar, $key, $value, $nextchar, $nextst ); 624 625 while (($key, $value) = each %{$states}) 626 { 627 $framename = $value->{'F'}; 628 unless ( $framename =~/^\d+$/ ) 629 { 630 # Convert non-numeric frames to their numeric counterparts. 631 die "Can't find image $framename in frame $key" unless defined $frameLookup->{ $framename }; 632 $value->{'F'} = $frameLookup->{ $framename }; 633 } 634 635 ($st,$lastchar) = $key =~ /(\w+)\s+(\d+)/; 636 637 unless ( defined $value->{'NEXTST'} ) 638 { 639 $nextchar = $lastchar + 1; 640 $nextst = "$st $nextchar"; 641 unless ( defined $states->{$nextst} ) { 642 # print "Go to Standby after $key\n"; 643 $nextst = 'Stand'; 644 } 645 $value->{'NEXTST'} = $nextst; 646 } 647 648 } 649} 650 651 652sub FindShorthands($) 653{ 654 my ( $states ) = @_; 655 656 my ( $key, $value, $st, $lastchar, %Shorthands ); 657 658 while (($key, $value) = each %{$states}) 659 { 660 ($st,$lastchar) = $key =~ /(\w+)\s+(\d+)/; 661 print "$key has no lastchar" unless defined $lastchar; 662 if ( $lastchar == 1 ) 663 { 664 $Shorthands{$st} = $states->{$key}; 665 } 666 } 667 668 return %Shorthands; 669} 670 671 672sub CheckStates($$) 673{ 674 my ( $fightername, $states ) = @_; 675 my ( $key,$state, $con ); 676 my ( $seq,$nextst ); 677 678 while (($key, $state) = each %{$states}) 679 { 680 die "Bad connection in fighter $fightername to '$state->{NEXTST} from $key!'" unless exists $states->{ $state->{NEXTST} }; 681 next unless $state->{CON}; 682 $con = $state->{CON}; 683 684 while (($seq, $nextst) = each %{$con}) 685 { 686 die "Bad connection in fighter $fightername to '$nextst' from $key!" unless exists $states->{$nextst}; 687 } 688 } 689} 690 691 692 693 694 695return 1; 696