private int fnISpeak(SwordObject cpt, int id, int cdt, int textNo, int spr, int f, int z, int x) { _speechClickDelay = 3; if (((textNo & ~1) == 0x3f0012) && (cdt == 0) && (spr == 0)) { cdt = SwordRes.GEOSTDLCDT; // workaround for missing animation when examining spr = SwordRes.GEOSTDL; // the conductor on the train roof } cpt.logic = LOGIC_speech; // first setup the talk animation if (cdt != 0 && (spr == 0)) { // if 'cdt' is non-zero but 'spr' is zero - 'cdt' is an anim table tag var animTabData = _resMan.OpenFetchRes((uint)cdt); var anim = new AnimSet(animTabData, Header.Size + cpt.dir * AnimSet.Size); cpt.anim_resource = (int)_resMan.ReadUInt32(anim.cdt); if (anim.cdt != 0) cpt.resource = (int)_resMan.ReadUInt32(anim.spr); _resMan.ResClose((uint)cdt); } else { cpt.anim_resource = cdt; if (cdt != 0) cpt.resource = spr; } cpt.anim_pc = 0; // start anim from first frame if (cpt.anim_resource != 0) { if (cpt.resource == 0) throw new InvalidOperationException($"ID {id}: Can't run anim with cdt={cdt}, spr={spr}"); FrameHeader frameHead = new FrameHeader(_resMan.FetchFrame(_resMan.OpenFetchRes((uint)cpt.resource), 0)); if (frameHead.offsetX != 0 && frameHead.offsetY != 0) { // is this a boxed mega? cpt.status |= STAT_SHRINK; cpt.anim_x = cpt.xcoord; cpt.anim_y = cpt.ycoord; } else cpt.status &= ~STAT_SHRINK; _resMan.ResClose((uint)cpt.resource); } if (SystemVars.PlaySpeech != 0) _speechRunning = _sound.StartSpeech((ushort)(textNo >> 16), (ushort)(textNo & 0xFFFF)); else _speechRunning = false; _speechFinished = false; if (SystemVars.ShowText != 0 || (!_speechRunning)) { _textRunning = true; var text = _objMan.LockText((uint) textNo); cpt.speech_time = GetTextLength(text) + 5; uint textCptId = _textMan.LowTextManager(text, cpt.speech_width, (byte)cpt.speech_pen); _objMan.UnlockText((uint) textNo); SwordObject textCpt = _objMan.FetchObject(textCptId); textCpt.screen = cpt.screen; textCpt.target = (int)textCptId; // the graphic is a property of Text, so we don't lock/unlock it. ushort textSpriteWidth = _resMan.ReadUInt16(new FrameHeader(_textMan.GiveSpriteData((byte)textCpt.target)).width); ushort textSpriteHeight = _resMan.ReadUInt16(new FrameHeader(_textMan.GiveSpriteData((byte)textCpt.target)).height); cpt.text_id = (int)textCptId; // now set text coords, above the player, usually const int TEXT_MARGIN = 3; // distance kept from edges of screen const int ABOVE_HEAD = 20; // distance kept above talking sprite ushort textX, textY; if (((id == GEORGE) || ((id == NICO) && (ScriptVars[(int)ScriptVariableNames.SCREEN] == 10))) && (cpt.anim_resource == 0)) { // if George is doing Voice-Over text (centered at the bottom of the screen) textX = (ushort)(ScriptVars[(int)ScriptVariableNames.SCROLL_OFFSET_X] + 128 + (640 / 2) - textSpriteWidth / 2); textY = (ushort)(ScriptVars[(int)ScriptVariableNames.SCROLL_OFFSET_Y] + 128 + 400); } else { if ((id == GEORGE) && (ScriptVars[(int)ScriptVariableNames.SCREEN] == 79)) textX = (ushort)cpt.mouse_x2; // move it off george's head else textX = (ushort)((cpt.mouse_x1 + cpt.mouse_x2) / 2 - textSpriteWidth / 2); textY = (ushort)(cpt.mouse_y1 - textSpriteHeight - ABOVE_HEAD); } // now ensure text is within visible screen ushort textLeftMargin, textRightMargin, textTopMargin, textBottomMargin; textLeftMargin = (ushort)(Screen.SCREEN_LEFT_EDGE + TEXT_MARGIN + ScriptVars[(int)ScriptVariableNames.SCROLL_OFFSET_X]); textRightMargin = (ushort)(Screen.SCREEN_RIGHT_EDGE - TEXT_MARGIN + ScriptVars[(int)ScriptVariableNames.SCROLL_OFFSET_X] - textSpriteWidth); textTopMargin = (ushort)(Screen.SCREEN_TOP_EDGE + TEXT_MARGIN + ScriptVars[(int)ScriptVariableNames.SCROLL_OFFSET_Y]); textBottomMargin = (ushort)(Screen.SCREEN_BOTTOM_EDGE - TEXT_MARGIN + ScriptVars[(int)ScriptVariableNames.SCROLL_OFFSET_Y] - textSpriteHeight); textCpt.anim_x = textCpt.xcoord = ScummHelper.Clip(textX, textLeftMargin, textRightMargin); textCpt.anim_y = textCpt.ycoord = ScummHelper.Clip(textY, textTopMargin, textBottomMargin); } return SCRIPT_STOP; }
private int fnAnim(SwordObject cpt, int id, int cdt, int spr, int e, int f, int z, int x) { if (cdt != 0 && (spr == 0)) { var animTab = _resMan.OpenFetchRes((uint)cdt); var animOffset = Header.Size + cpt.dir * AnimSet.Size; var anim = new AnimSet(animTab, animOffset); cpt.anim_resource = (int)_resMan.ReadUInt32(anim.cdt); cpt.resource = (int)_resMan.ReadUInt32(anim.spr); _resMan.ResClose((uint)cdt); } else { cpt.anim_resource = cdt; cpt.resource = spr; } if ((cpt.anim_resource == 0) || (cpt.resource == 0)) throw new InvalidOperationException($"fnAnim called width ({cdt}/{spr}) => ({cpt.anim_resource}/{cpt.resource})"); var frameHead = new FrameHeader(_resMan.FetchFrame(_resMan.OpenFetchRes((uint)cpt.resource), 0)); if (frameHead.offsetX != 0 || frameHead.offsetY != 0) { // boxed mega anim? cpt.status |= STAT_SHRINK; cpt.anim_x = cpt.xcoord; // set anim coords to 'feet' coords - only need to do this once cpt.anim_y = cpt.ycoord; } else { // Anim_driver sets anim coords to cdt coords for every frame of a loose anim cpt.status &= ~STAT_SHRINK; } _resMan.ResClose((uint)cpt.resource); cpt.logic = LOGIC_anim; cpt.anim_pc = 0; cpt.sync = 0; return SCRIPT_STOP; }