mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
This fixes a regression after db71d92 in case Character.Animate() called during idling, and Loop number assertion is done prior to view changing back to normal view, could cause game to error. Pick out "stop_character_idling" function that checks idle state and releases the locked idle view. From upstream 946fa71c9332cae98f31d8b7672a1fe7b76d46c5 Fix #15523
3831 lines
136 KiB
C++
3831 lines
136 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
//=============================================================================
|
|
//
|
|
// AGS Character functions
|
|
//
|
|
//=============================================================================
|
|
|
|
#include "ags/engine/ac/character.h"
|
|
#include "ags/shared/ac/common.h"
|
|
#include "ags/shared/ac/game_setup_struct.h"
|
|
#include "ags/shared/ac/view.h"
|
|
#include "ags/engine/ac/display.h"
|
|
#include "ags/engine/ac/draw.h"
|
|
#include "ags/engine/ac/event.h"
|
|
#include "ags/engine/ac/game.h"
|
|
#include "ags/engine/ac/global_audio.h"
|
|
#include "ags/engine/ac/global_character.h"
|
|
#include "ags/engine/ac/global_game.h"
|
|
#include "ags/engine/ac/global_object.h"
|
|
#include "ags/engine/ac/global_region.h"
|
|
#include "ags/engine/ac/global_room.h"
|
|
#include "ags/engine/ac/global_translation.h"
|
|
#include "ags/engine/ac/gui.h"
|
|
#include "ags/engine/ac/lip_sync.h"
|
|
#include "ags/engine/ac/mouse.h"
|
|
#include "ags/engine/ac/object.h"
|
|
#include "ags/engine/ac/overlay.h"
|
|
#include "ags/engine/ac/properties.h"
|
|
#include "ags/engine/ac/room.h"
|
|
#include "ags/engine/ac/screen_overlay.h"
|
|
#include "ags/engine/ac/string.h"
|
|
#include "ags/engine/ac/system.h"
|
|
#include "ags/engine/ac/view_frame.h"
|
|
#include "ags/engine/ac/walkable_area.h"
|
|
#include "ags/shared/gui/gui_main.h"
|
|
#include "ags/engine/ac/route_finder.h"
|
|
#include "ags/engine/ac/game_state.h"
|
|
#include "ags/engine/debugging/debug_log.h"
|
|
#include "ags/engine/main/game_run.h"
|
|
#include "ags/engine/main/update.h"
|
|
#include "ags/shared/ac/sprite_cache.h"
|
|
#include "ags/shared/util/string_compat.h"
|
|
#include "ags/engine/gfx/graphics_driver.h"
|
|
#include "ags/engine/script/runtime_script_value.h"
|
|
#include "ags/engine/ac/dynobj/cc_character.h"
|
|
#include "ags/engine/ac/dynobj/cc_inventory.h"
|
|
#include "ags/engine/ac/dynobj/dynobj_manager.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/shared/gfx/gfx_def.h"
|
|
#include "ags/engine/media/audio/audio_system.h"
|
|
#include "ags/engine/ac/move_list.h"
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/engine/script/script_api.h"
|
|
#include "ags/engine/script/script_runtime.h"
|
|
#include "ags/engine/ac/dynobj/script_string.h"
|
|
#include "ags/globals.h"
|
|
|
|
namespace AGS3 {
|
|
|
|
using namespace AGS::Shared;
|
|
|
|
bool is_valid_character(int char_id) {
|
|
return ((char_id >= 0) && (char_id < _GP(game).numcharacters));
|
|
}
|
|
|
|
// Checks if character is currently playing idle anim, and reset it
|
|
static void stop_character_idling(CharacterInfo *chi) {
|
|
if (chi->idleleft < 0) {
|
|
Character_UnlockView(chi);
|
|
chi->idleleft = chi->idletime;
|
|
}
|
|
}
|
|
|
|
bool AssertCharacter(const char *apiname, int char_id) {
|
|
if ((char_id >= 0) && (char_id < _GP(game).numcharacters))
|
|
return true;
|
|
debug_script_warn("%s: invalid character id %d (range is 0..%d)", apiname, char_id, _GP(game).numcharacters - 1);
|
|
return false;
|
|
}
|
|
|
|
void Character_AddInventory(CharacterInfo *chaa, ScriptInvItem *invi, int addIndex) {
|
|
int ee;
|
|
|
|
if (invi == nullptr)
|
|
quit("!AddInventoryToCharacter: invalid inventory number");
|
|
|
|
int inum = invi->id;
|
|
|
|
if (chaa->inv[inum] >= 32000)
|
|
quit("!AddInventory: cannot carry more than 32000 of one inventory item");
|
|
|
|
chaa->inv[inum]++;
|
|
|
|
int charid = chaa->index_id;
|
|
|
|
if (_GP(game).options[OPT_DUPLICATEINV] == 0) {
|
|
// Ensure it is only in the list once
|
|
for (ee = 0; ee < _GP(charextra)[charid].invorder_count; ee++) {
|
|
if (_GP(charextra)[charid].invorder[ee] == inum) {
|
|
// They already have the item, so don't add it to the list
|
|
if (chaa == _G(playerchar))
|
|
run_on_event(GE_ADD_INV, RuntimeScriptValue().SetInt32(inum));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (_GP(charextra)[charid].invorder_count >= MAX_INVORDER)
|
|
quit("!Too many inventory items added, max 500 display at one time");
|
|
|
|
if ((addIndex == SCR_NO_VALUE) ||
|
|
(addIndex >= _GP(charextra)[charid].invorder_count) ||
|
|
(addIndex < 0)) {
|
|
// add new item at end of list
|
|
_GP(charextra)[charid].invorder[_GP(charextra)[charid].invorder_count] = inum;
|
|
} else {
|
|
// insert new item at index
|
|
for (ee = _GP(charextra)[charid].invorder_count - 1; ee >= addIndex; ee--)
|
|
_GP(charextra)[charid].invorder[ee + 1] = _GP(charextra)[charid].invorder[ee];
|
|
|
|
_GP(charextra)[charid].invorder[addIndex] = inum;
|
|
}
|
|
_GP(charextra)[charid].invorder_count++;
|
|
GUI::MarkInventoryForUpdate(charid, charid == _GP(game).playercharacter);
|
|
if (chaa == _G(playerchar))
|
|
run_on_event(GE_ADD_INV, RuntimeScriptValue().SetInt32(inum));
|
|
}
|
|
|
|
void Character_AddWaypoint(CharacterInfo *chaa, int x, int y) {
|
|
|
|
if (chaa->room != _G(displayed_room))
|
|
quitprintf("!MoveCharacterPath: character %s is not in current room %d (it is in room %d)",
|
|
chaa->scrname, _G(displayed_room), chaa->room);
|
|
|
|
// not already walking, so just do a normal move
|
|
if (chaa->walking <= 0) {
|
|
Character_Walk(chaa, x, y, IN_BACKGROUND, ANYWHERE);
|
|
return;
|
|
}
|
|
|
|
MoveList *cmls = &_GP(mls)[chaa->walking % TURNING_AROUND];
|
|
if (cmls->numstage >= MAXNEEDSTAGES) {
|
|
debug_script_warn("Character::AddWaypoint: move is too complex, cannot add any further paths");
|
|
return;
|
|
}
|
|
|
|
// They're already walking there anyway
|
|
const Point &last_pos = cmls->GetLastPos();
|
|
if (last_pos == Point(x, y))
|
|
return;
|
|
|
|
int move_speed_x, move_speed_y;
|
|
chaa->get_effective_walkspeeds(move_speed_x, move_speed_y);
|
|
if ((move_speed_x == 0) && (move_speed_y == 0)) {
|
|
debug_script_warn("Character::AddWaypoint: called for '%s' with walk speed 0", chaa->scrname);
|
|
}
|
|
|
|
// There's an issue: the existing movelist is converted to room resolution,
|
|
// so we do this trick: convert last step to mask resolution, before calling
|
|
// a pathfinder api, and then we'll convert old and new last step back.
|
|
// TODO: figure out a better way of processing this!
|
|
const int last_stage = cmls->numstage - 1;
|
|
cmls->pos[last_stage] = { room_to_mask_coord(cmls->pos[last_stage].X), room_to_mask_coord(cmls->pos[last_stage].Y) };
|
|
const int dst_x = room_to_mask_coord(x);
|
|
const int dst_y = room_to_mask_coord(y);
|
|
if(add_waypoint_direct(cmls, dst_x, dst_y, move_speed_x, move_speed_y))
|
|
convert_move_path_to_room_resolution(cmls, last_stage, last_stage + 1);
|
|
}
|
|
|
|
void Character_Animate(CharacterInfo *chaa, int loop, int delay, int repeat,
|
|
int blocking, int direction, int sframe, int volume) {
|
|
|
|
// If idle view in progress for the character, stop the idle anim;
|
|
// do this prior to the loop check, as the view may switch back to defview here
|
|
stop_character_idling(chaa);
|
|
|
|
ValidateViewAnimVLF("Character.Animate", chaa->view, loop, sframe);
|
|
ValidateViewAnimParams("Character.Animate", repeat, blocking, direction);
|
|
|
|
animate_character(chaa, loop, delay, repeat, direction, sframe, volume);
|
|
|
|
if (blocking != 0)
|
|
GameLoopUntilValueIsZero(&chaa->animating);
|
|
}
|
|
|
|
void Character_Animate5(CharacterInfo *chaa, int loop, int delay, int repeat, int blocking, int direction) {
|
|
Character_Animate(chaa, loop, delay, repeat, blocking, direction, 0 /* first frame */, 100 /* full volume */);
|
|
}
|
|
|
|
void Character_Animate6(CharacterInfo *chaa, int loop, int delay, int repeat, int blocking, int direction, int sframe) {
|
|
Character_Animate(chaa, loop, delay, repeat, blocking, direction, sframe, 100 /* full volume */);
|
|
}
|
|
|
|
void Character_ChangeRoomAutoPosition(CharacterInfo *chaa, int room, int newPos) {
|
|
if (chaa->index_id != _GP(game).playercharacter) {
|
|
quit("!Character.ChangeRoomAutoPosition can only be used with the player character.");
|
|
}
|
|
|
|
_G(new_room_pos) = newPos;
|
|
|
|
if (_G(new_room_pos) == 0) {
|
|
// auto place on other side of screen
|
|
if (chaa->x <= _GP(thisroom).Edges.Left + 10)
|
|
_G(new_room_pos) = 2000;
|
|
else if (chaa->x >= _GP(thisroom).Edges.Right - 10)
|
|
_G(new_room_pos) = 1000;
|
|
else if (chaa->y <= _GP(thisroom).Edges.Top + 10)
|
|
_G(new_room_pos) = 3000;
|
|
else if (chaa->y >= _GP(thisroom).Edges.Bottom - 10)
|
|
_G(new_room_pos) = 4000;
|
|
|
|
if (_G(new_room_pos) < 3000)
|
|
_G(new_room_pos) += chaa->y;
|
|
else
|
|
_G(new_room_pos) += chaa->x;
|
|
}
|
|
NewRoom(room);
|
|
}
|
|
|
|
void Character_ChangeRoom(CharacterInfo *chaa, int room, int x, int y) {
|
|
Character_ChangeRoomSetLoop(chaa, room, x, y, SCR_NO_VALUE);
|
|
}
|
|
|
|
void Character_ChangeRoomSetLoop(CharacterInfo *chaa, int room, int x, int y, int direction) {
|
|
|
|
if (chaa->index_id != _GP(game).playercharacter) {
|
|
// NewRoomNPC
|
|
if ((x != SCR_NO_VALUE) && (y != SCR_NO_VALUE)) {
|
|
chaa->x = x;
|
|
chaa->y = y;
|
|
if (direction != SCR_NO_VALUE && direction >= 0) chaa->loop = direction;
|
|
}
|
|
chaa->prevroom = chaa->room;
|
|
chaa->room = room;
|
|
|
|
debug_script_log("%s moved to room %d, location %d,%d, loop %d",
|
|
chaa->scrname, room, chaa->x, chaa->y, chaa->loop);
|
|
|
|
return;
|
|
}
|
|
|
|
if ((x != SCR_NO_VALUE) && (y != SCR_NO_VALUE)) {
|
|
// We cannot set character position right away,
|
|
// because room switch will occur only after the script end,
|
|
// and character position may be still changing meanwhile.
|
|
_G(new_room_pos) = 0;
|
|
|
|
// don't check X or Y bounds, so that they can do a
|
|
// walk-in animation if they want
|
|
_G(new_room_x) = x;
|
|
_G(new_room_y) = y;
|
|
if (direction != SCR_NO_VALUE)
|
|
_G(new_room_loop) = direction;
|
|
}
|
|
|
|
NewRoom(room);
|
|
}
|
|
|
|
|
|
void Character_ChangeView(CharacterInfo *chap, int vii) {
|
|
vii--;
|
|
|
|
if ((vii < 0) || (vii >= _GP(game).numviews))
|
|
quit("!ChangeCharacterView: invalid view number specified");
|
|
|
|
// if animating, but not idle view, give warning message
|
|
if ((chap->flags & CHF_FIXVIEW) && (chap->idleleft >= 0))
|
|
debug_script_warn("Warning: ChangeCharacterView was used while the view was fixed - call ReleaseCharView first");
|
|
|
|
// if the idle animation is playing we should release the view
|
|
stop_character_idling(chap);
|
|
|
|
debug_script_log("%s: Change view to %d", chap->scrname, vii + 1);
|
|
chap->defview = vii;
|
|
chap->view = vii;
|
|
stop_character_anim(chap);
|
|
chap->frame = 0;
|
|
chap->wait = 0;
|
|
chap->walkwait = 0;
|
|
_GP(charextra)[chap->index_id].animwait = 0;
|
|
FindReasonableLoopForCharacter(chap);
|
|
}
|
|
|
|
enum DirectionalLoop {
|
|
kDirLoop_Down = 0,
|
|
kDirLoop_Left = 1,
|
|
kDirLoop_Right = 2,
|
|
kDirLoop_Up = 3,
|
|
kDirLoop_DownRight = 4,
|
|
kDirLoop_UpRight = 5,
|
|
kDirLoop_DownLeft = 6,
|
|
kDirLoop_UpLeft = 7,
|
|
|
|
kDirLoop_Default = kDirLoop_Down,
|
|
kDirLoop_LastOrthogonal = kDirLoop_Up,
|
|
kDirLoop_Last = kDirLoop_UpLeft,
|
|
};
|
|
|
|
// Internal direction-facing functions
|
|
|
|
DirectionalLoop GetDirectionalLoop(CharacterInfo *chinfo, int x_diff, int y_diff) {
|
|
DirectionalLoop next_loop = kDirLoop_Left; // NOTE: default loop was Left for some reason
|
|
|
|
const ViewStruct &chview = _GP(views)[chinfo->view];
|
|
const bool new_version = _G(loaded_game_file_version) > kGameVersion_272;
|
|
const bool has_down_loop = ((chview.numLoops > kDirLoop_Down) && (chview.loops[kDirLoop_Down].numFrames > 0));
|
|
const bool has_up_loop = ((chview.numLoops > kDirLoop_Up) && (chview.loops[kDirLoop_Up].numFrames > 0));
|
|
// NOTE: 3.+ games required left & right loops to be present at all times
|
|
const bool has_left_loop = new_version ||
|
|
((chview.numLoops > kDirLoop_Left) && (chview.loops[kDirLoop_Left].numFrames > 0));
|
|
const bool has_right_loop = new_version ||
|
|
((chview.numLoops > kDirLoop_Right) && (chview.loops[kDirLoop_Right].numFrames > 0));
|
|
const bool has_diagonal_loops = useDiagonal(chinfo) == 0; // NOTE: useDiagonal returns 0 for "true"
|
|
|
|
const bool want_horizontal = (abs(y_diff) < abs(x_diff)) ||
|
|
(new_version && (!has_down_loop || !has_up_loop)) ||
|
|
// NOTE: <= 2.72 games switch to horizontal loops only if both vertical ones are missing
|
|
(!new_version && (!has_down_loop && !has_up_loop));
|
|
if (want_horizontal) {
|
|
const bool want_diagonal = has_diagonal_loops && (abs(y_diff) > abs(x_diff) / 2);
|
|
if (!has_left_loop && !has_right_loop) {
|
|
next_loop = kDirLoop_Down;
|
|
} else if (has_right_loop && (x_diff > 0)) {
|
|
next_loop = want_diagonal ? (y_diff < 0 ? kDirLoop_UpRight : kDirLoop_DownRight) :
|
|
kDirLoop_Right;
|
|
} else if (has_left_loop && (x_diff <= 0)) {
|
|
next_loop = want_diagonal ? (y_diff < 0 ? kDirLoop_UpLeft : kDirLoop_DownLeft) :
|
|
kDirLoop_Left;
|
|
}
|
|
} else {
|
|
const bool want_diagonal = has_diagonal_loops && (abs(x_diff) > abs(y_diff) / 2);
|
|
if (y_diff > 0 || !has_up_loop) {
|
|
next_loop = want_diagonal ? (x_diff < 0 ? kDirLoop_DownLeft : kDirLoop_DownRight) :
|
|
kDirLoop_Down;
|
|
} else {
|
|
next_loop = want_diagonal ? (x_diff < 0 ? kDirLoop_UpLeft : kDirLoop_UpRight) :
|
|
kDirLoop_Up;
|
|
}
|
|
}
|
|
return next_loop;
|
|
}
|
|
|
|
void FaceDirectionalLoop(CharacterInfo *char1, int direction, int blockingStyle) {
|
|
// Change facing only if the desired direction is different
|
|
if (direction != char1->loop) {
|
|
if ((_GP(game).options[OPT_TURNTOFACELOC] != 0) &&
|
|
(_G(in_enters_screen) == 0)) {
|
|
const int no_diagonal = useDiagonal(char1);
|
|
const int highestLoopForTurning = no_diagonal != 1 ? kDirLoop_Last : kDirLoop_LastOrthogonal;
|
|
if ((char1->loop <= highestLoopForTurning)) {
|
|
// Turn to face new direction
|
|
Character_StopMoving(char1);
|
|
if (char1->on == 1) {
|
|
// only do the turning if the character is not hidden
|
|
// (otherwise GameLoopUntilNotMoving will never return)
|
|
start_character_turning(char1, direction, no_diagonal);
|
|
|
|
if ((blockingStyle == BLOCKING) || (blockingStyle == 1))
|
|
GameLoopUntilNotMoving(&char1->walking);
|
|
} else
|
|
char1->loop = direction;
|
|
} else
|
|
char1->loop = direction;
|
|
} else
|
|
char1->loop = direction;
|
|
}
|
|
|
|
char1->frame = 0;
|
|
}
|
|
|
|
void FaceLocationXY(CharacterInfo *char1, int xx, int yy, int blockingStyle) {
|
|
debug_script_log("%s: Face location %d,%d", char1->scrname, xx, yy);
|
|
|
|
const int diffrx = xx - char1->x;
|
|
const int diffry = yy - char1->y;
|
|
|
|
if ((diffrx == 0) && (diffry == 0)) {
|
|
// FaceLocation called on their current position - do nothing
|
|
return;
|
|
}
|
|
|
|
FaceDirectionalLoop(char1, GetDirectionalLoop(char1, diffrx, diffry), blockingStyle);
|
|
}
|
|
|
|
// External direction-facing functions with validation
|
|
|
|
void Character_FaceDirection(CharacterInfo *char1, int direction, int blockingStyle) {
|
|
if (char1 == nullptr)
|
|
quit("!FaceDirection: invalid character specified");
|
|
|
|
if (direction != SCR_NO_VALUE) {
|
|
if (direction < 0 || direction > kDirLoop_Last)
|
|
quit("!FaceDirection: invalid direction specified");
|
|
|
|
FaceDirectionalLoop(char1, direction, blockingStyle);
|
|
}
|
|
}
|
|
|
|
void Character_FaceLocation(CharacterInfo *char1, int xx, int yy, int blockingStyle) {
|
|
if (char1 == nullptr)
|
|
quit("!FaceLocation: invalid character specified");
|
|
|
|
FaceLocationXY(char1, xx, yy, blockingStyle);
|
|
}
|
|
|
|
void Character_FaceObject(CharacterInfo *char1, ScriptObject *obj, int blockingStyle) {
|
|
if (obj == nullptr)
|
|
quit("!FaceObject: invalid object specified");
|
|
|
|
FaceLocationXY(char1, _G(objs)[obj->id].x, _G(objs)[obj->id].y, blockingStyle);
|
|
}
|
|
|
|
void Character_FaceCharacter(CharacterInfo *char1, CharacterInfo *char2, int blockingStyle) {
|
|
if (char2 == nullptr)
|
|
quit("!FaceCharacter: invalid character specified");
|
|
|
|
if (char1->room != char2->room)
|
|
quitprintf("!FaceCharacter: characters %s and %s are in different rooms (room %d and room %d respectively)",
|
|
char1->scrname, char2->scrname, char1->room, char2->room);
|
|
|
|
FaceLocationXY(char1, char2->x, char2->y, blockingStyle);
|
|
}
|
|
|
|
void Character_FollowCharacter(CharacterInfo *chaa, CharacterInfo *tofollow, int distaway, int eagerness) {
|
|
|
|
if ((eagerness < 0) || (eagerness > 250))
|
|
quit("!FollowCharacterEx: invalid eagerness: must be 0-250");
|
|
|
|
if ((chaa->index_id == _GP(game).playercharacter) && (tofollow != nullptr) &&
|
|
(tofollow->room != chaa->room))
|
|
quitprintf("!FollowCharacterEx: you cannot tell the player character %s, who is in room %d, to follow a character %s who is in another room %d",
|
|
chaa->scrname, chaa->room, tofollow->scrname, tofollow->room);
|
|
|
|
if (tofollow != nullptr) {
|
|
debug_script_log("%s: Start following %s (dist %d, eager %d)", chaa->scrname, tofollow->scrname, distaway, eagerness);
|
|
} else {
|
|
debug_script_log("%s: Stop following other character", chaa->scrname);
|
|
}
|
|
|
|
if ((chaa->following >= 0) &&
|
|
(chaa->followinfo == FOLLOW_ALWAYSONTOP)) {
|
|
// if this character was following always-on-top, its baseline will
|
|
// have been changed, so release it.
|
|
chaa->baseline = -1;
|
|
}
|
|
|
|
if (tofollow == nullptr)
|
|
chaa->following = -1;
|
|
else
|
|
chaa->following = tofollow->index_id;
|
|
|
|
chaa->followinfo = (distaway << 8) | eagerness;
|
|
|
|
chaa->flags &= ~CHF_BEHINDSHEPHERD;
|
|
|
|
// special case for Always On Other Character
|
|
if (distaway == FOLLOW_ALWAYSONTOP) {
|
|
chaa->followinfo = FOLLOW_ALWAYSONTOP;
|
|
if (eagerness == 1)
|
|
chaa->flags |= CHF_BEHINDSHEPHERD;
|
|
}
|
|
|
|
if (chaa->animating & CHANIM_REPEAT)
|
|
debug_script_warn("Warning: FollowCharacter called but the sheep is currently animating looped. It may never start to follow.");
|
|
|
|
}
|
|
|
|
int Character_IsCollidingWithChar(CharacterInfo *char1, CharacterInfo *char2) {
|
|
if (char2 == nullptr)
|
|
quit("!AreCharactersColliding: invalid char2");
|
|
|
|
if (char1->room != char2->room) return 0; // not colliding
|
|
|
|
if ((char1->y > char2->y - 5) && (char1->y < char2->y + 5)) ;
|
|
else return 0;
|
|
|
|
int w1 = game_to_data_coord(GetCharacterWidth(char1->index_id));
|
|
int w2 = game_to_data_coord(GetCharacterWidth(char2->index_id));
|
|
|
|
int xps1 = char1->x - w1 / 2;
|
|
int xps2 = char2->x - w2 / 2;
|
|
|
|
if ((xps1 >= xps2 - w1) & (xps1 <= xps2 + w2)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int Character_IsCollidingWithObject(CharacterInfo *chin, ScriptObject *objid) {
|
|
if (objid == nullptr)
|
|
quit("!AreCharObjColliding: invalid object number");
|
|
|
|
if (chin->room != _G(displayed_room))
|
|
return 0;
|
|
if (_G(objs)[objid->id].on != 1)
|
|
return 0;
|
|
|
|
Bitmap *checkblk = GetObjectImage(objid->id);
|
|
int objWidth = checkblk->GetWidth();
|
|
int objHeight = checkblk->GetHeight();
|
|
int o1x = _G(objs)[objid->id].x;
|
|
int o1y = _G(objs)[objid->id].y - game_to_data_coord(objHeight);
|
|
|
|
Bitmap *charpic = GetCharacterImage(chin->index_id);
|
|
|
|
int charWidth = charpic->GetWidth();
|
|
int charHeight = charpic->GetHeight();
|
|
int o2x = chin->x - game_to_data_coord(charWidth) / 2;
|
|
int o2y = _GP(charextra)[chin->index_id].GetEffectiveY(chin) - 5; // only check feet
|
|
|
|
if ((o2x >= o1x - game_to_data_coord(charWidth)) &&
|
|
(o2x <= o1x + game_to_data_coord(objWidth)) &&
|
|
(o2y >= o1y - 8) &&
|
|
(o2y <= o1y + game_to_data_coord(objHeight))) {
|
|
// the character's feet are on the object
|
|
if (_GP(game).options[OPT_PIXPERFECT] == 0)
|
|
return 1;
|
|
// check if they're on a transparent bit of the object
|
|
int stxp = data_to_game_coord(o2x - o1x);
|
|
int styp = data_to_game_coord(o2y - o1y);
|
|
int maskcol = checkblk->GetMaskColor();
|
|
int maskcolc = charpic->GetMaskColor();
|
|
int thispix, thispixc;
|
|
// check each pixel of the object along the char's feet
|
|
for (int i = 0; i < charWidth; i += get_fixed_pixel_size(1)) {
|
|
for (int j = 0; j < get_fixed_pixel_size(6); j += get_fixed_pixel_size(1)) {
|
|
thispix = my_getpixel(checkblk, i + stxp, j + styp);
|
|
thispixc = my_getpixel(charpic, i, j + (charHeight - get_fixed_pixel_size(5)));
|
|
|
|
if ((thispix != -1) && (thispix != maskcol) &&
|
|
(thispixc != -1) && (thispixc != maskcolc))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool Character_IsInteractionAvailable(CharacterInfo *cchar, int mood) {
|
|
|
|
_GP(play).check_interaction_only = 1;
|
|
RunCharacterInteraction(cchar->index_id, mood);
|
|
int ciwas = _GP(play).check_interaction_only;
|
|
_GP(play).check_interaction_only = 0;
|
|
return (ciwas == 2);
|
|
}
|
|
|
|
void Character_LockView(CharacterInfo *chap, int vii) {
|
|
Character_LockViewEx(chap, vii, STOP_MOVING);
|
|
}
|
|
|
|
void Character_LockViewEx(CharacterInfo *chap, int vii, int stopMoving) {
|
|
vii--; // convert to 0-based
|
|
AssertView("SetCharacterView", vii);
|
|
|
|
stop_character_idling(chap);
|
|
|
|
if (stopMoving != KEEP_MOVING) {
|
|
Character_StopMoving(chap);
|
|
}
|
|
chap->view = vii;
|
|
stop_character_anim(chap);
|
|
FindReasonableLoopForCharacter(chap);
|
|
chap->frame = 0;
|
|
chap->wait = 0;
|
|
chap->flags |= CHF_FIXVIEW;
|
|
chap->pic_xoffs = 0;
|
|
chap->pic_yoffs = 0;
|
|
debug_script_log("%s: View locked to %d", chap->scrname, vii + 1);
|
|
}
|
|
|
|
void Character_LockViewAligned_Old(CharacterInfo *chap, int vii, int loop, int align) {
|
|
Character_LockViewAlignedEx(chap, vii, loop, ConvertLegacyScriptAlignment((LegacyScriptAlignment)align), STOP_MOVING);
|
|
}
|
|
|
|
void Character_LockViewAlignedEx_Old(CharacterInfo *chap, int vii, int loop, int align, int stopMoving) {
|
|
Character_LockViewAlignedEx(chap, vii, loop, ConvertLegacyScriptAlignment((LegacyScriptAlignment)align), stopMoving);
|
|
}
|
|
|
|
void Character_LockViewAligned(CharacterInfo *chap, int vii, int loop, int align) {
|
|
Character_LockViewAlignedEx(chap, vii, loop, align, STOP_MOVING);
|
|
}
|
|
|
|
void Character_LockViewAlignedEx(CharacterInfo *chap, int vii, int loop, int align, int stopMoving) {
|
|
if (chap->view < 0)
|
|
quit("!SetCharacterLoop: character has invalid old view number");
|
|
|
|
int sppic = _GP(views)[chap->view].loops[chap->loop].frames[chap->frame].pic;
|
|
int leftSide = data_to_game_coord(chap->x) - _GP(game).SpriteInfos[sppic].Width / 2;
|
|
|
|
Character_LockViewEx(chap, vii, stopMoving);
|
|
|
|
AssertLoop("SetCharacterViewEx", chap->view, loop);
|
|
|
|
chap->loop = loop;
|
|
chap->frame = 0;
|
|
int newpic = _GP(views)[chap->view].loops[chap->loop].frames[chap->frame].pic;
|
|
int newLeft = data_to_game_coord(chap->x) - _GP(game).SpriteInfos[newpic].Width / 2;
|
|
int xdiff = 0;
|
|
|
|
if (align & kMAlignLeft)
|
|
xdiff = leftSide - newLeft;
|
|
else if (align & kMAlignHCenter)
|
|
xdiff = 0;
|
|
else if (align & kMAlignRight)
|
|
xdiff = (leftSide + _GP(game).SpriteInfos[sppic].Width) - (newLeft + _GP(game).SpriteInfos[newpic].Width);
|
|
else
|
|
quit("!SetCharacterViewEx: invalid alignment type specified");
|
|
|
|
chap->pic_xoffs = xdiff;
|
|
chap->pic_yoffs = 0;
|
|
}
|
|
|
|
void Character_LockViewFrame(CharacterInfo *chaa, int view, int loop, int frame) {
|
|
Character_LockViewFrameEx(chaa, view, loop, frame, STOP_MOVING);
|
|
}
|
|
|
|
void Character_LockViewFrameEx(CharacterInfo *chaa, int view, int loop, int frame, int stopMoving) {
|
|
Character_LockViewEx(chaa, view, stopMoving);
|
|
AssertFrame("SetCharacterFrame", view - 1, loop, frame);
|
|
chaa->loop = loop;
|
|
chaa->frame = frame;
|
|
}
|
|
|
|
void Character_LockViewOffset(CharacterInfo *chap, int vii, int xoffs, int yoffs) {
|
|
Character_LockViewOffsetEx(chap, vii, xoffs, yoffs, STOP_MOVING);
|
|
}
|
|
|
|
void Character_LockViewOffsetEx(CharacterInfo *chap, int vii, int xoffs, int yoffs, int stopMoving) {
|
|
Character_LockViewEx(chap, vii, stopMoving);
|
|
|
|
// This function takes offsets in real game coordinates as opposed to script coordinates
|
|
defgame_to_finalgame_coords(xoffs, yoffs);
|
|
chap->pic_xoffs = xoffs;
|
|
chap->pic_yoffs = yoffs;
|
|
}
|
|
|
|
void Character_LoseInventory(CharacterInfo *chap, ScriptInvItem *invi) {
|
|
|
|
if (invi == nullptr)
|
|
quit("!LoseInventoryFromCharacter: invalid inventory number");
|
|
|
|
int inum = invi->id;
|
|
|
|
if (chap->inv[inum] > 0)
|
|
chap->inv[inum]--;
|
|
|
|
if ((chap->activeinv == inum) & (chap->inv[inum] < 1)) {
|
|
chap->activeinv = -1;
|
|
if ((chap == _G(playerchar)) && (GetCursorMode() == MODE_USE))
|
|
set_cursor_mode(0);
|
|
}
|
|
|
|
int charid = chap->index_id;
|
|
|
|
if ((chap->inv[inum] == 0) || (_GP(game).options[OPT_DUPLICATEINV] > 0)) {
|
|
int xx, tt;
|
|
for (xx = 0; xx < _GP(charextra)[charid].invorder_count; xx++) {
|
|
if (_GP(charextra)[charid].invorder[xx] == inum) {
|
|
_GP(charextra)[charid].invorder_count--;
|
|
for (tt = xx; tt < _GP(charextra)[charid].invorder_count; tt++)
|
|
_GP(charextra)[charid].invorder[tt] = _GP(charextra)[charid].invorder[tt + 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
GUI::MarkInventoryForUpdate(charid, charid == _GP(game).playercharacter);
|
|
|
|
if (chap == _G(playerchar))
|
|
run_on_event(GE_LOSE_INV, RuntimeScriptValue().SetInt32(inum));
|
|
}
|
|
|
|
void Character_PlaceOnWalkableArea(CharacterInfo *chap) {
|
|
if (_G(displayed_room) < 0)
|
|
quit("!Character.PlaceOnWalkableArea: no room is currently loaded");
|
|
|
|
find_nearest_walkable_area(&chap->x, &chap->y);
|
|
}
|
|
|
|
void Character_RemoveTint(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & (CHF_HASTINT | CHF_HASLIGHT)) {
|
|
debug_script_log("Un-tint %s", chaa->scrname);
|
|
chaa->flags &= ~(CHF_HASTINT | CHF_HASLIGHT);
|
|
} else {
|
|
debug_script_warn("Character.RemoveTint called but character was not tinted");
|
|
}
|
|
}
|
|
|
|
int Character_GetHasExplicitTint_Old(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint() || ch->has_explicit_light();
|
|
}
|
|
|
|
int Character_GetHasExplicitTint(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint();
|
|
}
|
|
|
|
void Character_Say(CharacterInfo *chaa, const char *text) {
|
|
_DisplaySpeechCore(chaa->index_id, text);
|
|
}
|
|
|
|
void Character_SayAt(CharacterInfo *chaa, int x, int y, int width, const char *texx) {
|
|
|
|
DisplaySpeechAt(x, y, width, chaa->index_id, texx);
|
|
}
|
|
|
|
ScriptOverlay *Character_SayBackground(CharacterInfo *chaa, const char *texx) {
|
|
int ovltype = DisplaySpeechBackground(chaa->index_id, texx);
|
|
auto *over = get_overlay(ovltype);
|
|
if (!over)
|
|
quit("!SayBackground internal error: no overlay");
|
|
|
|
// Create script object with an internal ref, keep at least until internal timeout
|
|
return create_scriptoverlay(*over, true);
|
|
}
|
|
|
|
void Character_SetAsPlayer(CharacterInfo *chaa) {
|
|
|
|
// Set to same character, so ignore.
|
|
// But only on versions > 2.61. The relevant entry in the 2.62 changelog is:
|
|
// - Fixed SetPlayerCharacter to do nothing at all if you pass the current
|
|
// player character to it (previously it was resetting the inventory layout)
|
|
if ((_G(loaded_game_file_version) > kGameVersion_261) && (_GP(game).playercharacter == chaa->index_id))
|
|
return;
|
|
|
|
setup_player_character(chaa->index_id);
|
|
GUI::MarkInventoryForUpdate(_GP(game).playercharacter, true);
|
|
debug_script_log("%s is new player character", _G(playerchar)->scrname);
|
|
|
|
// Within game_start, return now
|
|
if (_G(displayed_room) < 0)
|
|
return;
|
|
|
|
// Ignore invalid room numbers for the character and just place him in
|
|
// the current room for 2.x. Following script calls to NewRoom() will
|
|
// make sure this still works as intended.
|
|
if ((_G(loaded_game_file_version) <= kGameVersion_272) && (_G(playerchar)->room < 0))
|
|
_G(playerchar)->room = _G(displayed_room);
|
|
|
|
if (_G(displayed_room) != _G(playerchar)->room)
|
|
NewRoom(_G(playerchar)->room);
|
|
else // make sure it doesn't run the region interactions
|
|
_GP(play).player_on_region = GetRegionIDAtRoom(_G(playerchar)->x, _G(playerchar)->y);
|
|
|
|
if ((_G(playerchar)->activeinv >= 0) && (_G(playerchar)->inv[_G(playerchar)->activeinv] < 1))
|
|
_G(playerchar)->activeinv = -1;
|
|
|
|
// They had inv selected, so change the cursor
|
|
if (_G(cur_mode) == MODE_USE) {
|
|
if (_G(playerchar)->activeinv < 0)
|
|
SetNextCursor();
|
|
else
|
|
SetActiveInventory(_G(playerchar)->activeinv);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void Character_SetIdleView(CharacterInfo *chaa, int iview, int itime) {
|
|
|
|
if (iview == 1)
|
|
quit("!SetCharacterIdle: view 1 cannot be used as an idle view, sorry.");
|
|
|
|
// if an idle anim is currently playing, release it
|
|
stop_character_idling(chaa);
|
|
|
|
chaa->idleview = iview - 1;
|
|
// make sure they don't appear idle while idle anim is disabled
|
|
if (iview < 1)
|
|
itime = 10;
|
|
chaa->idletime = itime;
|
|
chaa->idleleft = itime;
|
|
|
|
// if not currently animating, reset the wait counter
|
|
if ((chaa->animating == 0) && (chaa->walking == 0))
|
|
chaa->wait = 0;
|
|
|
|
if (iview >= 1) {
|
|
debug_script_log("Set %s idle view to %d (time %d)", chaa->scrname, iview, itime);
|
|
} else {
|
|
debug_script_log("%s idle view disabled", chaa->scrname);
|
|
}
|
|
if (chaa->flags & CHF_FIXVIEW) {
|
|
debug_script_warn("SetCharacterIdle called while character view locked with SetCharacterView; idle ignored");
|
|
debug_script_log("View locked, idle will not kick in until Released");
|
|
}
|
|
// if they switch to a swimming animation, kick it off immediately
|
|
if (itime == 0)
|
|
_GP(charextra)[chaa->index_id].process_idle_this_time = 1;
|
|
|
|
}
|
|
|
|
bool Character_GetHasExplicitLight(CharacterInfo *ch) {
|
|
return ch->has_explicit_light();
|
|
}
|
|
|
|
int Character_GetLightLevel(CharacterInfo *ch) {
|
|
return ch->has_explicit_light() ? _GP(charextra)[ch->index_id].tint_light : 0;
|
|
}
|
|
|
|
void Character_SetLightLevel(CharacterInfo *chaa, int light_level) {
|
|
light_level = Math::Clamp(light_level, -100, 100);
|
|
|
|
_GP(charextra)[chaa->index_id].tint_light = light_level;
|
|
chaa->flags &= ~CHF_HASTINT;
|
|
chaa->flags |= CHF_HASLIGHT;
|
|
}
|
|
|
|
int Character_GetTintRed(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_r : 0;
|
|
}
|
|
|
|
int Character_GetTintGreen(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_g : 0;
|
|
}
|
|
|
|
int Character_GetTintBlue(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_b : 0;
|
|
}
|
|
|
|
int Character_GetTintSaturation(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint() ? _GP(charextra)[ch->index_id].tint_level : 0;
|
|
}
|
|
|
|
int Character_GetTintLuminance(CharacterInfo *ch) {
|
|
return ch->has_explicit_tint() ? ((_GP(charextra)[ch->index_id].tint_light * 10) / 25) : 0;
|
|
}
|
|
|
|
void Character_SetOption(CharacterInfo *chaa, int flag, int yesorno) {
|
|
|
|
if ((yesorno < 0) || (yesorno > 1))
|
|
quit("!SetCharacterProperty: last parameter must be 0 or 1");
|
|
|
|
if (flag & CHF_MANUALSCALING) {
|
|
// backwards compatibility fix
|
|
Character_SetIgnoreScaling(chaa, yesorno);
|
|
} else {
|
|
chaa->flags &= ~flag;
|
|
if (yesorno)
|
|
chaa->flags |= flag;
|
|
}
|
|
}
|
|
|
|
void Character_SetSpeed(CharacterInfo *chaa, int xspeed, int yspeed) {
|
|
if ((xspeed == 0) || (yspeed == 0))
|
|
quit("!SetCharacterSpeedEx: invalid speed value");
|
|
if ((chaa->walking > 0) && (_G(loaded_game_file_version) < kGameVersion_361)) {
|
|
debug_script_warn("Character_SetSpeed: cannot change speed while walking");
|
|
return;
|
|
}
|
|
|
|
xspeed = Math::Clamp(xspeed, (int)INT16_MIN, (int)INT16_MAX);
|
|
yspeed = Math::Clamp(yspeed, (int)INT16_MIN, (int)INT16_MAX);
|
|
|
|
uint16_t old_speedx = chaa->walkspeed;
|
|
uint16_t old_speedy = ((chaa->walkspeed_y == UNIFORM_WALK_SPEED) ? chaa->walkspeed : chaa->walkspeed_y);
|
|
|
|
chaa->walkspeed = xspeed;
|
|
if (yspeed == xspeed)
|
|
chaa->walkspeed_y = UNIFORM_WALK_SPEED;
|
|
else
|
|
chaa->walkspeed_y = yspeed;
|
|
|
|
if (chaa->walking > 0) {
|
|
recalculate_move_speeds(&_GP(mls)[chaa->walking % TURNING_AROUND], old_speedx, old_speedy, xspeed, yspeed);
|
|
}
|
|
}
|
|
|
|
void Character_StopMoving(CharacterInfo *charp) {
|
|
|
|
int chaa = charp->index_id;
|
|
if (chaa == _GP(play).skip_until_char_stops)
|
|
EndSkippingUntilCharStops();
|
|
|
|
if (_GP(charextra)[chaa].xwas != INVALID_X) {
|
|
charp->x = _GP(charextra)[chaa].xwas;
|
|
charp->y = _GP(charextra)[chaa].ywas;
|
|
_GP(charextra)[chaa].xwas = INVALID_X;
|
|
}
|
|
if ((charp->walking > 0) && (charp->walking < TURNING_AROUND)) {
|
|
// if it's not a MoveCharDirect, make sure they end up on a walkable area
|
|
if ((_GP(mls)[charp->walking].direct == 0) && (charp->room == _G(displayed_room)))
|
|
Character_PlaceOnWalkableArea(charp);
|
|
|
|
debug_script_log("%s: stop moving", charp->scrname);
|
|
|
|
charp->idleleft = charp->idletime;
|
|
// restart the idle animation straight away
|
|
_GP(charextra)[chaa].process_idle_this_time = 1;
|
|
}
|
|
if (charp->walking) {
|
|
// If the character is currently moving, stop them and reset their frame
|
|
charp->walking = 0;
|
|
if ((charp->flags & CHF_MOVENOTWALK) == 0)
|
|
charp->frame = 0;
|
|
}
|
|
}
|
|
|
|
void Character_Tint(CharacterInfo *chaa, int red, int green, int blue, int opacity, int luminance) {
|
|
if ((red < 0) || (green < 0) || (blue < 0) ||
|
|
(red > 255) || (green > 255) || (blue > 255) ||
|
|
(opacity < 0) || (opacity > 100) ||
|
|
(luminance < 0) || (luminance > 100))
|
|
quit("!Character.Tint: invalid parameter. R,G,B must be 0-255, opacity & luminance 0-100");
|
|
|
|
debug_script_log("Set %s tint RGB(%d,%d,%d) %d%%", chaa->scrname, red, green, blue, opacity);
|
|
|
|
_GP(charextra)[chaa->index_id].tint_r = red;
|
|
_GP(charextra)[chaa->index_id].tint_g = green;
|
|
_GP(charextra)[chaa->index_id].tint_b = blue;
|
|
_GP(charextra)[chaa->index_id].tint_level = opacity;
|
|
_GP(charextra)[chaa->index_id].tint_light = (luminance * 25) / 10;
|
|
chaa->flags &= ~CHF_HASLIGHT;
|
|
chaa->flags |= CHF_HASTINT;
|
|
}
|
|
|
|
void Character_Think(CharacterInfo *chaa, const char *text) {
|
|
_DisplayThoughtCore(chaa->index_id, text);
|
|
}
|
|
|
|
void Character_UnlockView(CharacterInfo *chaa) {
|
|
Character_UnlockViewEx(chaa, STOP_MOVING);
|
|
}
|
|
|
|
void Character_UnlockViewEx(CharacterInfo *chaa, int stopMoving) {
|
|
if (chaa->flags & CHF_FIXVIEW) {
|
|
debug_script_log("%s: Released view back to default", chaa->scrname);
|
|
}
|
|
chaa->flags &= ~CHF_FIXVIEW;
|
|
chaa->view = chaa->defview;
|
|
chaa->frame = 0;
|
|
if (stopMoving != KEEP_MOVING) {
|
|
Character_StopMoving(chaa);
|
|
}
|
|
if (chaa->view >= 0) {
|
|
int maxloop = _GP(views)[chaa->view].numLoops;
|
|
if (((chaa->flags & CHF_NODIAGONAL) != 0) && (maxloop > 4))
|
|
maxloop = 4;
|
|
FindReasonableLoopForCharacter(chaa);
|
|
}
|
|
stop_character_anim(chaa);
|
|
chaa->idleleft = chaa->idletime;
|
|
chaa->pic_xoffs = 0;
|
|
chaa->pic_yoffs = 0;
|
|
// restart the idle animation straight away
|
|
_GP(charextra)[chaa->index_id].process_idle_this_time = 1;
|
|
|
|
}
|
|
|
|
|
|
void Character_Walk(CharacterInfo *chaa, int x, int y, int blocking, int direct) {
|
|
walk_or_move_character(chaa, x, y, blocking, direct, true);
|
|
}
|
|
|
|
void Character_Move(CharacterInfo *chaa, int x, int y, int blocking, int direct) {
|
|
walk_or_move_character(chaa, x, y, blocking, direct, false);
|
|
}
|
|
|
|
void Character_WalkStraight(CharacterInfo *chaa, int xx, int yy, int blocking) {
|
|
|
|
if (chaa->room != _G(displayed_room))
|
|
quitprintf("!MoveCharacterStraight: character %s is not in current room %d (it is in room %d)",
|
|
chaa->scrname, _G(displayed_room), chaa->room);
|
|
|
|
int movetox = xx, movetoy = yy;
|
|
|
|
set_wallscreen(prepare_walkable_areas(chaa->index_id));
|
|
|
|
int fromXLowres = room_to_mask_coord(chaa->x);
|
|
int fromYLowres = room_to_mask_coord(chaa->y);
|
|
int toXLowres = room_to_mask_coord(xx);
|
|
int toYLowres = room_to_mask_coord(yy);
|
|
|
|
if (!can_see_from(fromXLowres, fromYLowres, toXLowres, toYLowres)) {
|
|
int lastcx, lastcy;
|
|
get_lastcpos(lastcx, lastcy);
|
|
movetox = mask_to_room_coord(lastcx);
|
|
movetoy = mask_to_room_coord(lastcy);
|
|
}
|
|
|
|
walk_or_move_character(chaa, movetox, movetoy, blocking, 1 /* use ANYWHERE */, true);
|
|
}
|
|
|
|
void Character_RunInteraction(CharacterInfo *chaa, int mood) {
|
|
|
|
RunCharacterInteraction(chaa->index_id, mood);
|
|
}
|
|
|
|
|
|
|
|
// **** CHARACTER: PROPERTIES ****
|
|
|
|
int Character_GetProperty(CharacterInfo *chaa, const char *property) {
|
|
if (!AssertCharacter("Character.GetProperty", chaa->index_id))
|
|
return 0;
|
|
return get_int_property(_GP(game).charProps[chaa->index_id], _GP(play).charProps[chaa->index_id], property);
|
|
}
|
|
|
|
void Character_GetPropertyText(CharacterInfo *chaa, const char *property, char *bufer) {
|
|
if (!AssertCharacter("Character.GetPropertyText", chaa->index_id))
|
|
return;
|
|
get_text_property(_GP(game).charProps[chaa->index_id], _GP(play).charProps[chaa->index_id], property, bufer);
|
|
}
|
|
|
|
const char *Character_GetTextProperty(CharacterInfo *chaa, const char *property) {
|
|
if (!AssertCharacter("Character.GetTextProperty", chaa->index_id))
|
|
return nullptr;
|
|
return get_text_property_dynamic_string(_GP(game).charProps[chaa->index_id], _GP(play).charProps[chaa->index_id], property);
|
|
}
|
|
|
|
bool Character_SetProperty(CharacterInfo *chaa, const char *property, int value) {
|
|
if (!AssertCharacter("Character.SetProperty", chaa->index_id))
|
|
return false;
|
|
return set_int_property(_GP(play).charProps[chaa->index_id], property, value);
|
|
}
|
|
|
|
bool Character_SetTextProperty(CharacterInfo *chaa, const char *property, const char *value) {
|
|
if (!AssertCharacter("Character.SetTextProperty", chaa->index_id))
|
|
return false;
|
|
return set_text_property(_GP(play).charProps[chaa->index_id], property, value);
|
|
}
|
|
|
|
ScriptInvItem *Character_GetActiveInventory(CharacterInfo *chaa) {
|
|
|
|
if (chaa->activeinv <= 0)
|
|
return nullptr;
|
|
|
|
return &_G(scrInv)[chaa->activeinv];
|
|
}
|
|
|
|
void Character_SetActiveInventory(CharacterInfo *chaa, ScriptInvItem *iit) {
|
|
if (iit == nullptr) {
|
|
chaa->activeinv = -1;
|
|
|
|
if (chaa->index_id == _GP(game).playercharacter) {
|
|
|
|
if (GetCursorMode() == MODE_USE)
|
|
set_cursor_mode(0);
|
|
}
|
|
GUI::MarkInventoryForUpdate(chaa->index_id, chaa->index_id == _GP(game).playercharacter);
|
|
return;
|
|
}
|
|
|
|
if (chaa->inv[iit->id] < 1) {
|
|
debug_script_warn("SetActiveInventory: character doesn't have any of that inventory");
|
|
return;
|
|
}
|
|
|
|
chaa->activeinv = iit->id;
|
|
|
|
if (chaa->index_id == _GP(game).playercharacter) {
|
|
// if it's the player character, update mouse cursor
|
|
update_inv_cursor(iit->id);
|
|
set_cursor_mode(MODE_USE);
|
|
}
|
|
GUI::MarkInventoryForUpdate(chaa->index_id, chaa->index_id == _GP(game).playercharacter);
|
|
}
|
|
|
|
int Character_GetAnimating(CharacterInfo *chaa) {
|
|
if (chaa->animating)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int Character_GetAnimationSpeed(CharacterInfo *chaa) {
|
|
return chaa->animspeed;
|
|
}
|
|
|
|
void Character_SetAnimationSpeed(CharacterInfo *chaa, int newval) {
|
|
chaa->animspeed = newval;
|
|
if (_G(loaded_game_file_version) < kGameVersion_360_16)
|
|
chaa->idle_anim_speed = chaa->animspeed + 5;
|
|
}
|
|
|
|
int Character_GetAnimationVolume(CharacterInfo *chaa) {
|
|
return _GP(charextra)[chaa->index_id].anim_volume;
|
|
}
|
|
|
|
void Character_SetAnimationVolume(CharacterInfo *chaa, int newval) {
|
|
|
|
_GP(charextra)[chaa->index_id].anim_volume = Math::Clamp(newval, 0, 100);
|
|
}
|
|
|
|
int Character_GetBaseline(CharacterInfo *chaa) {
|
|
if (chaa->baseline < 1)
|
|
return 0;
|
|
|
|
return chaa->baseline;
|
|
}
|
|
|
|
void Character_SetBaseline(CharacterInfo *chaa, int basel) {
|
|
|
|
chaa->baseline = basel;
|
|
}
|
|
|
|
int Character_GetBlinkInterval(CharacterInfo *chaa) {
|
|
|
|
return chaa->blinkinterval;
|
|
}
|
|
|
|
void Character_SetBlinkInterval(CharacterInfo *chaa, int interval) {
|
|
|
|
if (interval < 0)
|
|
quit("!SetCharacterBlinkView: invalid blink interval");
|
|
|
|
chaa->blinkinterval = interval;
|
|
|
|
if (chaa->blinktimer > 0)
|
|
chaa->blinktimer = chaa->blinkinterval;
|
|
}
|
|
|
|
int Character_GetBlinkView(CharacterInfo *chaa) {
|
|
|
|
return chaa->blinkview + 1;
|
|
}
|
|
|
|
void Character_SetBlinkView(CharacterInfo *chaa, int vii) {
|
|
|
|
if (((vii < 2) || (vii > _GP(game).numviews)) && (vii != -1))
|
|
quit("!SetCharacterBlinkView: invalid view number");
|
|
|
|
chaa->blinkview = vii - 1;
|
|
}
|
|
|
|
int Character_GetBlinkWhileThinking(CharacterInfo *chaa) {
|
|
if (chaa->flags & CHF_NOBLINKANDTHINK)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void Character_SetBlinkWhileThinking(CharacterInfo *chaa, int yesOrNo) {
|
|
chaa->flags &= ~CHF_NOBLINKANDTHINK;
|
|
if (yesOrNo == 0)
|
|
chaa->flags |= CHF_NOBLINKANDTHINK;
|
|
}
|
|
|
|
int Character_GetBlockingHeight(CharacterInfo *chaa) {
|
|
|
|
return chaa->blocking_height;
|
|
}
|
|
|
|
void Character_SetBlockingHeight(CharacterInfo *chaa, int hit) {
|
|
|
|
chaa->blocking_height = hit;
|
|
}
|
|
|
|
int Character_GetBlockingWidth(CharacterInfo *chaa) {
|
|
|
|
return chaa->blocking_width;
|
|
}
|
|
|
|
void Character_SetBlockingWidth(CharacterInfo *chaa, int wid) {
|
|
|
|
chaa->blocking_width = wid;
|
|
}
|
|
|
|
int Character_GetDiagonalWalking(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_NODIAGONAL)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void Character_SetDiagonalWalking(CharacterInfo *chaa, int yesorno) {
|
|
|
|
chaa->flags &= ~CHF_NODIAGONAL;
|
|
if (!yesorno)
|
|
chaa->flags |= CHF_NODIAGONAL;
|
|
}
|
|
|
|
int Character_GetClickable(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_NOINTERACT)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void Character_SetClickable(CharacterInfo *chaa, int clik) {
|
|
|
|
chaa->flags &= ~CHF_NOINTERACT;
|
|
// if they don't want it clickable, set the relevant bit
|
|
if (clik == 0)
|
|
chaa->flags |= CHF_NOINTERACT;
|
|
}
|
|
|
|
int Character_GetID(CharacterInfo *chaa) {
|
|
|
|
return chaa->index_id;
|
|
|
|
}
|
|
|
|
const char *Character_GetScriptName(CharacterInfo *chin) {
|
|
return CreateNewScriptString(_GP(game).chars2[chin->index_id].scrname_new);
|
|
}
|
|
|
|
int Character_GetFrame(CharacterInfo *chaa) {
|
|
return chaa->frame;
|
|
}
|
|
|
|
void Character_SetFrame(CharacterInfo *chaa, int newval) {
|
|
chaa->frame = newval;
|
|
}
|
|
|
|
int Character_GetIdleView(CharacterInfo *chaa) {
|
|
|
|
if (chaa->idleview < 1)
|
|
return -1;
|
|
|
|
return chaa->idleview + 1;
|
|
}
|
|
|
|
int Character_GetIInventoryQuantity(CharacterInfo *chaa, int index) {
|
|
if ((index < 1) || (index >= _GP(game).numinvitems))
|
|
quitprintf("!Character.InventoryQuantity: invalid inventory index %d", index);
|
|
|
|
return chaa->inv[index];
|
|
}
|
|
|
|
int Character_HasInventory(CharacterInfo *chaa, ScriptInvItem *invi) {
|
|
if (invi == nullptr)
|
|
quit("!Character.HasInventory: NULL inventory item supplied");
|
|
|
|
return (chaa->inv[invi->id] > 0) ? 1 : 0;
|
|
}
|
|
|
|
void Character_SetIInventoryQuantity(CharacterInfo *chaa, int index, int quant) {
|
|
if ((index < 1) || (index >= _GP(game).numinvitems))
|
|
quitprintf("!Character.InventoryQuantity: invalid inventory index %d", index);
|
|
|
|
if ((quant < 0) || (quant > 32000))
|
|
quitprintf("!Character.InventoryQuantity: invalid quantity %d", quant);
|
|
|
|
chaa->inv[index] = quant;
|
|
}
|
|
|
|
int Character_GetIgnoreLighting(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_NOLIGHTING)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Character_SetIgnoreLighting(CharacterInfo *chaa, int yesorno) {
|
|
|
|
chaa->flags &= ~CHF_NOLIGHTING;
|
|
if (yesorno)
|
|
chaa->flags |= CHF_NOLIGHTING;
|
|
}
|
|
|
|
int Character_GetIgnoreScaling(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_MANUALSCALING)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Character_SetIgnoreScaling(CharacterInfo *chaa, int yesorno) {
|
|
|
|
if (yesorno) {
|
|
// when setting IgnoreScaling to 1, should reset zoom level
|
|
// like it used to in pre-2.71
|
|
_GP(charextra)[chaa->index_id].zoom = 100;
|
|
}
|
|
Character_SetManualScaling(chaa, yesorno);
|
|
}
|
|
|
|
void Character_SetManualScaling(CharacterInfo *chaa, int yesorno) {
|
|
|
|
chaa->flags &= ~CHF_MANUALSCALING;
|
|
if (yesorno)
|
|
chaa->flags |= CHF_MANUALSCALING;
|
|
}
|
|
|
|
int Character_GetIgnoreWalkbehinds(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_NOWALKBEHINDS)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Character_SetIgnoreWalkbehinds(CharacterInfo *chaa, int yesorno) {
|
|
if (_GP(game).options[OPT_BASESCRIPTAPI] >= kScriptAPI_v350)
|
|
debug_script_warn("IgnoreWalkbehinds is not recommended for use, consider other solutions");
|
|
chaa->flags &= ~CHF_NOWALKBEHINDS;
|
|
if (yesorno)
|
|
chaa->flags |= CHF_NOWALKBEHINDS;
|
|
}
|
|
|
|
int Character_GetMovementLinkedToAnimation(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_ANTIGLIDE)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Character_SetMovementLinkedToAnimation(CharacterInfo *chaa, int yesorno) {
|
|
|
|
chaa->flags &= ~CHF_ANTIGLIDE;
|
|
if (yesorno)
|
|
chaa->flags |= CHF_ANTIGLIDE;
|
|
}
|
|
|
|
int Character_GetLoop(CharacterInfo *chaa) {
|
|
return chaa->loop;
|
|
}
|
|
|
|
void Character_SetLoop(CharacterInfo *chaa, int newval) {
|
|
AssertLoop("Character.Loop", chaa->view, newval);
|
|
|
|
chaa->loop = newval;
|
|
if (chaa->frame >= _GP(views)[chaa->view].loops[chaa->loop].numFrames)
|
|
chaa->frame = 0;
|
|
}
|
|
|
|
int Character_GetMoving(CharacterInfo *chaa) {
|
|
if (chaa->walking)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int Character_GetDestinationX(CharacterInfo *chaa) {
|
|
if (chaa->walking) {
|
|
MoveList *cmls = &_GP(mls)[chaa->walking % TURNING_AROUND];
|
|
return cmls->pos[cmls->numstage - 1].X;
|
|
} else
|
|
return chaa->x;
|
|
}
|
|
|
|
int Character_GetDestinationY(CharacterInfo *chaa) {
|
|
if (chaa->walking) {
|
|
MoveList *cmls = &_GP(mls)[chaa->walking % TURNING_AROUND];
|
|
return cmls->pos[cmls->numstage - 1].Y;
|
|
} else
|
|
return chaa->y;
|
|
}
|
|
|
|
const char *Character_GetName(CharacterInfo *chaa) {
|
|
return CreateNewScriptString(_GP(game).chars2[chaa->index_id].name_new.GetCStr());
|
|
}
|
|
|
|
void Character_SetName(CharacterInfo *chaa, const char *newName) {
|
|
_GP(game).chars2[chaa->index_id].name_new = newName;
|
|
// Fill legacy name fields, for compatibility with old scripts and plugins
|
|
snprintf(chaa->name, LEGACY_MAX_CHAR_NAME_LEN, "%s", newName);
|
|
GUI::MarkSpecialLabelsForUpdate(kLabelMacro_Overhotspot);
|
|
}
|
|
|
|
int Character_GetNormalView(CharacterInfo *chaa) {
|
|
return chaa->defview + 1;
|
|
}
|
|
|
|
int Character_GetPreviousRoom(CharacterInfo *chaa) {
|
|
return chaa->prevroom;
|
|
}
|
|
|
|
int Character_GetRoom(CharacterInfo *chaa) {
|
|
return chaa->room;
|
|
}
|
|
|
|
|
|
int Character_GetScaleMoveSpeed(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_SCALEMOVESPEED)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Character_SetScaleMoveSpeed(CharacterInfo *chaa, int yesorno) {
|
|
|
|
if ((yesorno < 0) || (yesorno > 1))
|
|
quit("Character.ScaleMoveSpeed: value must be true or false (1 or 0)");
|
|
|
|
chaa->flags &= ~CHF_SCALEMOVESPEED;
|
|
if (yesorno)
|
|
chaa->flags |= CHF_SCALEMOVESPEED;
|
|
}
|
|
|
|
int Character_GetScaleVolume(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_SCALEVOLUME)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void Character_SetScaleVolume(CharacterInfo *chaa, int yesorno) {
|
|
|
|
if ((yesorno < 0) || (yesorno > 1))
|
|
quit("Character.ScaleVolume: value must be true or false (1 or 0)");
|
|
|
|
chaa->flags &= ~CHF_SCALEVOLUME;
|
|
if (yesorno)
|
|
chaa->flags |= CHF_SCALEVOLUME;
|
|
}
|
|
|
|
int Character_GetScaling(CharacterInfo *chaa) {
|
|
return _GP(charextra)[chaa->index_id].zoom;
|
|
}
|
|
|
|
void Character_SetScaling(CharacterInfo *chaa, int zoomlevel) {
|
|
|
|
if ((chaa->flags & CHF_MANUALSCALING) == 0) {
|
|
debug_script_warn("Character.Scaling: cannot set property unless ManualScaling is enabled");
|
|
return;
|
|
}
|
|
int zoom_fixed = Math::Clamp(zoomlevel, 1, (int)(INT16_MAX)); // CharacterExtras.zoom is int16
|
|
if (zoomlevel != zoom_fixed)
|
|
debug_script_warn("Character.Scaling: scaling level must be between 1 and %d%%, asked for: %d",
|
|
(int)(INT16_MAX), zoomlevel);
|
|
|
|
_GP(charextra)[chaa->index_id].zoom = zoom_fixed;
|
|
}
|
|
|
|
int Character_GetSolid(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_NOBLOCKING)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void Character_SetSolid(CharacterInfo *chaa, int yesorno) {
|
|
|
|
chaa->flags &= ~CHF_NOBLOCKING;
|
|
if (!yesorno)
|
|
chaa->flags |= CHF_NOBLOCKING;
|
|
}
|
|
|
|
int Character_GetSpeaking(CharacterInfo *chaa) {
|
|
if (get_character_currently_talking() == chaa->index_id)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Character_GetSpeechColor(CharacterInfo *chaa) {
|
|
|
|
return chaa->talkcolor;
|
|
}
|
|
|
|
void Character_SetSpeechColor(CharacterInfo *chaa, int ncol) {
|
|
|
|
chaa->talkcolor = ncol;
|
|
}
|
|
|
|
void Character_SetSpeechAnimationDelay(CharacterInfo *chaa, int newDelay) {
|
|
if (_GP(game).options[OPT_GLOBALTALKANIMSPD] != 0) {
|
|
debug_script_warn("Character.SpeechAnimationDelay cannot be set when global speech animation speed is enabled");
|
|
return;
|
|
}
|
|
|
|
chaa->speech_anim_speed = newDelay;
|
|
}
|
|
|
|
int Character_GetIdleAnimationDelay(CharacterInfo *chaa) {
|
|
return chaa->idle_anim_speed;
|
|
}
|
|
|
|
void Character_SetIdleAnimationDelay(CharacterInfo *chaa, int newDelay) {
|
|
chaa->idle_anim_speed = newDelay;
|
|
}
|
|
|
|
int Character_GetSpeechView(CharacterInfo *chaa) {
|
|
return chaa->talkview + 1;
|
|
}
|
|
|
|
void Character_SetSpeechView(CharacterInfo *chaa, int vii) {
|
|
if (vii == -1) {
|
|
chaa->talkview = -1;
|
|
return;
|
|
}
|
|
|
|
if ((vii < 1) || (vii > _GP(game).numviews))
|
|
quit("!SetCharacterSpeechView: invalid view number");
|
|
|
|
chaa->talkview = vii - 1;
|
|
}
|
|
|
|
bool Character_GetThinking(CharacterInfo *chaa) {
|
|
return _G(char_thinking) == chaa->index_id;
|
|
}
|
|
|
|
int Character_GetThinkingFrame(CharacterInfo *chaa) {
|
|
if (_G(char_thinking) == chaa->index_id)
|
|
return chaa->thinkview > 0 ? chaa->frame : -1;
|
|
|
|
debug_script_warn("Character.ThinkingFrame: character is not currently thinking");
|
|
return -1;
|
|
}
|
|
|
|
int Character_GetThinkView(CharacterInfo *chaa) {
|
|
|
|
return chaa->thinkview + 1;
|
|
}
|
|
|
|
void Character_SetThinkView(CharacterInfo *chaa, int vii) {
|
|
if (((vii < 2) || (vii > _GP(game).numviews)) && (vii != -1))
|
|
quit("!SetCharacterThinkView: invalid view number");
|
|
|
|
chaa->thinkview = vii - 1;
|
|
}
|
|
|
|
int Character_GetTransparency(CharacterInfo *chaa) {
|
|
|
|
return GfxDef::LegacyTrans255ToTrans100(chaa->transparency);
|
|
}
|
|
|
|
void Character_SetTransparency(CharacterInfo *chaa, int trans) {
|
|
|
|
if ((trans < 0) || (trans > 100))
|
|
quit("!SetCharTransparent: transparency value must be between 0 and 100");
|
|
|
|
chaa->transparency = GfxDef::Trans100ToLegacyTrans255(trans);
|
|
}
|
|
|
|
int Character_GetTurnBeforeWalking(CharacterInfo *chaa) {
|
|
|
|
if (chaa->flags & CHF_NOTURNING)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void Character_SetTurnBeforeWalking(CharacterInfo *chaa, int yesorno) {
|
|
|
|
chaa->flags &= ~CHF_NOTURNING;
|
|
if (!yesorno)
|
|
chaa->flags |= CHF_NOTURNING;
|
|
}
|
|
|
|
int Character_GetView(CharacterInfo *chaa) {
|
|
return chaa->view + 1;
|
|
}
|
|
|
|
int Character_GetWalkSpeedX(CharacterInfo *chaa) {
|
|
return chaa->walkspeed;
|
|
}
|
|
|
|
int Character_GetWalkSpeedY(CharacterInfo *chaa) {
|
|
if (chaa->walkspeed_y != UNIFORM_WALK_SPEED)
|
|
return chaa->walkspeed_y;
|
|
|
|
return chaa->walkspeed;
|
|
}
|
|
|
|
int Character_GetX(CharacterInfo *chaa) {
|
|
return chaa->x;
|
|
}
|
|
|
|
void Character_SetX(CharacterInfo *chaa, int newval) {
|
|
chaa->x = newval;
|
|
}
|
|
|
|
int Character_GetY(CharacterInfo *chaa) {
|
|
return chaa->y;
|
|
}
|
|
|
|
void Character_SetY(CharacterInfo *chaa, int newval) {
|
|
chaa->y = newval;
|
|
}
|
|
|
|
int Character_GetZ(CharacterInfo *chaa) {
|
|
return chaa->z;
|
|
}
|
|
|
|
void Character_SetZ(CharacterInfo *chaa, int newval) {
|
|
chaa->z = newval;
|
|
}
|
|
|
|
|
|
|
|
int Character_GetSpeakingFrame(CharacterInfo *chaa) {
|
|
|
|
if ((_G(face_talking) >= 0) && (_G(facetalkrepeat))) {
|
|
if (_G(facetalkchar)->index_id == chaa->index_id) {
|
|
return _G(facetalkframe);
|
|
}
|
|
} else if (_G(char_speaking) >= 0) {
|
|
if (_G(char_speaking) == chaa->index_id) {
|
|
return chaa->frame;
|
|
}
|
|
}
|
|
|
|
debug_script_warn("Character.SpeakingFrame: character is not currently speaking");
|
|
return -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
// order of loops to turn character in circle from down to down
|
|
int turnlooporder[8] = {0, 6, 1, 7, 3, 5, 2, 4};
|
|
|
|
void walk_character(int chac, int tox, int toy, int ignwal, bool autoWalkAnims) {
|
|
CharacterInfo *chin = &_GP(game).chars[chac];
|
|
if (chin->room != _G(displayed_room))
|
|
quitprintf("!MoveCharacter: character %s is not in current room %d (it is in room %d)",
|
|
chin->scrname, _G(displayed_room), chin->room);
|
|
|
|
chin->flags &= ~CHF_MOVENOTWALK;
|
|
|
|
if ((tox == chin->x) && (toy == chin->y)) {
|
|
StopMoving(chac);
|
|
debug_script_log("%s already at destination, not moving", chin->scrname);
|
|
return;
|
|
}
|
|
|
|
if ((chin->animating) && (autoWalkAnims))
|
|
stop_character_anim(chin);
|
|
// Stop idling anim
|
|
stop_character_idling(chin);
|
|
// 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;
|
|
float wasStepFrac = 0.f;
|
|
// if they are currently walking, save the current Wait
|
|
if (chin->walking) {
|
|
waitWas = chin->walkwait;
|
|
animWaitWas = _GP(charextra)[chac].animwait;
|
|
const auto &movelist = _GP(mls)[chin->walking % TURNING_AROUND];
|
|
// We set (fraction + 1), because movelist is always +1 ahead of current character pos;
|
|
if (movelist.onpart > 0.f)
|
|
wasStepFrac = movelist.GetPixelUnitFraction() + movelist.GetStepLength();
|
|
}
|
|
|
|
StopMoving(chac);
|
|
chin->frame = oldframe;
|
|
// use toxPassedIn cached variable so the hi-res co-ordinates
|
|
// are still displayed as such
|
|
debug_script_log("%s: Start move to %d,%d", chin->scrname, tox, toy);
|
|
|
|
int move_speed_x, move_speed_y;
|
|
chin->get_effective_walkspeeds(move_speed_x, move_speed_y);
|
|
if ((move_speed_x == 0) && (move_speed_y == 0)) {
|
|
debug_script_warn("MoveCharacter: called for '%s' with walk speed 0", chin->scrname);
|
|
}
|
|
|
|
// Convert src and dest coords to the mask resolution, for pathfinder
|
|
const int src_x = room_to_mask_coord(chin->x);
|
|
const int src_y = room_to_mask_coord(chin->y);
|
|
const int dst_x = room_to_mask_coord(tox);
|
|
const int dst_y = room_to_mask_coord(toy);
|
|
|
|
int mslot = find_route(src_x, src_y, dst_x, dst_y, move_speed_x, move_speed_y, prepare_walkable_areas(chac), chac + CHMLSOFFS, 1, ignwal);
|
|
|
|
if (mslot > 0) {
|
|
chin->walking = mslot;
|
|
_GP(mls)[mslot].direct = ignwal;
|
|
convert_move_path_to_room_resolution(&_GP(mls)[mslot]);
|
|
|
|
if (wasStepFrac > 0.f) {
|
|
_GP(mls)[mslot].SetPixelUnitFraction(wasStepFrac);
|
|
}
|
|
|
|
// 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;
|
|
_GP(charextra)[chac].animwait = animWaitWas;
|
|
|
|
if (_GP(mls)[mslot].pos[0] != _GP(mls)[mslot].pos[1]) {
|
|
fix_player_sprite(&_GP(mls)[mslot], chin);
|
|
}
|
|
} else
|
|
chin->flags |= CHF_MOVENOTWALK;
|
|
} else if (autoWalkAnims) // pathfinder couldn't get a route, stand them still
|
|
chin->frame = 0;
|
|
}
|
|
|
|
int find_looporder_index(int curloop) {
|
|
int rr;
|
|
for (rr = 0; rr < 8; rr++) {
|
|
if (turnlooporder[rr] == curloop)
|
|
return rr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// returns 0 to use diagonal, 1 to not
|
|
int useDiagonal(CharacterInfo *char1) {
|
|
if ((_GP(views)[char1->view].numLoops < 8) || ((char1->flags & CHF_NODIAGONAL) != 0))
|
|
return 1;
|
|
// If they have just provided standing frames for loops 4-7, to
|
|
// provide smoother turning
|
|
if (_GP(views)[char1->view].loops[4].numFrames < 2)
|
|
return 2;
|
|
return 0;
|
|
}
|
|
|
|
// returns 1 normally, or 0 if they only have horizontal animations
|
|
int hasUpDownLoops(CharacterInfo *char1) {
|
|
// if no loops in the Down animation
|
|
// or no loops in the Up animation
|
|
if ((_GP(views)[char1->view].loops[0].numFrames < 1) ||
|
|
(_GP(views)[char1->view].numLoops < 4) ||
|
|
(_GP(views)[char1->view].loops[3].numFrames < 1)) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void start_character_turning(CharacterInfo *chinf, int useloop, int no_diagonal) {
|
|
// work out how far round they have to turn
|
|
int fromidx = find_looporder_index(chinf->loop);
|
|
int toidx = find_looporder_index(useloop);
|
|
//Display("Curloop: %d, needloop: %d",chinf->loop, useloop);
|
|
int ii, go_anticlock = 0;
|
|
// work out whether anticlockwise is quicker or not
|
|
if ((toidx > fromidx) && ((toidx - fromidx) > 4))
|
|
go_anticlock = 1;
|
|
if ((toidx < fromidx) && ((fromidx - toidx) < 4))
|
|
go_anticlock = 1;
|
|
// strip any current turning_around stages
|
|
chinf->walking = chinf->walking % TURNING_AROUND;
|
|
if (go_anticlock)
|
|
chinf->walking += TURNING_BACKWARDS;
|
|
else
|
|
go_anticlock = -1;
|
|
|
|
// Allow the diagonal frames just for turning
|
|
if (no_diagonal == 2)
|
|
no_diagonal = 0;
|
|
|
|
for (ii = fromidx; ii != toidx; ii -= go_anticlock) {
|
|
// Wrap the loop order into range [0-7]
|
|
if (ii < 0)
|
|
ii = 7;
|
|
if (ii >= 8)
|
|
ii = 0;
|
|
if (ii == toidx)
|
|
break;
|
|
if ((turnlooporder[ii] >= 4) && (no_diagonal > 0))
|
|
continue; // there are no diagonal loops
|
|
if (turnlooporder[ii] >= _GP(views)[chinf->view].numLoops)
|
|
continue; // no such loop
|
|
if (_GP(views)[chinf->view].loops[turnlooporder[ii]].numFrames < 1)
|
|
continue; // no frames in such loop
|
|
chinf->walking += TURNING_AROUND;
|
|
}
|
|
|
|
}
|
|
|
|
void fix_player_sprite(MoveList *cmls, CharacterInfo *chinf) {
|
|
const fixed xpmove = cmls->xpermove[cmls->onstage];
|
|
const fixed ypmove = cmls->ypermove[cmls->onstage];
|
|
|
|
// if not moving, do nothing
|
|
if ((xpmove == 0) && (ypmove == 0))
|
|
return;
|
|
|
|
const int useloop = GetDirectionalLoop(chinf, xpmove, ypmove);
|
|
|
|
if ((_GP(game).options[OPT_ROTATECHARS] == 0) || ((chinf->flags & CHF_NOTURNING) != 0)) {
|
|
chinf->loop = useloop;
|
|
return;
|
|
}
|
|
if ((chinf->loop > kDirLoop_LastOrthogonal) && ((chinf->flags & CHF_NODIAGONAL) != 0)) {
|
|
// They've just been playing an animation with an extended loop number,
|
|
// so don't try and rotate using it
|
|
chinf->loop = useloop;
|
|
return;
|
|
}
|
|
if ((chinf->loop >= _GP(views)[chinf->view].numLoops) ||
|
|
(_GP(views)[chinf->view].loops[chinf->loop].numFrames < 1) ||
|
|
(hasUpDownLoops(chinf) == 0)) {
|
|
// Character is not currently on a valid loop, so don't try to rotate
|
|
// eg. left/right only view, but current loop 0
|
|
chinf->loop = useloop;
|
|
return;
|
|
}
|
|
const int no_diagonal = useDiagonal(chinf);
|
|
start_character_turning(chinf, useloop, no_diagonal);
|
|
}
|
|
|
|
// Check whether two characters have walked into each other
|
|
int has_hit_another_character(int sourceChar) {
|
|
|
|
// if the character who's moving doesn't Bitmap *, don't bother checking
|
|
if (_GP(game).chars[sourceChar].flags & CHF_NOBLOCKING)
|
|
return -1;
|
|
|
|
for (int ww = 0; ww < _GP(game).numcharacters; ww++) {
|
|
if (_GP(game).chars[ww].on != 1) continue;
|
|
if (_GP(game).chars[ww].room != _G(displayed_room)) continue;
|
|
if (ww == sourceChar) continue;
|
|
if (_GP(game).chars[ww].flags & CHF_NOBLOCKING) continue;
|
|
|
|
if (is_char_on_another(sourceChar, ww, nullptr, nullptr)) {
|
|
// we are now overlapping character 'ww'
|
|
if ((_GP(game).chars[ww].walking) &&
|
|
((_GP(game).chars[ww].flags & CHF_AWAITINGMOVE) == 0))
|
|
return ww;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Does the next move from the character's movelist.
|
|
// Returns 1 if they are now waiting for another char to move,
|
|
// otherwise returns 0
|
|
int doNextCharMoveStep(CharacterInfo *chi, int &char_index, CharacterExtras *chex) {
|
|
int ntf = 0, xwas = chi->x, ywas = chi->y;
|
|
|
|
if (do_movelist_move(chi->walking, chi->x, chi->y) == 2) {
|
|
if ((chi->flags & CHF_MOVENOTWALK) == 0)
|
|
fix_player_sprite(&_GP(mls)[chi->walking], chi);
|
|
}
|
|
|
|
ntf = has_hit_another_character(char_index);
|
|
if (ntf >= 0) {
|
|
chi->walkwait = 30;
|
|
if (_GP(game).chars[ntf].walkspeed < 5)
|
|
chi->walkwait += (5 - _GP(game).chars[ntf].walkspeed) * 5;
|
|
// we are now waiting for the other char to move, so
|
|
// make sure he doesn't stop for us too
|
|
|
|
chi->flags |= CHF_AWAITINGMOVE;
|
|
|
|
if ((chi->flags & CHF_MOVENOTWALK) == 0) {
|
|
chi->frame = 0;
|
|
chex->animwait = chi->walkwait;
|
|
}
|
|
|
|
if ((chi->walking < 1) || (chi->walking >= TURNING_AROUND)) ;
|
|
else if (_GP(mls)[chi->walking].onpart > 0.f) {
|
|
_GP(mls)[chi->walking].onpart -= 1.f;
|
|
chi->x = xwas;
|
|
chi->y = ywas;
|
|
}
|
|
debug_script_log("%s: Bumped into %s, waiting for them to move", chi->scrname, _GP(game).chars[ntf].scrname);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool is_char_walking_ndirect(CharacterInfo *chi) {
|
|
return ((chi->walking > 0) && (chi->walking < TURNING_AROUND)) &&
|
|
(_GP(mls)[chi->walking].direct == 0);
|
|
}
|
|
|
|
int find_nearest_walkable_area_within(int *xx, int *yy, int range, int step) {
|
|
int ex, ey, nearest = 99999, thisis, nearx = 0, neary = 0;
|
|
int startx = 0, starty = 14;
|
|
int roomWidthLowRes = room_to_mask_coord(_GP(thisroom).Width);
|
|
int roomHeightLowRes = room_to_mask_coord(_GP(thisroom).Height);
|
|
int xwidth = roomWidthLowRes, yheight = roomHeightLowRes;
|
|
|
|
int xLowRes = room_to_mask_coord(xx[0]);
|
|
int yLowRes = room_to_mask_coord(yy[0]);
|
|
int rightEdge = room_to_mask_coord(_GP(thisroom).Edges.Right);
|
|
int leftEdge = room_to_mask_coord(_GP(thisroom).Edges.Left);
|
|
int topEdge = room_to_mask_coord(_GP(thisroom).Edges.Top);
|
|
int bottomEdge = room_to_mask_coord(_GP(thisroom).Edges.Bottom);
|
|
|
|
// tweak because people forget to move the edges sometimes
|
|
// if the player is already over the edge, ignore it
|
|
if (xLowRes >= rightEdge) rightEdge = roomWidthLowRes;
|
|
if (xLowRes <= leftEdge) leftEdge = 0;
|
|
if (yLowRes >= bottomEdge) bottomEdge = roomHeightLowRes;
|
|
if (yLowRes <= topEdge) topEdge = 0;
|
|
|
|
if (range > 0) {
|
|
startx = xLowRes - range;
|
|
starty = yLowRes - range;
|
|
xwidth = startx + range * 2;
|
|
yheight = starty + range * 2;
|
|
if (startx < 0) startx = 0;
|
|
if (starty < 10) starty = 10;
|
|
if (xwidth > roomWidthLowRes) xwidth = roomWidthLowRes;
|
|
if (yheight > roomHeightLowRes) yheight = roomHeightLowRes;
|
|
}
|
|
|
|
for (ex = startx; ex < xwidth; ex += step) {
|
|
for (ey = starty; ey < yheight; ey += step) {
|
|
// non-walkalbe, so don't go here
|
|
if (_GP(thisroom).WalkAreaMask->GetPixel(ex, ey) == 0) continue;
|
|
// off a screen edge, don't move them there
|
|
if ((ex <= leftEdge) || (ex >= rightEdge) ||
|
|
(ey <= topEdge) || (ey >= bottomEdge))
|
|
continue;
|
|
// otherwise, calculate distance from target
|
|
thisis = (int) ::sqrt((double)((ex - xLowRes) * (ex - xLowRes) + (ey - yLowRes) * (ey - yLowRes)));
|
|
if (thisis < nearest) {
|
|
nearest = thisis;
|
|
nearx = ex;
|
|
neary = ey;
|
|
}
|
|
}
|
|
}
|
|
if (nearest < 90000) {
|
|
xx[0] = mask_to_room_coord(nearx);
|
|
yy[0] = mask_to_room_coord(neary);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void find_nearest_walkable_area(int *xx, int *yy) {
|
|
|
|
int pixValue = _GP(thisroom).WalkAreaMask->GetPixel(room_to_mask_coord(xx[0]), room_to_mask_coord(yy[0]));
|
|
// only fix this code if the game was built with 2.61 or above
|
|
if (pixValue == 0 || (_G(loaded_game_file_version) >= kGameVersion_261 && pixValue < 1)) {
|
|
// First, check every 2 pixels within immediate area
|
|
if (!find_nearest_walkable_area_within(xx, yy, 20, 2)) {
|
|
// If not, check whole screen at 5 pixel intervals
|
|
find_nearest_walkable_area_within(xx, yy, -1, 5);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void FindReasonableLoopForCharacter(CharacterInfo *chap) {
|
|
|
|
if (chap->loop >= _GP(views)[chap->view].numLoops)
|
|
chap->loop = kDirLoop_Default;
|
|
if (_GP(views)[chap->view].numLoops < 1)
|
|
quitprintf("!View %d does not have any loops", chap->view + 1);
|
|
|
|
// if the current loop has no frames, find one that does
|
|
if (_GP(views)[chap->view].loops[chap->loop].numFrames < 1) {
|
|
for (int i = 0; i < _GP(views)[chap->view].numLoops; i++) {
|
|
if (_GP(views)[chap->view].loops[i].numFrames > 0) {
|
|
chap->loop = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void walk_or_move_character(CharacterInfo *chaa, int x, int y, int blocking, int direct, bool isWalk) {
|
|
if (chaa->on != 1) {
|
|
debug_script_warn("MoveCharacterBlocking: character is turned off and cannot be moved");
|
|
return;
|
|
}
|
|
|
|
if ((direct == ANYWHERE) || (direct == 1))
|
|
walk_character(chaa->index_id, x, y, 1, isWalk);
|
|
else if ((direct == WALKABLE_AREAS) || (direct == 0))
|
|
walk_character(chaa->index_id, x, y, 0, isWalk);
|
|
else
|
|
quit("!Character.Walk: Direct must be ANYWHERE or WALKABLE_AREAS");
|
|
|
|
if ((blocking == BLOCKING) || (blocking == 1))
|
|
GameLoopUntilNotMoving(&chaa->walking);
|
|
else if ((blocking != IN_BACKGROUND) && (blocking != 0))
|
|
quit("!Character.Walk: Blocking must be BLOCKING or IN_BACKGROUND");
|
|
|
|
}
|
|
|
|
int wantMoveNow(CharacterInfo *chi, CharacterExtras *chex) {
|
|
// check most likely case first
|
|
if ((chex->zoom == 100) || ((chi->flags & CHF_SCALEMOVESPEED) == 0))
|
|
return 1;
|
|
|
|
// the % checks don't work when the counter is negative, so once
|
|
// it wraps round, correct it
|
|
while (chi->walkwaitcounter < 0) {
|
|
chi->walkwaitcounter += 12000;
|
|
}
|
|
|
|
// scaling 170-200%, move 175% speed
|
|
if (chex->zoom >= 170) {
|
|
if ((chi->walkwaitcounter % 4) >= 1)
|
|
return 2;
|
|
else
|
|
return 1;
|
|
}
|
|
// scaling 140-170%, move 150% speed
|
|
else if (chex->zoom >= 140) {
|
|
if ((chi->walkwaitcounter % 2) == 1)
|
|
return 2;
|
|
else
|
|
return 1;
|
|
}
|
|
// scaling 115-140%, move 125% speed
|
|
else if (chex->zoom >= 115) {
|
|
if ((chi->walkwaitcounter % 4) >= 3)
|
|
return 2;
|
|
else
|
|
return 1;
|
|
}
|
|
// scaling 80-120%, normal speed
|
|
else if (chex->zoom >= 80)
|
|
return 1;
|
|
// scaling 60-80%, move 75% speed
|
|
if (chex->zoom >= 60) {
|
|
if ((chi->walkwaitcounter % 4) >= 1)
|
|
return -1;
|
|
else if (chex->xwas != INVALID_X) {
|
|
// move the second half of the movement to make it smoother
|
|
chi->x = chex->xwas;
|
|
chi->y = chex->ywas;
|
|
chex->xwas = INVALID_X;
|
|
}
|
|
}
|
|
// scaling 30-60%, move 50% speed
|
|
else if (chex->zoom >= 30) {
|
|
if ((chi->walkwaitcounter % 2) == 1)
|
|
return -1;
|
|
else if (chex->xwas != INVALID_X) {
|
|
// move the second half of the movement to make it smoother
|
|
chi->x = chex->xwas;
|
|
chi->y = chex->ywas;
|
|
chex->xwas = INVALID_X;
|
|
}
|
|
}
|
|
// scaling 0-30%, move 25% speed
|
|
else {
|
|
if ((chi->walkwaitcounter % 4) >= 3)
|
|
return -1;
|
|
if (((chi->walkwaitcounter % 4) == 1) && (chex->xwas != INVALID_X)) {
|
|
// move the second half of the movement to make it smoother
|
|
chi->x = chex->xwas;
|
|
chi->y = chex->ywas;
|
|
chex->xwas = INVALID_X;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void setup_player_character(int charid) {
|
|
_GP(game).playercharacter = charid;
|
|
_G(playerchar) = &_GP(game).chars[charid];
|
|
_G(sc_PlayerCharPtr) = ccGetObjectHandleFromAddress(_G(playerchar));
|
|
if (_G(loaded_game_file_version) < kGameVersion_270) {
|
|
ccAddExternalScriptObject("player", _G(playerchar), &_GP(ccDynamicCharacter));
|
|
}
|
|
}
|
|
|
|
// Animate character internal implementation;
|
|
// this function may be called by the game logic too, so we assume
|
|
// the arguments must be correct, and do not fix them up as we do for API functions.
|
|
void animate_character(CharacterInfo *chap, int loopn, int sppd, int rept, int direction, int sframe, int volume) {
|
|
if ((chap->view < 0) || (chap->view > _GP(game).numviews) ||
|
|
(loopn < 0) || (loopn >= _GP(views)[chap->view].numLoops)) {
|
|
quitprintf("!AnimateCharacter: invalid view and/or loop\n"
|
|
"(trying to animate '%s' using view %d (range is 1..%d) and loop %d (view has %d loops)).",
|
|
chap->scrname, chap->view + 1, _GP(game).numviews, loopn, _GP(views)[chap->view].numLoops);
|
|
}
|
|
// NOTE: there's always frame 0 allocated for safety
|
|
sframe = std::max(0, std::min(sframe, _GP(views)[chap->view].loops[loopn].numFrames - 1));
|
|
debug_script_log("%s: Start anim view %d loop %d, spd %d, repeat %d, frame: %d",
|
|
chap->scrname, chap->view + 1, loopn, sppd, rept, sframe);
|
|
|
|
Character_StopMoving(chap);
|
|
|
|
chap->set_animating(rept != 0, direction == 0, sppd);
|
|
chap->loop = loopn;
|
|
chap->frame = SetFirstAnimFrame(chap->view, loopn, sframe, direction);
|
|
|
|
chap->wait = sppd + _GP(views)[chap->view].loops[loopn].frames[chap->frame].speed;
|
|
_GP(charextra)[chap->index_id].cur_anim_volume = Math::Clamp(volume, 0, 100);
|
|
|
|
_GP(charextra)[chap->index_id].CheckViewFrame(chap);
|
|
}
|
|
|
|
void stop_character_anim(CharacterInfo *chap) { // TODO: may expand with resetting more properties,
|
|
// but have to be careful to not break logic somewhere
|
|
chap->animating = 0;
|
|
_GP(charextra)[chap->index_id].cur_anim_volume = 100;
|
|
}
|
|
|
|
int GetCharacterFrameVolume(CharacterInfo *chi) {
|
|
// We view the audio property relation as the relation of the entities:
|
|
// system -> audio type -> audio emitter (character) -> animation's audio
|
|
// therefore the sound volume is a multiplication of factors.
|
|
int frame_vol = 100; // default to full volume
|
|
// Try the active animation volume
|
|
if (_GP(charextra)[chi->index_id].cur_anim_volume >= 0)
|
|
frame_vol = _GP(charextra)[chi->index_id].cur_anim_volume;
|
|
// Try the character's own animation volume property
|
|
if (_GP(charextra)[chi->index_id].anim_volume >= 0)
|
|
frame_vol = frame_vol * _GP(charextra)[chi->index_id].anim_volume / 100;
|
|
// Try the character's zoom volume scaling (optional)
|
|
// NOTE: historically scales only in 0-100 range :/
|
|
if (chi->flags & CHF_SCALEVOLUME) {
|
|
int zoom_level = _GP(charextra)[chi->index_id].zoom;
|
|
if (zoom_level <= 0)
|
|
zoom_level = 100;
|
|
else
|
|
zoom_level = std::min(zoom_level, 100);
|
|
frame_vol = frame_vol * zoom_level / 100;
|
|
}
|
|
return frame_vol;
|
|
}
|
|
|
|
Bitmap *GetCharacterImage(int charid, bool *is_original) {
|
|
// NOTE: the cached image will only be present in software render mode
|
|
Bitmap *actsp = get_cached_character_image(charid);
|
|
if (is_original)
|
|
*is_original = !actsp; // no cached means we use original sprite
|
|
if (actsp)
|
|
return actsp;
|
|
CharacterInfo *chin = &_GP(game).chars[charid];
|
|
int sppic = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
|
|
return _GP(spriteset)[sppic];
|
|
}
|
|
|
|
CharacterInfo *GetCharacterAtScreen(int xx, int yy) {
|
|
int hsnum = GetCharIDAtScreen(xx, yy);
|
|
if (hsnum < 0)
|
|
return nullptr;
|
|
return &_GP(game).chars[hsnum];
|
|
}
|
|
|
|
CharacterInfo *GetCharacterAtRoom(int x, int y) {
|
|
int hsnum = is_pos_on_character(x, y);
|
|
if (hsnum < 0)
|
|
return nullptr;
|
|
return &_GP(game).chars[hsnum];
|
|
}
|
|
|
|
void update_character_scale(int charid) {
|
|
// Test for valid view and loop
|
|
CharacterInfo &chin = _GP(game).chars[charid];
|
|
if (chin.on == 0 || chin.room != _G(displayed_room))
|
|
return; // not enabled, or in a different room
|
|
|
|
CharacterExtras &chex = _GP(charextra)[charid];
|
|
if (chin.view < 0) {
|
|
quitprintf("!The character '%s' was turned on in the current room (room %d) but has not been assigned a view number.",
|
|
chin.scrname, _G(displayed_room));
|
|
}
|
|
if (chin.loop >= _GP(views)[chin.view].numLoops) {
|
|
quitprintf("!The character '%s' could not be displayed because there was no loop %d of view %d.",
|
|
chin.scrname, chin.loop, chin.view + 1);
|
|
}
|
|
// If frame is too high -- fallback to the frame 0;
|
|
// there's always at least 1 dummy frame at index 0
|
|
if (chin.frame >= _GP(views)[chin.view].loops[chin.loop].numFrames) {
|
|
chin.frame = 0;
|
|
}
|
|
|
|
int zoom, zoom_offs, scale_width, scale_height;
|
|
update_object_scale(zoom, scale_width, scale_height,
|
|
chin.x, chin.y, _GP(views)[chin.view].loops[chin.loop].frames[chin.frame].pic,
|
|
chex.zoom, (chin.flags & CHF_MANUALSCALING) == 0);
|
|
zoom_offs = (_GP(game).options[OPT_SCALECHAROFFSETS] != 0) ? zoom : 100;
|
|
|
|
// Calculate the X & Y co-ordinates of where the sprite will be;
|
|
// for the character sprite's origin is at the bottom-mid of a sprite.
|
|
const int atxp = (data_to_game_coord(chin.x)) - scale_width / 2;
|
|
const int atyp = (data_to_game_coord(chin.y) - scale_height)
|
|
// adjust the Y positioning for the character's Z co-ord
|
|
- (data_to_game_coord(chin.z) * zoom_offs / 100);
|
|
|
|
// Save calculated properties
|
|
chex.width = scale_width;
|
|
chex.height = scale_height;
|
|
chin.actx = atxp;
|
|
chin.acty = atyp;
|
|
chex.zoom = zoom;
|
|
chex.zoom_offs = zoom_offs;
|
|
}
|
|
|
|
int is_pos_on_character(int xx, int yy) {
|
|
int cc, sppic, lowestyp = 0, lowestwas = -1;
|
|
for (cc = 0; cc < _GP(game).numcharacters; cc++) {
|
|
if (_GP(game).chars[cc].room != _G(displayed_room)) continue;
|
|
if (_GP(game).chars[cc].on == 0) continue;
|
|
if (_GP(game).chars[cc].flags & CHF_NOINTERACT) continue;
|
|
if (_GP(game).chars[cc].view < 0) continue;
|
|
CharacterInfo *chin = &_GP(game).chars[cc];
|
|
|
|
if ((chin->view < 0) ||
|
|
(chin->loop >= _GP(views)[chin->view].numLoops) ||
|
|
(chin->frame >= _GP(views)[chin->view].loops[chin->loop].numFrames)) {
|
|
continue;
|
|
}
|
|
|
|
sppic = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].pic;
|
|
int usewid = _GP(charextra)[cc].width;
|
|
int usehit = _GP(charextra)[cc].height;
|
|
if (usewid == 0) usewid = _GP(game).SpriteInfos[sppic].Width;
|
|
if (usehit == 0) usehit = _GP(game).SpriteInfos[sppic].Height;
|
|
int xxx = chin->x - game_to_data_coord(usewid) / 2;
|
|
int yyy = _GP(charextra)[cc].GetEffectiveY(chin) - game_to_data_coord(usehit);
|
|
|
|
int mirrored = _GP(views)[chin->view].loops[chin->loop].frames[chin->frame].flags & VFLG_FLIPSPRITE;
|
|
|
|
bool is_original;
|
|
Bitmap *theImage = GetCharacterImage(cc, &is_original);
|
|
if (!is_original)
|
|
mirrored = 0; // transformed image is already flipped
|
|
|
|
if (is_pos_in_sprite(xx, yy, xxx, yyy, theImage,
|
|
game_to_data_coord(usewid),
|
|
game_to_data_coord(usehit), mirrored, is_original) == FALSE)
|
|
continue;
|
|
|
|
int use_base = chin->get_baseline();
|
|
if (use_base < lowestyp) continue;
|
|
lowestyp = use_base;
|
|
lowestwas = cc;
|
|
}
|
|
_G(char_lowest_yp) = lowestyp;
|
|
return lowestwas;
|
|
}
|
|
|
|
void get_char_blocking_rect(int charid, int *x1, int *y1, int *width, int *y2) {
|
|
CharacterInfo *char1 = &_GP(game).chars[charid];
|
|
int cwidth, fromx;
|
|
|
|
if (char1->blocking_width < 1)
|
|
cwidth = game_to_data_coord(GetCharacterWidth(charid)) - 4;
|
|
else
|
|
cwidth = char1->blocking_width;
|
|
|
|
fromx = char1->x - cwidth / 2;
|
|
if (fromx < 0) {
|
|
cwidth += fromx;
|
|
fromx = 0;
|
|
}
|
|
if (fromx + cwidth >= mask_to_room_coord(_G(walkable_areas_temp)->GetWidth()))
|
|
cwidth = mask_to_room_coord(_G(walkable_areas_temp)->GetWidth()) - fromx;
|
|
|
|
if (x1)
|
|
*x1 = fromx;
|
|
if (width)
|
|
*width = cwidth;
|
|
if (y1)
|
|
*y1 = char1->get_blocking_top();
|
|
if (y2)
|
|
*y2 = char1->get_blocking_bottom();
|
|
}
|
|
|
|
// Check whether the source char has walked onto character ww
|
|
int is_char_on_another(int sourceChar, int ww, int *fromxptr, int *cwidptr) {
|
|
|
|
int fromx, cwidth;
|
|
int y1, y2;
|
|
get_char_blocking_rect(ww, &fromx, &y1, &cwidth, &y2);
|
|
|
|
if (fromxptr)
|
|
fromxptr[0] = fromx;
|
|
if (cwidptr)
|
|
cwidptr[0] = cwidth;
|
|
|
|
// if the character trying to move is already on top of
|
|
// this char somehow, allow them through
|
|
if ((sourceChar >= 0) &&
|
|
// x/width are left and width co-ords, so they need >= and <
|
|
(_GP(game).chars[sourceChar].x >= fromx) &&
|
|
(_GP(game).chars[sourceChar].x < fromx + cwidth) &&
|
|
// y1/y2 are the top/bottom co-ords, so they need >= / <=
|
|
(_GP(game).chars[sourceChar].y >= y1) &&
|
|
(_GP(game).chars[sourceChar].y <= y2))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int my_getpixel(Bitmap *blk, int x, int y) {
|
|
if ((x < 0) || (y < 0) || (x >= blk->GetWidth()) || (y >= blk->GetHeight()))
|
|
return -1;
|
|
|
|
// strip the alpha channel
|
|
// TODO: is there a way to do this vtable thing with Bitmap?
|
|
BITMAP *al_bmp = (BITMAP *)blk->GetAllegroBitmap();
|
|
return al_bmp->getpixel(x, y) & 0x00ffffff;
|
|
}
|
|
|
|
int check_click_on_character(int xx, int yy, int mood) {
|
|
int lowestwas = is_pos_on_character(xx, yy);
|
|
if (lowestwas >= 0) {
|
|
RunCharacterInteraction(lowestwas, mood);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void _DisplaySpeechCore(int chid, const char *displbuf) {
|
|
if (displbuf[0] == 0) {
|
|
// no text, just update the current character who's speaking
|
|
// this allows the portrait side to be switched with an empty
|
|
// speech line
|
|
_GP(play).swap_portrait_lastchar = chid;
|
|
return;
|
|
}
|
|
|
|
// adjust timing of text (so that DisplaySpeech("%s", str) pauses
|
|
// for the length of the string not 2 frames)
|
|
int len = (int)strlen(displbuf);
|
|
if (len > _G(source_text_length) + 3)
|
|
_G(source_text_length) = len;
|
|
|
|
DisplaySpeech(displbuf, chid);
|
|
}
|
|
|
|
void _DisplayThoughtCore(int chid, const char *displbuf) {
|
|
// adjust timing of text (so that DisplayThought("%s", str) pauses
|
|
// for the length of the string not 2 frames)
|
|
int len = (int)strlen(displbuf);
|
|
if (len > _G(source_text_length) + 3)
|
|
_G(source_text_length) = len;
|
|
|
|
int xpp = -1, ypp = -1, width = -1;
|
|
|
|
if ((_GP(game).options[OPT_SPEECHTYPE] == 0) || (_GP(game).chars[chid].thinkview <= 0)) {
|
|
// lucasarts-style, so we want a speech bubble actually above
|
|
// their head (or if they have no think anim in Sierra-style)
|
|
width = data_to_game_coord(_GP(play).speech_bubble_width);
|
|
xpp = _GP(play).RoomToScreenX(data_to_game_coord(_GP(game).chars[chid].x)) - width / 2;
|
|
if (xpp < 0)
|
|
xpp = 0;
|
|
// -1 will automatically put it above the char's head
|
|
ypp = -1;
|
|
}
|
|
|
|
_displayspeech(displbuf, chid, xpp, ypp, width, 1);
|
|
}
|
|
|
|
void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int isThought) {
|
|
if (!is_valid_character(aschar))
|
|
quit("!DisplaySpeech: invalid character");
|
|
|
|
CharacterInfo *speakingChar = &_GP(game).chars[aschar];
|
|
if ((speakingChar->view < 0) || (speakingChar->view >= _GP(game).numviews))
|
|
quit("!DisplaySpeech: character has invalid view");
|
|
|
|
if (_GP(play).screen_is_faded_out > 0)
|
|
debug_script_warn("Warning: blocking Say call during fade-out.");
|
|
if (_GP(play).text_overlay_on > 0) {
|
|
debug_script_warn("DisplaySpeech: speech was already displayed (nested DisplaySpeech, perhaps room script and global script conflict?)");
|
|
return;
|
|
}
|
|
|
|
EndSkippingUntilCharStops();
|
|
|
|
_G(said_speech_line) = 1;
|
|
|
|
if (_GP(play).bgspeech_stay_on_display == 0) {
|
|
// remove any background speech
|
|
auto &overs = get_overlays();
|
|
for (auto &over : overs) {
|
|
if (over.timeout > 0)
|
|
remove_screen_overlay(over.type);
|
|
}
|
|
}
|
|
_G(said_text) = 1;
|
|
|
|
// the strings are pre-translated
|
|
//texx = get_translation(texx);
|
|
set_our_eip(150);
|
|
|
|
int isPause = 1;
|
|
// if the message is all .'s, don't display anything
|
|
for (size_t aa = 0; texx[aa] != 0; aa++) {
|
|
if (texx[aa] != '.') {
|
|
isPause = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_GP(play).messagetime = GetTextDisplayTime(texx);
|
|
_GP(play).speech_in_post_state = false;
|
|
|
|
if (isPause) {
|
|
postpone_scheduled_music_update_by(std::chrono::milliseconds(_GP(play).messagetime * 1000 / _G(frames_per_second)));
|
|
// Set a post-state right away, as we only need to wait for a messagetime timer
|
|
_GP(play).speech_in_post_state = true;
|
|
GameLoopUntilValueIsNegative(&_GP(play).messagetime);
|
|
post_display_cleanup();
|
|
return;
|
|
}
|
|
|
|
int textcol = speakingChar->talkcolor;
|
|
|
|
// if it's 0, it won't be recognised as speech
|
|
if (textcol == 0)
|
|
textcol = 16;
|
|
|
|
Rect ui_view = _GP(play).GetUIViewport();
|
|
int allowShrink = 0;
|
|
int bwidth = widd;
|
|
if (bwidth < 0)
|
|
bwidth = ui_view.GetWidth() / 2 + ui_view.GetWidth() / 4;
|
|
|
|
set_our_eip(151);
|
|
|
|
int useview = speakingChar->talkview;
|
|
if (isThought) {
|
|
useview = speakingChar->thinkview;
|
|
// view 0 is not valid for think views
|
|
if (useview == 0)
|
|
useview = -1;
|
|
// speech bubble can shrink to fit
|
|
allowShrink = 1;
|
|
if (speakingChar->room != _G(displayed_room)) {
|
|
// not in room, centre it
|
|
xx = -1;
|
|
yy = -1;
|
|
}
|
|
}
|
|
|
|
if (useview >= _GP(game).numviews)
|
|
quitprintf("!Character.Say: attempted to use view %d for animation, but it does not exist", useview + 1);
|
|
|
|
if (_GP(game).options[OPT_SPEECHTYPE] == 3)
|
|
remove_screen_overlay(OVER_COMPLETE);
|
|
set_our_eip(1500);
|
|
|
|
if (_GP(game).options[OPT_SPEECHTYPE] == 0)
|
|
allowShrink = 1;
|
|
|
|
// If has a valid speech view, and idle anim in progress for the character, then stop it
|
|
if (useview >= 0) {
|
|
stop_character_idling(speakingChar);
|
|
}
|
|
|
|
int tdxp = xx, tdyp = yy;
|
|
int oldview = -1, oldloop = -1;
|
|
int ovr_type = 0;
|
|
_G(text_lips_offset) = 0;
|
|
_G(text_lips_text) = texx;
|
|
Bitmap *closeupface = nullptr;
|
|
bool overlayPositionFixed = false;
|
|
int charFrameWas = 0;
|
|
int viewWasLocked = 0;
|
|
if (speakingChar->flags & CHF_FIXVIEW)
|
|
viewWasLocked = 1;
|
|
|
|
// Start voice-over, if requested by the tokens in speech text
|
|
try_auto_play_speech(texx, texx, aschar);
|
|
|
|
if (speakingChar->room == _G(displayed_room)) {
|
|
// If the character is in this room, go for it - otherwise
|
|
// run the "else" clause which does text in the middle of
|
|
// the screen.
|
|
set_our_eip(1501);
|
|
|
|
if (speakingChar->walking)
|
|
StopMoving(aschar);
|
|
|
|
// save the frame we need to go back to
|
|
// if they were moving, this will be 0 (because we just called
|
|
// StopMoving); otherwise, it might be a specific animation
|
|
// frame which we should return to
|
|
if (viewWasLocked)
|
|
charFrameWas = speakingChar->frame;
|
|
|
|
if ((speakingChar->view < 0) || _GP(views)[speakingChar->view].numLoops == 0)
|
|
quitprintf("!Character %s current view %d is invalid, or has no loops.", speakingChar->scrname, speakingChar->view + 1);
|
|
// If current view is missing a loop - use loop 0
|
|
if (speakingChar->loop >= _GP(views)[speakingChar->view].numLoops) {
|
|
debug_script_warn("WARNING: Character %s current view %d does not have necessary loop %d; switching to loop 0.",
|
|
speakingChar->scrname, speakingChar->view + 1, speakingChar->loop);
|
|
speakingChar->loop = 0;
|
|
}
|
|
|
|
set_our_eip(1504);
|
|
|
|
// Calculate speech position based on character's position on screen
|
|
auto view = FindNearestViewport(aschar);
|
|
if (tdxp < 0)
|
|
tdxp = view->RoomToScreen(data_to_game_coord(speakingChar->x), 0).first.X;
|
|
if (tdxp < 2)
|
|
tdxp = 2;
|
|
tdxp = -tdxp; // tell it to centre it ([ikm] not sure what's going on here... wrong comment?)
|
|
|
|
if (tdyp < 0) {
|
|
int sppic = _GP(views)[speakingChar->view].loops[speakingChar->loop].frames[0].pic;
|
|
int height = (_GP(charextra)[aschar].height < 1) ? _GP(game).SpriteInfos[sppic].Height : _GP(charextra)[aschar].height;
|
|
tdyp = view->RoomToScreen(0, data_to_game_coord(_GP(charextra)[aschar].GetEffectiveY(speakingChar)) - height).first.Y
|
|
- get_fixed_pixel_size(5);
|
|
if (isThought) // if it's a thought, lift it a bit further up
|
|
tdyp -= get_fixed_pixel_size(10);
|
|
}
|
|
if (tdyp < 5)
|
|
tdyp = 5;
|
|
|
|
set_our_eip(152);
|
|
|
|
if ((useview >= 0) && (_GP(game).options[OPT_SPEECHTYPE] > 0)) {
|
|
// Sierra-style close-up portrait
|
|
|
|
if (_GP(play).swap_portrait_lastchar != aschar) {
|
|
// if the portraits are set to Alternate, OR they are
|
|
// set to Left but swap_portrait has been set to 1 (the old
|
|
// method for enabling it), then swap them round
|
|
if ((_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_ALTERNATE) ||
|
|
((_GP(game).options[OPT_PORTRAITSIDE] == 0) &&
|
|
(_GP(play).swap_portrait_side > 0))) {
|
|
|
|
if (_GP(play).swap_portrait_side == 2)
|
|
_GP(play).swap_portrait_side = 1;
|
|
else
|
|
_GP(play).swap_portrait_side = 2;
|
|
}
|
|
|
|
if (_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_XPOSITION) {
|
|
// Portrait side based on character X-positions
|
|
if (_GP(play).swap_portrait_lastchar < 0) {
|
|
// No previous character been spoken to
|
|
// therefore, assume it's the player
|
|
if (_GP(game).playercharacter != aschar && _GP(game).chars[_GP(game).playercharacter].room == speakingChar->room && _GP(game).chars[_GP(game).playercharacter].on == 1)
|
|
_GP(play).swap_portrait_lastchar = _GP(game).playercharacter;
|
|
else
|
|
// The player's not here. Find another character in this room
|
|
// that it could be
|
|
for (int ce = 0; ce < _GP(game).numcharacters; ce++) {
|
|
if ((_GP(game).chars[ce].room == speakingChar->room) &&
|
|
(_GP(game).chars[ce].on == 1) &&
|
|
(ce != aschar)) {
|
|
_GP(play).swap_portrait_lastchar = ce;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_GP(play).swap_portrait_lastchar >= 0) {
|
|
// if this character is right of the one before, put the
|
|
// portrait on the right
|
|
if (speakingChar->x > _GP(game).chars[_GP(play).swap_portrait_lastchar].x)
|
|
_GP(play).swap_portrait_side = -1;
|
|
else
|
|
_GP(play).swap_portrait_side = 0;
|
|
}
|
|
}
|
|
_GP(play).swap_portrait_lastlastchar = _GP(play).swap_portrait_lastchar;
|
|
_GP(play).swap_portrait_lastchar = aschar;
|
|
} else
|
|
// If the portrait side is based on the character's X position and the same character is
|
|
// speaking, compare against the previous *previous* character to see where the speech should be
|
|
if (_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_XPOSITION && _GP(play).swap_portrait_lastlastchar >= 0) {
|
|
if (speakingChar->x > _GP(game).chars[_GP(play).swap_portrait_lastlastchar].x)
|
|
_GP(play).swap_portrait_side = -1;
|
|
else
|
|
_GP(play).swap_portrait_side = 0;
|
|
}
|
|
|
|
// Determine whether to display the portrait on the left or right
|
|
int portrait_on_right = 0;
|
|
|
|
if (_GP(game).options[OPT_SPEECHTYPE] == 3) {
|
|
} // always on left with QFG-style speech
|
|
else if ((_GP(play).swap_portrait_side == 1) ||
|
|
(_GP(play).swap_portrait_side == -1) ||
|
|
(_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_RIGHT))
|
|
portrait_on_right = 1;
|
|
|
|
|
|
int bigx = 0, bigy = 0, kk;
|
|
ViewStruct *viptr = &_GP(views)[useview];
|
|
for (kk = 0; kk < viptr->loops[0].numFrames; kk++) {
|
|
int tw = _GP(game).SpriteInfos[viptr->loops[0].frames[kk].pic].Width;
|
|
if (tw > bigx) bigx = tw;
|
|
tw = _GP(game).SpriteInfos[viptr->loops[0].frames[kk].pic].Height;
|
|
if (tw > bigy) bigy = tw;
|
|
}
|
|
|
|
// if they accidentally used a large full-screen image as the sierra-style
|
|
// talk view, correct it
|
|
if ((_GP(game).options[OPT_SPEECHTYPE] != 3) && (bigx > ui_view.GetWidth() - get_fixed_pixel_size(50)))
|
|
bigx = ui_view.GetWidth() - get_fixed_pixel_size(50);
|
|
|
|
if (widd > 0)
|
|
bwidth = widd - bigx;
|
|
|
|
set_our_eip(153);
|
|
int ovr_yp = get_fixed_pixel_size(20);
|
|
int view_frame_x = 0;
|
|
int view_frame_y = 0;
|
|
_G(facetalk_qfg4_override_placement_x) = false;
|
|
_G(facetalk_qfg4_override_placement_y) = false;
|
|
|
|
if (_GP(game).options[OPT_SPEECHTYPE] == 3) {
|
|
// QFG4-style whole screen picture
|
|
closeupface = BitmapHelper::CreateBitmap(ui_view.GetWidth(), ui_view.GetHeight());
|
|
closeupface->Clear(0);
|
|
if (xx < 0 && _GP(play).speech_portrait_placement) {
|
|
_G(facetalk_qfg4_override_placement_x) = true;
|
|
view_frame_x = _GP(play).speech_portrait_x;
|
|
}
|
|
if (yy < 0 && _GP(play).speech_portrait_placement) {
|
|
_G(facetalk_qfg4_override_placement_y) = true;
|
|
view_frame_y = _GP(play).speech_portrait_y;
|
|
} else {
|
|
view_frame_y = ui_view.GetHeight() / 2 - _GP(game).SpriteInfos[viptr->loops[0].frames[0].pic].Height / 2;
|
|
}
|
|
bigx = ui_view.GetWidth() / 2 - get_fixed_pixel_size(20);
|
|
ovr_type = OVER_COMPLETE;
|
|
ovr_yp = 0;
|
|
tdyp = -1; // center vertically
|
|
} else {
|
|
// KQ6-style close-up face picture
|
|
if (yy < 0 && _GP(play).speech_portrait_placement) {
|
|
ovr_yp = _GP(play).speech_portrait_y;
|
|
} else if (yy < 0)
|
|
ovr_yp = adjust_y_for_guis(ovr_yp, true /* displayspeech is always blocking */);
|
|
else
|
|
ovr_yp = yy;
|
|
|
|
closeupface = BitmapHelper::CreateTransparentBitmap(bigx + 1, bigy + 1);
|
|
ovr_type = OVER_PICTURE;
|
|
|
|
if (yy < 0)
|
|
tdyp = ovr_yp + get_textwindow_top_border_height(_GP(play).speech_textwindow_gui);
|
|
}
|
|
const ViewFrame *vf = &viptr->loops[0].frames[0];
|
|
const bool closeupface_has_alpha = (_GP(game).SpriteInfos[vf->pic].Flags & SPF_ALPHACHANNEL) != 0;
|
|
DrawViewFrame(closeupface, vf, view_frame_x, view_frame_y);
|
|
|
|
int overlay_x = get_fixed_pixel_size(10);
|
|
if (xx < 0) {
|
|
tdxp = bigx + get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
|
|
if (_GP(play).speech_portrait_placement) {
|
|
overlay_x = _GP(play).speech_portrait_x;
|
|
tdxp += overlay_x + get_fixed_pixel_size(6);
|
|
} else {
|
|
tdxp += get_fixed_pixel_size(16);
|
|
}
|
|
|
|
int maxWidth = (ui_view.GetWidth() - tdxp) - get_fixed_pixel_size(5) -
|
|
get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
|
|
|
|
if (bwidth > maxWidth)
|
|
bwidth = maxWidth;
|
|
} else {
|
|
tdxp = xx + bigx + get_fixed_pixel_size(8);
|
|
overlay_x = xx;
|
|
}
|
|
|
|
// allow the text box to be shrunk to fit the text
|
|
allowShrink = 1;
|
|
|
|
// if the portrait's on the right, swap it round
|
|
if (portrait_on_right) {
|
|
if ((xx < 0) || (widd < 0)) {
|
|
tdxp = get_fixed_pixel_size(9);
|
|
if (_GP(play).speech_portrait_placement) {
|
|
overlay_x = (ui_view.GetWidth() - bigx) - _GP(play).speech_portrait_x;
|
|
int maxWidth = overlay_x - tdxp - get_fixed_pixel_size(9) -
|
|
get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
|
|
if (bwidth > maxWidth)
|
|
bwidth = maxWidth;
|
|
} else {
|
|
overlay_x = (ui_view.GetWidth() - bigx) - get_fixed_pixel_size(5);
|
|
}
|
|
} else {
|
|
overlay_x = (xx + widd - bigx) - get_fixed_pixel_size(5);
|
|
tdxp = xx;
|
|
}
|
|
tdxp += get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
|
|
allowShrink = 2;
|
|
}
|
|
if (_GP(game).options[OPT_SPEECHTYPE] == 3)
|
|
overlay_x = 0;
|
|
_G(face_talking) = add_screen_overlay(false, overlay_x, ovr_yp, ovr_type, closeupface, closeupface_has_alpha);
|
|
_G(facetalkview) = useview;
|
|
_G(facetalkloop) = 0;
|
|
_G(facetalkframe) = 0;
|
|
_G(facetalkwait) = viptr->loops[0].frames[0].speed + GetCharacterSpeechAnimationDelay(speakingChar);
|
|
_G(facetalkrepeat) = (isThought) ? 0 : 1;
|
|
_G(facetalkBlinkLoop) = 0;
|
|
_G(facetalkAllowBlink) = 1;
|
|
if ((isThought) && (speakingChar->flags & CHF_NOBLINKANDTHINK))
|
|
_G(facetalkAllowBlink) = 0;
|
|
_G(facetalkchar) = &_GP(game).chars[aschar];
|
|
if (_G(facetalkchar)->blinktimer < 0)
|
|
_G(facetalkchar)->blinktimer = _G(facetalkchar)->blinkinterval;
|
|
textcol = -textcol;
|
|
overlayPositionFixed = true;
|
|
// Process the first portrait view frame
|
|
const int frame_vol = _GP(charextra)[_G(facetalkchar)->index_id].GetFrameSoundVolume(_G(facetalkchar));
|
|
CheckViewFrame(_G(facetalkview), _G(facetalkloop), _G(facetalkframe), frame_vol);
|
|
} else if (useview >= 0) {
|
|
// Lucasarts-style speech
|
|
set_our_eip(154);
|
|
|
|
oldview = speakingChar->view;
|
|
oldloop = speakingChar->loop;
|
|
|
|
speakingChar->set_animating(!isThought, // only repeat if speech, not thought
|
|
true, // always forwards
|
|
GetCharacterSpeechAnimationDelay(speakingChar));
|
|
|
|
speakingChar->view = useview;
|
|
speakingChar->frame = 0;
|
|
speakingChar->flags |= CHF_FIXVIEW;
|
|
|
|
if ((speakingChar->view < 0) || _GP(views)[speakingChar->view].numLoops == 0)
|
|
quitprintf("!Character %s speech view %d is invalid, or has no loops.", speakingChar->scrname, speakingChar->view + 1);
|
|
// If speech view is missing a loop - use loop 0
|
|
if (speakingChar->loop >= _GP(views)[speakingChar->view].numLoops) {
|
|
debug_script_warn("WARNING: Character %s speech view %d does not have necessary loop %d; switching to loop 0.",
|
|
speakingChar->scrname, speakingChar->view + 1, speakingChar->loop);
|
|
speakingChar->loop = 0;
|
|
}
|
|
|
|
_G(facetalkBlinkLoop) = speakingChar->loop;
|
|
|
|
// set up the speed of the first frame
|
|
speakingChar->wait = GetCharacterSpeechAnimationDelay(speakingChar) +
|
|
_GP(views)[speakingChar->view].loops[speakingChar->loop].frames[0].speed;
|
|
|
|
if (widd < 0) {
|
|
bwidth = ui_view.GetWidth() / 2 + ui_view.GetWidth() / 6;
|
|
// If they are close to the screen edge, make the text narrower
|
|
int relx = _GP(play).RoomToScreenX(data_to_game_coord(speakingChar->x));
|
|
if ((relx < ui_view.GetWidth() / 4) || (relx > ui_view.GetWidth() - (ui_view.GetWidth() / 4)))
|
|
bwidth -= ui_view.GetWidth() / 5;
|
|
}
|
|
/* this causes the text to bob up and down as they talk
|
|
tdxp = OVR_AUTOPLACE;
|
|
tdyp = aschar;*/
|
|
if (!isThought) // set up the lip sync if not thinking
|
|
_G(char_speaking) = aschar;
|
|
|
|
}
|
|
} else
|
|
allowShrink = 1;
|
|
|
|
// it wants the centred position, so make it so
|
|
if ((xx >= 0) && (tdxp < 0))
|
|
tdxp -= widd / 2;
|
|
|
|
// if they used DisplaySpeechAt, then use the supplied width
|
|
if ((widd > 0) && (isThought == 0))
|
|
allowShrink = 0;
|
|
|
|
if (isThought)
|
|
_G(char_thinking) = aschar;
|
|
|
|
set_our_eip(155);
|
|
display_main(tdxp, tdyp, bwidth, texx, DISPLAYTEXT_SPEECH, FONT_SPEECH, textcol, isThought, allowShrink, overlayPositionFixed);
|
|
if (_G(abort_engine))
|
|
return;
|
|
|
|
set_our_eip(156);
|
|
if ((_GP(play).in_conversation > 0) && (_GP(game).options[OPT_SPEECHTYPE] == 3))
|
|
closeupface = nullptr;
|
|
if (closeupface != nullptr)
|
|
remove_screen_overlay(ovr_type);
|
|
mark_screen_dirty();
|
|
_G(face_talking) = -1;
|
|
_G(facetalkchar) = nullptr;
|
|
set_our_eip(157);
|
|
if (oldview >= 0) {
|
|
speakingChar->flags &= ~CHF_FIXVIEW;
|
|
if (viewWasLocked)
|
|
speakingChar->flags |= CHF_FIXVIEW;
|
|
speakingChar->view = oldview;
|
|
|
|
// Don't reset the loop in 2.x games
|
|
if (_G(loaded_game_file_version) > kGameVersion_272)
|
|
speakingChar->loop = oldloop;
|
|
|
|
stop_character_anim(speakingChar);
|
|
speakingChar->frame = charFrameWas;
|
|
speakingChar->wait = 0;
|
|
speakingChar->idleleft = speakingChar->idletime;
|
|
// restart the idle animation straight away
|
|
_GP(charextra)[aschar].process_idle_this_time = 1;
|
|
}
|
|
_G(char_speaking) = -1;
|
|
_G(char_thinking) = -1;
|
|
// Stop any blocking voice-over, if was started by this function
|
|
if (_GP(play).IsBlockingVoiceSpeech())
|
|
stop_voice_speech();
|
|
}
|
|
|
|
int get_character_currently_talking() {
|
|
if ((_G(face_talking) >= 0) && (_G(facetalkrepeat)))
|
|
return _G(facetalkchar)->index_id;
|
|
else if (_G(char_speaking) >= 0)
|
|
return _G(char_speaking);
|
|
|
|
return -1;
|
|
}
|
|
|
|
void DisplaySpeech(const char *texx, int aschar) {
|
|
_displayspeech(texx, aschar, -1, -1, -1, 0);
|
|
}
|
|
|
|
// Calculate which frame of the loop to use for this character of
|
|
// speech
|
|
int GetLipSyncFrame(const char *curtex, int *stroffs) {
|
|
/*char *frameletters[MAXLIPSYNCFRAMES] =
|
|
{"./,/ ", "A", "O", "F/V", "D/N/G/L/R", "B/P/M",
|
|
"Y/H/K/Q/C", "I/T/E/X/th", "U/W", "S/Z/J/ch", NULL,
|
|
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};*/
|
|
|
|
int bestfit_len = 0, bestfit = _GP(game).default_lipsync_frame;
|
|
for (int aa = 0; aa < MAXLIPSYNCFRAMES; aa++) {
|
|
char *tptr = _GP(game).lipSyncFrameLetters[aa];
|
|
while (tptr[0] != 0) {
|
|
int lenthisbit = strlen(tptr);
|
|
if (strchr(tptr, '/'))
|
|
lenthisbit = strchr(tptr, '/') - tptr;
|
|
|
|
if ((ags_strnicmp(curtex, tptr, lenthisbit) == 0) && (lenthisbit > bestfit_len)) {
|
|
bestfit = aa;
|
|
bestfit_len = lenthisbit;
|
|
}
|
|
tptr += lenthisbit;
|
|
while (tptr[0] == '/')
|
|
tptr++;
|
|
}
|
|
}
|
|
// If it's an unknown character, use the default frame
|
|
if (bestfit_len == 0)
|
|
bestfit_len = 1;
|
|
*stroffs += bestfit_len;
|
|
return bestfit;
|
|
}
|
|
|
|
int update_lip_sync(int talkview, int talkloop, int *talkframeptr) {
|
|
int talkframe = talkframeptr[0];
|
|
int talkwait = 0;
|
|
|
|
// lip-sync speech
|
|
const char *nowsaying = &_G(text_lips_text)[_G(text_lips_offset)];
|
|
// if it's an apostraphe, skip it (we'll, I'll, etc)
|
|
if (nowsaying[0] == '\'') {
|
|
_G(text_lips_offset)++;
|
|
nowsaying++;
|
|
}
|
|
|
|
if (_G(text_lips_offset) >= (int)strlen(_G(text_lips_text)))
|
|
talkframe = 0;
|
|
else {
|
|
talkframe = GetLipSyncFrame(nowsaying, &_G(text_lips_offset));
|
|
if (talkframe >= _GP(views)[talkview].loops[talkloop].numFrames)
|
|
talkframe = 0;
|
|
}
|
|
|
|
talkwait = _G(loops_per_character) + _GP(views)[talkview].loops[talkloop].frames[talkframe].speed;
|
|
|
|
talkframeptr[0] = talkframe;
|
|
return talkwait;
|
|
}
|
|
|
|
void restore_characters() {
|
|
for (int i = 0; i < _GP(game).numcharacters; ++i) {
|
|
_GP(charextra)[i].zoom_offs = (_GP(game).options[OPT_SCALECHAROFFSETS] != 0) ? _GP(charextra)[i].zoom : 100;
|
|
}
|
|
}
|
|
|
|
Rect GetCharacterRoomBBox(int charid, bool use_frame_0) {
|
|
int width, height;
|
|
const CharacterExtras &chex = _GP(charextra)[charid];
|
|
const CharacterInfo &chin = _GP(game).chars[charid];
|
|
int frame = use_frame_0 ? 0 : chin.frame;
|
|
int pic = _GP(views)[chin.view].loops[chin.loop].frames[frame].pic;
|
|
scale_sprite_size(pic, chex.zoom, &width, &height);
|
|
return RectWH(chin.x - width / 2, chin.y - height, width, height);
|
|
}
|
|
|
|
PViewport FindNearestViewport(int charid) {
|
|
Rect bbox = GetCharacterRoomBBox(charid, true);
|
|
float min_dist = -1.f;
|
|
PViewport nearest_view;
|
|
for (int i = 0; i < _GP(play).GetRoomViewportCount(); ++i) {
|
|
auto view = _GP(play).GetRoomViewport(i);
|
|
if (!view->IsVisible())
|
|
continue;
|
|
auto cam = view->GetCamera();
|
|
if (!cam)
|
|
continue;
|
|
Rect camr = cam->GetRect();
|
|
float dist = DistanceBetween(bbox, camr);
|
|
if (dist == 0.f)
|
|
return view;
|
|
if (min_dist < 0.f || dist < min_dist) {
|
|
min_dist = dist;
|
|
nearest_view = view;
|
|
}
|
|
}
|
|
return nearest_view ? nearest_view : _GP(play).GetRoomViewport(0);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Script API Functions
|
|
//
|
|
//=============================================================================
|
|
|
|
CharacterInfo *Character_GetByName(const char *name) {
|
|
return static_cast<CharacterInfo *>(ccGetScriptObjectAddress(name, _GP(ccDynamicCharacter).GetType()));
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetByName(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJ_POBJ(CharacterInfo, _GP(ccDynamicCharacter), Character_GetByName, const char);
|
|
}
|
|
|
|
// void | CharacterInfo *chaa, ScriptInvItem *invi, int addIndex
|
|
RuntimeScriptValue Sc_Character_AddInventory(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ_PINT(CharacterInfo, Character_AddInventory, ScriptInvItem);
|
|
}
|
|
|
|
// void | CharacterInfo *chaa, int x, int y
|
|
RuntimeScriptValue Sc_Character_AddWaypoint(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_AddWaypoint);
|
|
}
|
|
|
|
// void | CharacterInfo *chaa, int loop, int delay, int repeat, int blocking, int direction
|
|
RuntimeScriptValue Sc_Character_Animate5(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT5(CharacterInfo, Character_Animate5);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_Animate6(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT6(CharacterInfo, Character_Animate6);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_Animate(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT7(CharacterInfo, Character_Animate);
|
|
}
|
|
|
|
// void | CharacterInfo *chaa, int room, int x, int y
|
|
RuntimeScriptValue Sc_Character_ChangeRoom(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_ChangeRoom);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_ChangeRoomSetLoop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_ChangeRoomSetLoop);
|
|
}
|
|
|
|
// void | CharacterInfo *chaa, int room, int newPos
|
|
RuntimeScriptValue Sc_Character_ChangeRoomAutoPosition(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_ChangeRoomAutoPosition);
|
|
}
|
|
|
|
// void | CharacterInfo *chap, int vii
|
|
RuntimeScriptValue Sc_Character_ChangeView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_ChangeView);
|
|
}
|
|
|
|
// void | CharacterInfo *char1, CharacterInfo *char2, int blockingStyle
|
|
RuntimeScriptValue Sc_Character_FaceCharacter(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ_PINT(CharacterInfo, Character_FaceCharacter, CharacterInfo);
|
|
}
|
|
|
|
// void | CharacterInfo *char1, int direction, int blockingStyle
|
|
RuntimeScriptValue Sc_Character_FaceDirection(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_FaceDirection);
|
|
}
|
|
|
|
// void | CharacterInfo *char1, int xx, int yy, int blockingStyle
|
|
RuntimeScriptValue Sc_Character_FaceLocation(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_FaceLocation);
|
|
}
|
|
|
|
// void | CharacterInfo *char1, ScriptObject *obj, int blockingStyle
|
|
RuntimeScriptValue Sc_Character_FaceObject(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ_PINT(CharacterInfo, Character_FaceObject, ScriptObject);
|
|
}
|
|
|
|
// void | CharacterInfo *chaa, CharacterInfo *tofollow, int distaway, int eagerness
|
|
RuntimeScriptValue Sc_Character_FollowCharacter(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ_PINT2(CharacterInfo, Character_FollowCharacter, CharacterInfo);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa, const char *property)
|
|
RuntimeScriptValue Sc_Character_GetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_POBJ(CharacterInfo, Character_GetProperty, const char);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, const char *property, char *bufer)
|
|
RuntimeScriptValue Sc_Character_GetPropertyText(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ2(CharacterInfo, Character_GetPropertyText, const char, char);
|
|
}
|
|
|
|
// const char* (CharacterInfo *chaa, const char *property)
|
|
RuntimeScriptValue Sc_Character_GetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_OBJ_POBJ(CharacterInfo, const char, _GP(myScriptStringImpl), Character_GetTextProperty, const char);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_SetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_BOOL_POBJ_PINT(CharacterInfo, Character_SetProperty, const char);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_SetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_BOOL_POBJ2(CharacterInfo, Character_SetTextProperty, const char, const char);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa, ScriptInvItem *invi)
|
|
RuntimeScriptValue Sc_Character_HasInventory(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_POBJ(CharacterInfo, Character_HasInventory, ScriptInvItem);
|
|
}
|
|
|
|
// int (CharacterInfo *char1, CharacterInfo *char2)
|
|
RuntimeScriptValue Sc_Character_IsCollidingWithChar(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_POBJ(CharacterInfo, Character_IsCollidingWithChar, CharacterInfo);
|
|
}
|
|
|
|
// int (CharacterInfo *chin, ScriptObject *objid)
|
|
RuntimeScriptValue Sc_Character_IsCollidingWithObject(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_POBJ(CharacterInfo, Character_IsCollidingWithObject, ScriptObject);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_IsInteractionAvailable(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_BOOL_PINT(CharacterInfo, Character_IsInteractionAvailable);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, int vii)
|
|
RuntimeScriptValue Sc_Character_LockView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_LockView);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, int vii, int stopMoving)
|
|
RuntimeScriptValue Sc_Character_LockViewEx(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_LockViewEx);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, int vii, int loop, int align)
|
|
RuntimeScriptValue Sc_Character_LockViewAligned_Old(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_LockViewAligned_Old);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, int vii, int loop, int align, int stopMoving)
|
|
RuntimeScriptValue Sc_Character_LockViewAlignedEx_Old(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_LockViewAlignedEx_Old);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_LockViewAligned(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_LockViewAligned);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_LockViewAlignedEx(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_LockViewAlignedEx);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int view, int loop, int frame)
|
|
RuntimeScriptValue Sc_Character_LockViewFrame(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_LockViewFrame);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int view, int loop, int frame, int stopMoving)
|
|
RuntimeScriptValue Sc_Character_LockViewFrameEx(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_LockViewFrameEx);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, int vii, int xoffs, int yoffs)
|
|
RuntimeScriptValue Sc_Character_LockViewOffset(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_LockViewOffset);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, int vii, int xoffs, int yoffs, int stopMoving)
|
|
RuntimeScriptValue Sc_Character_LockViewOffsetEx(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_LockViewOffsetEx);
|
|
}
|
|
|
|
// void (CharacterInfo *chap, ScriptInvItem *invi)
|
|
RuntimeScriptValue Sc_Character_LoseInventory(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ(CharacterInfo, Character_LoseInventory, ScriptInvItem);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int x, int y, int blocking, int direct)
|
|
RuntimeScriptValue Sc_Character_Move(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_Move);
|
|
}
|
|
|
|
// void (CharacterInfo *chap)
|
|
RuntimeScriptValue Sc_Character_PlaceOnWalkableArea(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID(CharacterInfo, Character_PlaceOnWalkableArea);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_RemoveTint(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID(CharacterInfo, Character_RemoveTint);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int mood)
|
|
RuntimeScriptValue Sc_Character_RunInteraction(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_RunInteraction);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, const char *texx, ...)
|
|
RuntimeScriptValue Sc_Character_Say(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_SCRIPT_SPRINTF(Character_Say, 1);
|
|
Character_Say((CharacterInfo *)self, scsf_buffer);
|
|
return RuntimeScriptValue((int32_t)0);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_SayAt(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_SCRIPT_SPRINTF(Character_SayAt, 4);
|
|
Character_SayAt((CharacterInfo *)self, params[0].IValue, params[1].IValue, params[2].IValue, scsf_buffer);
|
|
return RuntimeScriptValue((int32_t)0);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_SayBackground(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_SCRIPT_SPRINTF(Character_SayBackground, 1);
|
|
auto *ret_obj = Character_SayBackground((CharacterInfo *)self, scsf_buffer);
|
|
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_SetAsPlayer(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID(CharacterInfo, Character_SetAsPlayer);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int iview, int itime)
|
|
RuntimeScriptValue Sc_Character_SetIdleView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_SetIdleView);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetHasExplicitLight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_BOOL(CharacterInfo, Character_GetHasExplicitLight);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetLightLevel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetLightLevel);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_SetLightLevel(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetLightLevel);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetTintBlue(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTintBlue);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetTintGreen(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTintGreen);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetTintRed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTintRed);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetTintSaturation(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTintSaturation);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetTintLuminance(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTintLuminance);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int xspeed, int yspeed)
|
|
RuntimeScriptValue Sc_Character_SetSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_SetSpeed);
|
|
}
|
|
|
|
// void (CharacterInfo *charp)
|
|
RuntimeScriptValue Sc_Character_StopMoving(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID(CharacterInfo, Character_StopMoving);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, const char *texx, ...)
|
|
RuntimeScriptValue Sc_Character_Think(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_SCRIPT_SPRINTF(Character_Think, 1);
|
|
Character_Think((CharacterInfo *)self, scsf_buffer);
|
|
return RuntimeScriptValue((int32_t)0);
|
|
}
|
|
|
|
//void (CharacterInfo *chaa, int red, int green, int blue, int opacity, int luminance)
|
|
RuntimeScriptValue Sc_Character_Tint(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT5(CharacterInfo, Character_Tint);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_UnlockView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID(CharacterInfo, Character_UnlockView);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int stopMoving)
|
|
RuntimeScriptValue Sc_Character_UnlockViewEx(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_UnlockViewEx);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int x, int y, int blocking, int direct)
|
|
RuntimeScriptValue Sc_Character_Walk(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT4(CharacterInfo, Character_Walk);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int xx, int yy, int blocking)
|
|
RuntimeScriptValue Sc_Character_WalkStraight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT3(CharacterInfo, Character_WalkStraight);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_GetCharacterAtRoom(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJ_PINT2(CharacterInfo, _GP(ccDynamicCharacter), GetCharacterAtRoom);
|
|
}
|
|
|
|
// CharacterInfo *(int xx, int yy)
|
|
RuntimeScriptValue Sc_GetCharacterAtScreen(const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_SCALL_OBJ_PINT2(CharacterInfo, _GP(ccDynamicCharacter), GetCharacterAtScreen);
|
|
}
|
|
|
|
// ScriptInvItem* (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetActiveInventory(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_OBJ(CharacterInfo, ScriptInvItem, _GP(ccDynamicInv), Character_GetActiveInventory);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, ScriptInvItem* iit)
|
|
RuntimeScriptValue Sc_Character_SetActiveInventory(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ(CharacterInfo, Character_SetActiveInventory, ScriptInvItem);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetAnimating(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetAnimating);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetAnimationSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetAnimationSpeed);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newval)
|
|
RuntimeScriptValue Sc_Character_SetAnimationSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetAnimationSpeed);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetAnimationVolume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetAnimationVolume);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_SetAnimationVolume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetAnimationVolume);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetBaseline(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetBaseline);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int basel)
|
|
RuntimeScriptValue Sc_Character_SetBaseline(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetBaseline);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetBlinkInterval(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetBlinkInterval);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int interval)
|
|
RuntimeScriptValue Sc_Character_SetBlinkInterval(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetBlinkInterval);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetBlinkView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetBlinkView);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int vii)
|
|
RuntimeScriptValue Sc_Character_SetBlinkView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetBlinkView);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetBlinkWhileThinking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetBlinkWhileThinking);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesOrNo)
|
|
RuntimeScriptValue Sc_Character_SetBlinkWhileThinking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetBlinkWhileThinking);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetBlockingHeight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetBlockingHeight);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int hit)
|
|
RuntimeScriptValue Sc_Character_SetBlockingHeight(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetBlockingHeight);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetBlockingWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetBlockingWidth);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int wid)
|
|
RuntimeScriptValue Sc_Character_SetBlockingWidth(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetBlockingWidth);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetClickable(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetClickable);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int clik)
|
|
RuntimeScriptValue Sc_Character_SetClickable(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetClickable);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetDiagonalWalking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetDiagonalWalking);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetDiagonalWalking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetDiagonalWalking);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetFrame(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetFrame);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newval)
|
|
RuntimeScriptValue Sc_Character_SetFrame(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetFrame);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetHasExplicitTint_Old(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetHasExplicitTint_Old);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetHasExplicitTint(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetHasExplicitTint);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetID(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetID);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetScriptName(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_OBJ(CharacterInfo, const char, _GP(myScriptStringImpl), Character_GetScriptName);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetIdleView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetIdleView);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa, int index)
|
|
RuntimeScriptValue Sc_Character_GetIInventoryQuantity(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT_PINT(CharacterInfo, Character_GetIInventoryQuantity);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int index, int quant)
|
|
RuntimeScriptValue Sc_Character_SetIInventoryQuantity(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT2(CharacterInfo, Character_SetIInventoryQuantity);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetIgnoreLighting(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetIgnoreLighting);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetIgnoreLighting(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetIgnoreLighting);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetIgnoreScaling(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetIgnoreScaling);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetIgnoreScaling(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetIgnoreScaling);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetIgnoreWalkbehinds(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetIgnoreWalkbehinds);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetIgnoreWalkbehinds(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetIgnoreWalkbehinds);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetLoop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetLoop);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newval)
|
|
RuntimeScriptValue Sc_Character_SetLoop(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetLoop);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetManualScaling(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetManualScaling);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetMovementLinkedToAnimation(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetMovementLinkedToAnimation);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetMovementLinkedToAnimation(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetMovementLinkedToAnimation);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetMoving(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetMoving);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetDestinationX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetDestinationX);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetDestinationY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetDestinationY);
|
|
}
|
|
|
|
// const char* (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetName(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_OBJ(CharacterInfo, const char, _GP(myScriptStringImpl), Character_GetName);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, const char *newName)
|
|
RuntimeScriptValue Sc_Character_SetName(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_POBJ(CharacterInfo, Character_SetName, const char);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetNormalView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetNormalView);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetPreviousRoom(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetPreviousRoom);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetRoom(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetRoom);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetScaleMoveSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetScaleMoveSpeed);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetScaleMoveSpeed(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetScaleMoveSpeed);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetScaleVolume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetScaleVolume);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetScaleVolume(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetScaleVolume);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetScaling(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetScaling);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int zoomlevel)
|
|
RuntimeScriptValue Sc_Character_SetScaling(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetScaling);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetSolid(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetSolid);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetSolid(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetSolid);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetSpeaking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetSpeaking);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetSpeakingFrame(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetSpeakingFrame);
|
|
}
|
|
|
|
// int (CharacterInfo *cha)
|
|
RuntimeScriptValue Sc_GetCharacterSpeechAnimationDelay(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, GetCharacterSpeechAnimationDelay);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newDelay)
|
|
RuntimeScriptValue Sc_Character_SetSpeechAnimationDelay(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetSpeechAnimationDelay);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetIdleAnimationDelay(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetIdleAnimationDelay);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newDelay)
|
|
RuntimeScriptValue Sc_Character_SetIdleAnimationDelay(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetIdleAnimationDelay);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetSpeechColor(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetSpeechColor);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int ncol)
|
|
RuntimeScriptValue Sc_Character_SetSpeechColor(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetSpeechColor);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetSpeechView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetSpeechView);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int vii)
|
|
RuntimeScriptValue Sc_Character_SetSpeechView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetSpeechView);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetThinking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_BOOL(CharacterInfo, Character_GetThinking);
|
|
}
|
|
|
|
RuntimeScriptValue Sc_Character_GetThinkingFrame(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetThinkingFrame);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetThinkView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetThinkView);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int vii)
|
|
RuntimeScriptValue Sc_Character_SetThinkView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetThinkView);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetTransparency(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTransparency);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int trans)
|
|
RuntimeScriptValue Sc_Character_SetTransparency(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetTransparency);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetTurnBeforeWalking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetTurnBeforeWalking);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int yesorno)
|
|
RuntimeScriptValue Sc_Character_SetTurnBeforeWalking(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetTurnBeforeWalking);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetView(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetView);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetWalkSpeedX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetWalkSpeedX);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetWalkSpeedY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetWalkSpeedY);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetX);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newval)
|
|
RuntimeScriptValue Sc_Character_SetX(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetX);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetY);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newval)
|
|
RuntimeScriptValue Sc_Character_SetY(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetY);
|
|
}
|
|
|
|
// int (CharacterInfo *chaa)
|
|
RuntimeScriptValue Sc_Character_GetZ(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_INT(CharacterInfo, Character_GetZ);
|
|
}
|
|
|
|
// void (CharacterInfo *chaa, int newval)
|
|
RuntimeScriptValue Sc_Character_SetZ(void *self, const RuntimeScriptValue *params, int32_t param_count) {
|
|
API_OBJCALL_VOID_PINT(CharacterInfo, Character_SetZ);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Exclusive variadic API implementation for Plugins
|
|
//
|
|
//=============================================================================
|
|
|
|
void ScPl_Character_Say(CharacterInfo *chaa, const char *texx, ...) {
|
|
API_PLUGIN_SCRIPT_SPRINTF(texx);
|
|
Character_Say(chaa, scsf_buffer);
|
|
}
|
|
|
|
void ScPl_Character_SayAt(CharacterInfo *chaa, int x, int y, int width, const char *texx, ...) {
|
|
API_PLUGIN_SCRIPT_SPRINTF(texx);
|
|
Character_SayAt(chaa, x, y, width, scsf_buffer);
|
|
}
|
|
|
|
ScriptOverlay *ScPl_Character_SayBackground(CharacterInfo *chaa, const char *texx, ...) {
|
|
API_PLUGIN_SCRIPT_SPRINTF(texx);
|
|
return Character_SayBackground(chaa, scsf_buffer);
|
|
}
|
|
|
|
void ScPl_Character_Think(CharacterInfo *chaa, const char *texx, ...) {
|
|
API_PLUGIN_SCRIPT_SPRINTF(texx);
|
|
Character_Think(chaa, scsf_buffer);
|
|
}
|
|
|
|
void RegisterCharacterAPI(ScriptAPIVersion base_api, ScriptAPIVersion /* compat_api */) {
|
|
ScFnRegister character_api[] = {
|
|
{"Character::GetAtRoomXY^2", API_FN_PAIR(GetCharacterAtRoom)},
|
|
{"Character::GetAtScreenXY^2", API_FN_PAIR(GetCharacterAtScreen)},
|
|
{"Character::GetByName", API_FN_PAIR(Character_GetByName)},
|
|
|
|
{"Character::AddInventory^2", API_FN_PAIR(Character_AddInventory)},
|
|
{"Character::AddWaypoint^2", API_FN_PAIR(Character_AddWaypoint)},
|
|
{"Character::Animate^5", API_FN_PAIR(Character_Animate5)},
|
|
{"Character::Animate^6", API_FN_PAIR(Character_Animate6)},
|
|
{"Character::Animate^7", API_FN_PAIR(Character_Animate)},
|
|
{"Character::ChangeRoom^3", API_FN_PAIR(Character_ChangeRoom)},
|
|
{"Character::ChangeRoom^4", API_FN_PAIR(Character_ChangeRoomSetLoop)},
|
|
{"Character::ChangeRoomAutoPosition^2", API_FN_PAIR(Character_ChangeRoomAutoPosition)},
|
|
{"Character::ChangeView^1", API_FN_PAIR(Character_ChangeView)},
|
|
{"Character::FaceCharacter^2", API_FN_PAIR(Character_FaceCharacter)},
|
|
{"Character::FaceDirection^2", API_FN_PAIR(Character_FaceDirection)},
|
|
{"Character::FaceLocation^3", API_FN_PAIR(Character_FaceLocation)},
|
|
{"Character::FaceObject^2", API_FN_PAIR(Character_FaceObject)},
|
|
{"Character::FollowCharacter^3", API_FN_PAIR(Character_FollowCharacter)},
|
|
{"Character::GetProperty^1", API_FN_PAIR(Character_GetProperty)},
|
|
{"Character::GetPropertyText^2", API_FN_PAIR(Character_GetPropertyText)},
|
|
{"Character::GetTextProperty^1", API_FN_PAIR(Character_GetTextProperty)},
|
|
{"Character::SetProperty^2", API_FN_PAIR(Character_SetProperty)},
|
|
{"Character::SetTextProperty^2", API_FN_PAIR(Character_SetTextProperty)},
|
|
{"Character::HasInventory^1", API_FN_PAIR(Character_HasInventory)},
|
|
{"Character::IsCollidingWithChar^1", API_FN_PAIR(Character_IsCollidingWithChar)},
|
|
{"Character::IsCollidingWithObject^1", API_FN_PAIR(Character_IsCollidingWithObject)},
|
|
{"Character::IsInteractionAvailable^1", API_FN_PAIR(Character_IsInteractionAvailable)},
|
|
{"Character::LockView^1", API_FN_PAIR(Character_LockView)},
|
|
{"Character::LockView^2", API_FN_PAIR(Character_LockViewEx)},
|
|
{"Character::LockViewFrame^3", API_FN_PAIR(Character_LockViewFrame)},
|
|
{"Character::LockViewFrame^4", API_FN_PAIR(Character_LockViewFrameEx)},
|
|
{"Character::LockViewOffset^3", API_FN_PAIR(Character_LockViewOffset)},
|
|
{"Character::LockViewOffset^4", API_FN_PAIR(Character_LockViewOffsetEx)},
|
|
{"Character::LoseInventory^1", API_FN_PAIR(Character_LoseInventory)},
|
|
{"Character::Move^4", API_FN_PAIR(Character_Move)},
|
|
{"Character::PlaceOnWalkableArea^0", API_FN_PAIR(Character_PlaceOnWalkableArea)},
|
|
{"Character::RemoveTint^0", API_FN_PAIR(Character_RemoveTint)},
|
|
{"Character::RunInteraction^1", API_FN_PAIR(Character_RunInteraction)},
|
|
{"Character::Say^101", Sc_Character_Say},
|
|
// old non-variadic variants
|
|
{"Character::SayAt^4", API_FN_PAIR(Character_SayAt)},
|
|
{"Character::SayBackground^1", API_FN_PAIR(Character_SayBackground)},
|
|
// newer variadic variants
|
|
{"Character::SayAt^104", Sc_Character_SayAt},
|
|
{"Character::SayBackground^101", Sc_Character_SayBackground},
|
|
{"Character::SetAsPlayer^0", API_FN_PAIR(Character_SetAsPlayer)},
|
|
{"Character::SetIdleView^2", API_FN_PAIR(Character_SetIdleView)},
|
|
{"Character::SetLightLevel^1", API_FN_PAIR(Character_SetLightLevel)},
|
|
{"Character::SetWalkSpeed^2", API_FN_PAIR(Character_SetSpeed)},
|
|
{"Character::StopMoving^0", API_FN_PAIR(Character_StopMoving)},
|
|
{"Character::Think^101", Sc_Character_Think},
|
|
{"Character::Tint^5", API_FN_PAIR(Character_Tint)},
|
|
{"Character::UnlockView^0", API_FN_PAIR(Character_UnlockView)},
|
|
{"Character::UnlockView^1", API_FN_PAIR(Character_UnlockViewEx)},
|
|
{"Character::Walk^4", API_FN_PAIR(Character_Walk)},
|
|
{"Character::WalkStraight^3", API_FN_PAIR(Character_WalkStraight)},
|
|
|
|
{"Character::get_ActiveInventory", API_FN_PAIR(Character_GetActiveInventory)},
|
|
{"Character::set_ActiveInventory", API_FN_PAIR(Character_SetActiveInventory)},
|
|
{"Character::get_Animating", API_FN_PAIR(Character_GetAnimating)},
|
|
{"Character::get_AnimationSpeed", API_FN_PAIR(Character_GetAnimationSpeed)},
|
|
{"Character::set_AnimationSpeed", API_FN_PAIR(Character_SetAnimationSpeed)},
|
|
{"Character::get_AnimationVolume", API_FN_PAIR(Character_GetAnimationVolume)},
|
|
{"Character::set_AnimationVolume", API_FN_PAIR(Character_SetAnimationVolume)},
|
|
{"Character::get_Baseline", API_FN_PAIR(Character_GetBaseline)},
|
|
{"Character::set_Baseline", API_FN_PAIR(Character_SetBaseline)},
|
|
{"Character::get_BlinkInterval", API_FN_PAIR(Character_GetBlinkInterval)},
|
|
{"Character::set_BlinkInterval", API_FN_PAIR(Character_SetBlinkInterval)},
|
|
{"Character::get_BlinkView", API_FN_PAIR(Character_GetBlinkView)},
|
|
{"Character::set_BlinkView", API_FN_PAIR(Character_SetBlinkView)},
|
|
{"Character::get_BlinkWhileThinking", API_FN_PAIR(Character_GetBlinkWhileThinking)},
|
|
{"Character::set_BlinkWhileThinking", API_FN_PAIR(Character_SetBlinkWhileThinking)},
|
|
{"Character::get_BlockingHeight", API_FN_PAIR(Character_GetBlockingHeight)},
|
|
{"Character::set_BlockingHeight", API_FN_PAIR(Character_SetBlockingHeight)},
|
|
{"Character::get_BlockingWidth", API_FN_PAIR(Character_GetBlockingWidth)},
|
|
{"Character::set_BlockingWidth", API_FN_PAIR(Character_SetBlockingWidth)},
|
|
{"Character::get_Clickable", API_FN_PAIR(Character_GetClickable)},
|
|
{"Character::set_Clickable", API_FN_PAIR(Character_SetClickable)},
|
|
{"Character::get_DestinationX", API_FN_PAIR(Character_GetDestinationX)},
|
|
{"Character::get_DestinationY", API_FN_PAIR(Character_GetDestinationY)},
|
|
{"Character::get_DiagonalLoops", API_FN_PAIR(Character_GetDiagonalWalking)},
|
|
{"Character::set_DiagonalLoops", API_FN_PAIR(Character_SetDiagonalWalking)},
|
|
{"Character::get_Frame", API_FN_PAIR(Character_GetFrame)},
|
|
{"Character::set_Frame", API_FN_PAIR(Character_SetFrame)},
|
|
{"Character::get_ID", API_FN_PAIR(Character_GetID)},
|
|
{"Character::get_IdleView", API_FN_PAIR(Character_GetIdleView)},
|
|
{"Character::get_IdleAnimationDelay", API_FN_PAIR(Character_GetIdleAnimationDelay)},
|
|
{"Character::set_IdleAnimationDelay", API_FN_PAIR(Character_SetIdleAnimationDelay)},
|
|
{"Character::geti_InventoryQuantity", API_FN_PAIR(Character_GetIInventoryQuantity)},
|
|
{"Character::seti_InventoryQuantity", API_FN_PAIR(Character_SetIInventoryQuantity)},
|
|
{"Character::get_IgnoreLighting", API_FN_PAIR(Character_GetIgnoreLighting)},
|
|
{"Character::set_IgnoreLighting", API_FN_PAIR(Character_SetIgnoreLighting)},
|
|
{"Character::get_IgnoreScaling", API_FN_PAIR(Character_GetIgnoreScaling)},
|
|
{"Character::set_IgnoreScaling", API_FN_PAIR(Character_SetIgnoreScaling)},
|
|
{"Character::get_IgnoreWalkbehinds", API_FN_PAIR(Character_GetIgnoreWalkbehinds)},
|
|
{"Character::set_IgnoreWalkbehinds", API_FN_PAIR(Character_SetIgnoreWalkbehinds)},
|
|
{"Character::get_Loop", API_FN_PAIR(Character_GetLoop)},
|
|
{"Character::set_Loop", API_FN_PAIR(Character_SetLoop)},
|
|
{"Character::get_ManualScaling", API_FN_PAIR(Character_GetIgnoreScaling)},
|
|
{"Character::set_ManualScaling", API_FN_PAIR(Character_SetManualScaling)},
|
|
{"Character::get_MovementLinkedToAnimation", API_FN_PAIR(Character_GetMovementLinkedToAnimation)},
|
|
{"Character::set_MovementLinkedToAnimation", API_FN_PAIR(Character_SetMovementLinkedToAnimation)},
|
|
{"Character::get_Moving", API_FN_PAIR(Character_GetMoving)},
|
|
{"Character::get_Name", API_FN_PAIR(Character_GetName)},
|
|
{"Character::set_Name", API_FN_PAIR(Character_SetName)},
|
|
{"Character::get_NormalView", API_FN_PAIR(Character_GetNormalView)},
|
|
{"Character::get_PreviousRoom", API_FN_PAIR(Character_GetPreviousRoom)},
|
|
{"Character::get_Room", API_FN_PAIR(Character_GetRoom)},
|
|
{"Character::get_ScaleMoveSpeed", API_FN_PAIR(Character_GetScaleMoveSpeed)},
|
|
{"Character::set_ScaleMoveSpeed", API_FN_PAIR(Character_SetScaleMoveSpeed)},
|
|
{"Character::get_ScaleVolume", API_FN_PAIR(Character_GetScaleVolume)},
|
|
{"Character::set_ScaleVolume", API_FN_PAIR(Character_SetScaleVolume)},
|
|
{"Character::get_Scaling", API_FN_PAIR(Character_GetScaling)},
|
|
{"Character::set_Scaling", API_FN_PAIR(Character_SetScaling)},
|
|
{"Character::get_ScriptName", API_FN_PAIR(Character_GetScriptName)},
|
|
{"Character::get_Solid", API_FN_PAIR(Character_GetSolid)},
|
|
{"Character::set_Solid", API_FN_PAIR(Character_SetSolid)},
|
|
{"Character::get_Speaking", API_FN_PAIR(Character_GetSpeaking)},
|
|
{"Character::get_SpeakingFrame", API_FN_PAIR(Character_GetSpeakingFrame)},
|
|
{"Character::get_SpeechAnimationDelay", API_FN_PAIR(GetCharacterSpeechAnimationDelay)},
|
|
{"Character::set_SpeechAnimationDelay", API_FN_PAIR(Character_SetSpeechAnimationDelay)},
|
|
{"Character::get_SpeechColor", API_FN_PAIR(Character_GetSpeechColor)},
|
|
{"Character::set_SpeechColor", API_FN_PAIR(Character_SetSpeechColor)},
|
|
{"Character::get_SpeechView", API_FN_PAIR(Character_GetSpeechView)},
|
|
{"Character::set_SpeechView", API_FN_PAIR(Character_SetSpeechView)},
|
|
{"Character::get_Thinking", API_FN_PAIR(Character_GetThinking)},
|
|
{"Character::get_ThinkingFrame", API_FN_PAIR(Character_GetThinkingFrame)},
|
|
{"Character::get_ThinkView", API_FN_PAIR(Character_GetThinkView)},
|
|
{"Character::set_ThinkView", API_FN_PAIR(Character_SetThinkView)},
|
|
{"Character::get_Transparency", API_FN_PAIR(Character_GetTransparency)},
|
|
{"Character::set_Transparency", API_FN_PAIR(Character_SetTransparency)},
|
|
{"Character::get_TurnBeforeWalking", API_FN_PAIR(Character_GetTurnBeforeWalking)},
|
|
{"Character::set_TurnBeforeWalking", API_FN_PAIR(Character_SetTurnBeforeWalking)},
|
|
{"Character::get_View", API_FN_PAIR(Character_GetView)},
|
|
{"Character::get_WalkSpeedX", API_FN_PAIR(Character_GetWalkSpeedX)},
|
|
{"Character::get_WalkSpeedY", API_FN_PAIR(Character_GetWalkSpeedY)},
|
|
{"Character::get_X", API_FN_PAIR(Character_GetX)},
|
|
{"Character::set_X", API_FN_PAIR(Character_SetX)},
|
|
{"Character::get_x", API_FN_PAIR(Character_GetX)},
|
|
{"Character::set_x", API_FN_PAIR(Character_SetX)},
|
|
{"Character::get_Y", API_FN_PAIR(Character_GetY)},
|
|
{"Character::set_Y", API_FN_PAIR(Character_SetY)},
|
|
{"Character::get_y", API_FN_PAIR(Character_GetY)},
|
|
{"Character::set_y", API_FN_PAIR(Character_SetY)},
|
|
{"Character::get_Z", API_FN_PAIR(Character_GetZ)},
|
|
{"Character::set_Z", API_FN_PAIR(Character_SetZ)},
|
|
{"Character::get_z", API_FN_PAIR(Character_GetZ)},
|
|
{"Character::set_z", API_FN_PAIR(Character_SetZ)},
|
|
{"Character::get_HasExplicitLight", API_FN_PAIR(Character_GetHasExplicitLight)},
|
|
{"Character::get_LightLevel", API_FN_PAIR(Character_GetLightLevel)},
|
|
{"Character::get_TintBlue", API_FN_PAIR(Character_GetTintBlue)},
|
|
{"Character::get_TintGreen", API_FN_PAIR(Character_GetTintGreen)},
|
|
{"Character::get_TintRed", API_FN_PAIR(Character_GetTintRed)},
|
|
{"Character::get_TintSaturation", API_FN_PAIR(Character_GetTintSaturation)},
|
|
{"Character::get_TintLuminance", API_FN_PAIR(Character_GetTintLuminance)},
|
|
};
|
|
|
|
ccAddExternalFunctions361(character_api);
|
|
|
|
// Few functions have to be selected based on API level
|
|
if (base_api < kScriptAPI_v350) {
|
|
ccAddExternalObjectFunction361("Character::LockViewAligned^3", API_FN_PAIR(Character_LockViewAligned_Old));
|
|
ccAddExternalObjectFunction361("Character::LockViewAligned^4", API_FN_PAIR(Character_LockViewAlignedEx_Old));
|
|
} else {
|
|
ccAddExternalObjectFunction361("Character::LockViewAligned^3", API_FN_PAIR(Character_LockViewAligned));
|
|
ccAddExternalObjectFunction361("Character::LockViewAligned^4", API_FN_PAIR(Character_LockViewAlignedEx));
|
|
}
|
|
|
|
if (base_api < kScriptAPI_v341) {
|
|
ccAddExternalObjectFunction361("Character::get_HasExplicitTint", API_FN_PAIR(Character_GetHasExplicitTint_Old));
|
|
} else {
|
|
ccAddExternalObjectFunction361("Character::get_HasExplicitTint", API_FN_PAIR(Character_GetHasExplicitTint));
|
|
}
|
|
}
|
|
|
|
} // namespace AGS3
|