int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) { // not moving, but animating // idleleft is <0 while idle view is playing (.animating is 0) if (((animating != 0) || (idleleft < 0)) && ((walking == 0) || ((flags & CHF_MOVENOTWALK) != 0)) && (room == displayed_room)) { const bool is_voice = channels[SCHAN_SPEECH] != NULL; doing_nothing = 0; // idle anim doesn't count as doing something if (idleleft < 0) doing_nothing = 1; if (wait>0) wait--; else if ((char_speaking == aa) && (game.options[OPT_LIPSYNCTEXT] != 0)) { // currently talking with lip-sync speech int fraa = frame; wait = update_lip_sync (view, loop, &fraa) - 1; // closed mouth at end of sentence // NOTE: standard lip-sync is synchronized with text timer, not voice file if (play.speech_in_post_state || (play.messagetime >= 0) && (play.messagetime < play.close_mouth_speech_time)) frame = 0; if (frame != fraa) { frame = fraa; CheckViewFrameForCharacter(this); } //continue; return RETURN_CONTINUE; } else { int oldframe = frame; if (animating & CHANIM_BACKWARDS) { frame--; if (frame < 0) { // if the previous loop is a Run Next Loop one, go back to it if ((loop > 0) && (views[view].loops[loop - 1].RunNextLoop())) { loop --; frame = views[view].loops[loop].numFrames - 1; } else if (animating & CHANIM_REPEAT) { frame = views[view].loops[loop].numFrames - 1; while (views[view].loops[loop].RunNextLoop()) { loop++; frame = views[view].loops[loop].numFrames - 1; } } else { frame++; animating = 0; } } } else frame++; if ((aa == char_speaking) && (play.speech_in_post_state || (!is_voice) && (play.close_mouth_speech_time > 0) && (play.messagetime < play.close_mouth_speech_time))) { // finished talking - stop animation animating = 0; frame = 0; } if (frame >= views[view].loops[loop].numFrames) { if (views[view].loops[loop].RunNextLoop()) { if (loop+1 >= views[view].numLoops) quit("!Animating character tried to overrun last loop in view"); loop++; frame=0; } else if ((animating & CHANIM_REPEAT)==0) { animating=0; frame--; // end of idle anim if (idleleft < 0) { // constant anim, reset (need this cos animating==0) if (idletime == 0) frame = 0; // one-off anim, stop else { ReleaseCharacterView(aa); idleleft=idletime; } } } else { frame=0; // if it's a multi-loop animation, go back to start if (play.no_multiloop_repeat == 0) { while ((loop > 0) && (views[view].loops[loop - 1].RunNextLoop())) loop--; } } } wait = views[view].loops[loop].frames[frame].speed; // idle anim doesn't have speed stored cos animating==0 if (idleleft < 0) wait += animspeed+5; else wait += (animating >> 8) & 0x00ff; if (frame != oldframe) CheckViewFrameForCharacter(this); } } return 0; }
// the 'cmdsrun' parameter counts how many commands are run. // if a 'Inv Item Was Used' check does not pass, it doesn't count // so cmdsrun remains 0 if no inventory items matched int run_interaction_commandlist (InteractionCommandList *nicl, int *timesrun, int*cmdsrun) { size_t i; if (nicl == NULL) return -1; for (i = 0; i < nicl->Cmds.size(); i++) { cmdsrun[0] ++; int room_was = play.room_changes; switch (nicl->Cmds[i].Type) { case 0: // Do nothing break; case 1: // Run script { TempEip tempip(4001); RuntimeScriptValue rval_null; update_mp3(); if ((strstr(evblockbasename,"character")!=0) || (strstr(evblockbasename,"inventory")!=0)) { // Character or Inventory (global script) const char *torun = make_ts_func_name(evblockbasename,evblocknum,nicl->Cmds[i].Data[0].Value); // we are already inside the mouseclick event of the script, can't nest calls QueueScriptFunction(kScInstGame, torun); } else { // Other (room script) const char *torun = make_ts_func_name(evblockbasename,evblocknum,nicl->Cmds[i].Data[0].Value); QueueScriptFunction(kScInstRoom, torun); } update_mp3(); break; } case 2: // Add score (first time) if (timesrun[0] > 0) break; timesrun[0] ++; case 3: // Add score GiveScore (IPARAM1); break; case 4: // Display Message /* if (comprdata<0) display_message_aschar=evb->data[ss];*/ DisplayMessage(IPARAM1); break; case 5: // Play Music PlayMusicResetQueue(IPARAM1); break; case 6: // Stop Music stopmusic (); break; case 7: // Play Sound play_sound (IPARAM1); break; case 8: // Play Flic play_flc_file(IPARAM1, IPARAM2); break; case 9: // Run Dialog { int room_was = play.room_changes; RunDialog(IPARAM1); // if they changed room within the dialog script, // the interaction command list is no longer valid if (room_was != play.room_changes) return -1; } break; case 10: // Enable Dialog Option SetDialogOption (IPARAM1, IPARAM2, 1); break; case 11: // Disable Dialog Option SetDialogOption (IPARAM1, IPARAM2, 0); break; case 12: // Go To Screen Character_ChangeRoomAutoPosition(playerchar, IPARAM1, IPARAM2); return -1; case 13: // Add Inventory add_inventory (IPARAM1); break; case 14: // Move Object MoveObject (IPARAM1, IPARAM2, IPARAM3, IPARAM4); // if they want to wait until finished, do so if (IPARAM5) GameLoopUntilEvent(UNTIL_MOVEEND,(long)&objs[IPARAM1].moving); break; case 15: // Object Off ObjectOff (IPARAM1); break; case 16: // Object On ObjectOn (IPARAM1); break; case 17: // Set Object View SetObjectView (IPARAM1, IPARAM2); break; case 18: // Animate Object AnimateObject (IPARAM1, IPARAM2, IPARAM3, IPARAM4); break; case 19: // Move Character if (IPARAM4) MoveCharacterBlocking (IPARAM1, IPARAM2, IPARAM3, 0); else MoveCharacter (IPARAM1, IPARAM2, IPARAM3); break; case 20: // If Inventory Item was used if (play.usedinv == IPARAM1) { if (game.options[OPT_NOLOSEINV] == 0) lose_inventory (play.usedinv); if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; } else cmdsrun[0] --; break; case 21: // if player has inventory item if (playerchar->inv[IPARAM1] > 0) if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; break; case 22: // if a character is moving if (game.chars[IPARAM1].walking) if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; break; case 23: // if two variables are equal if (IPARAM1 == IPARAM2) if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; break; case 24: // Stop character walking StopMoving (IPARAM1); break; case 25: // Go to screen at specific co-ordinates NewRoomEx (IPARAM1, IPARAM2, IPARAM3); return -1; case 26: // Move NPC to different room if (!is_valid_character(IPARAM1)) quit("!Move NPC to different room: invalid character specified"); game.chars[IPARAM1].room = IPARAM2; break; case 27: // Set character view SetCharacterView (IPARAM1, IPARAM2); break; case 28: // Release character view ReleaseCharacterView (IPARAM1); break; case 29: // Follow character FollowCharacter (IPARAM1, IPARAM2); break; case 30: // Stop following FollowCharacter (IPARAM1, -1); break; case 31: // Disable hotspot DisableHotspot (IPARAM1); break; case 32: // Enable hotspot EnableHotspot (IPARAM1); break; case 33: // Set variable value get_interaction_variable(nicl->Cmds[i].Data[0].Value)->Value = IPARAM2; break; case 34: // Run animation scAnimateCharacter(IPARAM1, IPARAM2, IPARAM3, 0); GameLoopUntilEvent(UNTIL_SHORTIS0,(long)&game.chars[IPARAM1].animating); break; case 35: // Quick animation SetCharacterView (IPARAM1, IPARAM2); scAnimateCharacter(IPARAM1, IPARAM3, IPARAM4, 0); GameLoopUntilEvent(UNTIL_SHORTIS0,(long)&game.chars[IPARAM1].animating); ReleaseCharacterView (IPARAM1); break; case 36: // Set idle animation SetCharacterIdle (IPARAM1, IPARAM2, IPARAM3); break; case 37: // Disable idle animation SetCharacterIdle (IPARAM1, -1, -1); break; case 38: // Lose inventory item lose_inventory (IPARAM1); break; case 39: // Show GUI InterfaceOn (IPARAM1); break; case 40: // Hide GUI InterfaceOff (IPARAM1); break; case 41: // Stop running more commands return -1; case 42: // Face location FaceLocation (IPARAM1, IPARAM2, IPARAM3); break; case 43: // Pause command processor scrWait (IPARAM1); break; case 44: // Change character view ChangeCharacterView (IPARAM1, IPARAM2); break; case 45: // If player character is if (GetPlayerCharacter() == IPARAM1) if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; break; case 46: // if cursor mode is if (GetCursorMode() == IPARAM1) if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; break; case 47: // if player has been to room if (HasBeenToRoom(IPARAM1)) if (run_interaction_commandlist (nicl->Cmds[i].Children.get(), timesrun, cmdsrun)) return -1; break; default: quit("unknown new interaction command"); break; } // if the room changed within the action, nicl is no longer valid if (room_was != play.room_changes) return -1; } return 0; }
void walk_character(int chac,int tox,int toy,int ignwal, bool autoWalkAnims) { CharacterInfo*chin=&game.chars[chac]; if (chin->room!=displayed_room) quit("!MoveCharacter: character not in current room"); chin->flags &= ~CHF_MOVENOTWALK; int toxPassedIn = tox, toyPassedIn = toy; int charX = convert_to_low_res(chin->x); int charY = convert_to_low_res(chin->y); tox = convert_to_low_res(tox); toy = convert_to_low_res(toy); if ((tox == charX) && (toy == charY)) { StopMoving(chac); DEBUG_CONSOLE("%s already at destination, not moving", chin->scrname); return; } if ((chin->animating) && (autoWalkAnims)) chin->animating = 0; if (chin->idleleft < 0) { ReleaseCharacterView(chac); chin->idleleft=chin->idletime; } // stop them to make sure they're on a walkable area // but save their frame first so that if they're already // moving it looks smoother int oldframe = chin->frame; int waitWas = 0, animWaitWas = 0; // if they are currently walking, save the current Wait if (chin->walking) { waitWas = chin->walkwait; animWaitWas = charextra[chac].animwait; } StopMoving (chac); chin->frame = oldframe; // use toxPassedIn cached variable so the hi-res co-ordinates // are still displayed as such DEBUG_CONSOLE("%s: Start move to %d,%d", chin->scrname, toxPassedIn, toyPassedIn); int move_speed_x = chin->walkspeed; int move_speed_y = chin->walkspeed; if (chin->walkspeed_y != UNIFORM_WALK_SPEED) move_speed_y = chin->walkspeed_y; if ((move_speed_x == 0) && (move_speed_y == 0)) { debug_log("Warning: MoveCharacter called for '%s' with walk speed 0", chin->name); } set_route_move_speed(move_speed_x, move_speed_y); set_color_depth(8); int mslot=find_route(charX, charY, tox, toy, prepare_walkable_areas(chac), chac+CHMLSOFFS, 1, ignwal); set_color_depth(final_col_dep); if (mslot>0) { chin->walking = mslot; mls[mslot].direct = ignwal; if ((game.options[OPT_NATIVECOORDINATES] != 0) && (game.default_resolution > 2)) { convert_move_path_to_high_res(&mls[mslot]); } // cancel any pending waits on current animations // or if they were already moving, keep the current wait - // this prevents a glitch if MoveCharacter is called when they // are already moving if (autoWalkAnims) { chin->walkwait = waitWas; charextra[chac].animwait = animWaitWas; if (mls[mslot].pos[0] != mls[mslot].pos[1]) { fix_player_sprite(&mls[mslot],chin); } } else chin->flags |= CHF_MOVENOTWALK; } else if (autoWalkAnims) // pathfinder couldn't get a route, stand them still chin->frame = 0; }