Initial commit
This commit is contained in:
1968
engines/ultima/nuvie/actors/actor.cpp
Normal file
1968
engines/ultima/nuvie/actors/actor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
743
engines/ultima/nuvie/actors/actor.h
Normal file
743
engines/ultima/nuvie/actors/actor.h
Normal file
@@ -0,0 +1,743 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_ACTORS_ACTOR_H
|
||||
#define NUVIE_ACTORS_ACTOR_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/misc/actor_list.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::list;
|
||||
using Std::string;
|
||||
using Std::vector;
|
||||
|
||||
#define ACTOR_NO_READIABLE_LOCATION -1
|
||||
#define ACTOR_HEAD 0
|
||||
#define ACTOR_NECK 1
|
||||
#define ACTOR_BODY 2
|
||||
#define ACTOR_ARM 3
|
||||
#define ACTOR_ARM_2 4
|
||||
#define ACTOR_HAND 5
|
||||
#define ACTOR_HAND_2 6
|
||||
#define ACTOR_FOOT 7
|
||||
#define ACTOR_NOT_READIABLE 8
|
||||
|
||||
// actor alignment
|
||||
enum ActorAlignment {
|
||||
ACTOR_ALIGNMENT_DEFAULT = 0,
|
||||
ACTOR_ALIGNMENT_NEUTRAL = 1,
|
||||
ACTOR_ALIGNMENT_EVIL = 2,
|
||||
ACTOR_ALIGNMENT_GOOD = 3,
|
||||
ACTOR_ALIGNMENT_CHAOTIC = 4,
|
||||
};
|
||||
|
||||
// move-flags
|
||||
#define ACTOR_FORCE_MOVE 1
|
||||
#define ACTOR_IGNORE_OTHERS 2
|
||||
#define ACTOR_OPEN_DOORS 4
|
||||
#define ACTOR_IGNORE_DANGER 8
|
||||
#define ACTOR_IGNORE_MOVES 0x10
|
||||
#define ACTOR_IGNORE_PARTY_MEMBERS 0x20 // only used in Actor::check_move. In U6, player diagonal movement
|
||||
// between two blocked tiles isn't allowed (non-party actors block)
|
||||
|
||||
// push-flags (exclusive)
|
||||
#define ACTOR_PUSH_ANYWHERE 0
|
||||
#define ACTOR_PUSH_HERE 1
|
||||
#define ACTOR_PUSH_FORWARD 2
|
||||
|
||||
#define ACTOR_SHOW_BLOOD true
|
||||
#define ACTOR_FORCE_HIT true
|
||||
|
||||
#define ACTOR_STATUS_PROTECTED 0x1
|
||||
#define ACTOR_STATUS_PARALYZED 0x2
|
||||
#define ACTOR_STATUS_ASLEEP 0x4
|
||||
#define ACTOR_STATUS_POISONED 0x8
|
||||
#define ACTOR_STATUS_DEAD 0x10
|
||||
#define ACTOR_STATUS_ATTACK_EVIL 0x20
|
||||
#define ACTOR_STATUS_ATTACK_GOOD 0x40
|
||||
#define ACTOR_STATUS_IN_PARTY 0x80
|
||||
#define ACTOR_STATUS_ALIGNMENT_MASK 0x60
|
||||
|
||||
#define ACTOR_MOVEMENT_HIT_FLAG 0x8
|
||||
#define ACTOR_MOVEMENT_FLAGS_OLD_ALIGNMENT_MASK 0x60
|
||||
|
||||
#define ACTOR_OBJ_FLAG_
|
||||
#define ACTOR_NO_ERROR 0
|
||||
#define ACTOR_OUT_OF_MOVES 1
|
||||
#define ACTOR_BLOCKED 2
|
||||
#define ACTOR_BLOCKED_BY_OBJECT 3
|
||||
#define ACTOR_BLOCKED_BY_ACTOR 4
|
||||
|
||||
#define ACTOR_MAX_READIED_OBJECTS 8
|
||||
|
||||
#define ACTOR_WT_FOLLOW 1
|
||||
#define ACTOR_WT_PLAYER 2
|
||||
#define ACTOR_WT_RANGED 4
|
||||
#define ACTOR_WT_RETREAT 7
|
||||
#define ACTOR_WT_ASSAULT 8
|
||||
|
||||
#define ACTOR_CHANGE_BASE_OBJ_N true
|
||||
|
||||
#define ACTOR_VEHICLE_ID_N 0
|
||||
#define ACTOR_AVATAR_ID_N 1
|
||||
|
||||
#define INV_EXCLUDE_READIED_OBJECTS false
|
||||
#define INV_INCLUDE_READIED_OBJECTS true
|
||||
|
||||
#define ACTOR_MD_OBJ_FLAG_HYPOXIA 6
|
||||
#define ACTOR_MD_OBJ_FLAG_FRENZY 7
|
||||
|
||||
#define ACTOR_MD_STATUS_FLAG_COLD 0
|
||||
|
||||
class Map;
|
||||
class MapCoord;
|
||||
class UseCode;
|
||||
class ActorPathFinder;
|
||||
class U6LList;
|
||||
class GameClock;
|
||||
class Path;
|
||||
|
||||
typedef struct {
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint8 z;
|
||||
uint8 hour;
|
||||
uint8 day_of_week; // 0 = any day 1..7
|
||||
uint8 worktype;
|
||||
} Schedule;
|
||||
|
||||
typedef enum { ATTACK_TYPE_NONE, ATTACK_TYPE_HAND, ATTACK_TYPE_THROWN, ATTACK_TYPE_MISSLE } AttackType;
|
||||
|
||||
typedef struct {
|
||||
uint16 obj_n;
|
||||
|
||||
union {
|
||||
uint8 defence;
|
||||
uint8 defense;
|
||||
};
|
||||
union {
|
||||
uint8 attack;
|
||||
uint8 damage;
|
||||
};
|
||||
|
||||
uint16 hit_range;
|
||||
AttackType attack_type;
|
||||
|
||||
uint16 missle_tile_num;
|
||||
uint16 thrown_obj_n;
|
||||
|
||||
bool breaks_on_contact;
|
||||
} CombatType;
|
||||
|
||||
typedef struct {
|
||||
Obj *obj;
|
||||
const CombatType *combat_type;
|
||||
bool double_handed;
|
||||
} ReadiedObj;
|
||||
|
||||
typedef uint8 ActorErrorCode;
|
||||
typedef struct {
|
||||
ActorErrorCode err;
|
||||
Obj *blocking_obj;
|
||||
Actor *blocking_actor;
|
||||
} ActorError;
|
||||
|
||||
typedef uint8 ActorMoveFlags;
|
||||
|
||||
typedef enum {
|
||||
ACTOR_ST, // single tile
|
||||
ACTOR_DT, // double tile
|
||||
ACTOR_QT, // quad tile
|
||||
ACTOR_MT // multi tile
|
||||
} ActorTileType;
|
||||
|
||||
class Actor {
|
||||
friend class ActorManager;
|
||||
friend class MapWindow;
|
||||
friend class Party;
|
||||
friend class Player;
|
||||
friend class U6UseCode;
|
||||
|
||||
public:
|
||||
struct cmp_level {
|
||||
bool operator()(Actor *a1, Actor *a2) const {
|
||||
return (a1->level > a2->level);
|
||||
}
|
||||
};
|
||||
|
||||
struct cmp_dex {
|
||||
bool operator()(Actor *a1, Actor *a2) const {
|
||||
return (a1->dex > a2->dex);
|
||||
}
|
||||
};
|
||||
|
||||
struct cmp_moves {
|
||||
bool operator()(Actor *a1, Actor *a2) const {
|
||||
return (a1->moves > a2->moves);
|
||||
}
|
||||
};
|
||||
|
||||
struct cmp_move_fraction {
|
||||
bool operator()(Actor *a1, Actor *a2) const {
|
||||
if (a1->dex == 0) {
|
||||
a1->dex = 1;
|
||||
DEBUG(0, LEVEL_WARNING, "%s (%d) has 0 dex!\n", a1->get_name(), a1->id_n);
|
||||
}
|
||||
if (a2->dex == 0) {
|
||||
a2->dex = 1;
|
||||
DEBUG(0, LEVEL_WARNING, "%s (%d) has 0 dex!\n", a2->get_name(), a2->id_n);
|
||||
}
|
||||
return (a1->moves / a1->dex > a2->moves / a2->dex);
|
||||
}
|
||||
};
|
||||
|
||||
struct cmp_distance_to_loc {
|
||||
MapCoord cmp_loc;
|
||||
void operator()(const MapCoord &cmp_loc2) {
|
||||
cmp_loc = cmp_loc2;
|
||||
}
|
||||
bool operator()(Actor *a1, Actor *a2) {
|
||||
MapCoord loc1(a1->x, a1->y, a1->z);
|
||||
MapCoord loc2(a2->x, a2->y, a2->z);
|
||||
return (loc1.distance(cmp_loc) < loc2.distance(cmp_loc));
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
uint8 id_n;
|
||||
|
||||
Map *map;
|
||||
ObjManager *obj_manager;
|
||||
GameClock *_clock;
|
||||
UseCode *usecode;
|
||||
ActorPathFinder *pathfinder;
|
||||
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint16 z;
|
||||
|
||||
uint8 worktype;
|
||||
MapCoord work_location;
|
||||
uint32 move_time; // time (in clock ticks) of last update() (for display)
|
||||
// FIXME: replace with animation
|
||||
uint16 obj_n;
|
||||
uint16 frame_n;
|
||||
uint16 base_obj_n;
|
||||
uint16 old_frame_n;
|
||||
|
||||
NuvieDir direction;
|
||||
uint8 walk_frame;
|
||||
|
||||
uint8 obj_flags;
|
||||
uint8 status_flags;
|
||||
uint8 talk_flags;
|
||||
uint8 movement_flags; //0x19f1
|
||||
|
||||
bool ethereal;
|
||||
bool can_move;
|
||||
bool temp_actor;
|
||||
bool met_player;
|
||||
|
||||
bool visible_flag;
|
||||
// bool active; // "cached in"
|
||||
|
||||
sint8 moves; // number of moves actor has this turn
|
||||
uint8 light; // level of light around actor (normally 0)
|
||||
vector<uint8> light_source;
|
||||
|
||||
ActorError error_struct; // error/status; result of previous action
|
||||
|
||||
uint8 strength;
|
||||
uint8 dex;
|
||||
uint8 intelligence;
|
||||
uint8 hp;
|
||||
uint8 level;
|
||||
uint16 exp;
|
||||
uint8 magic;
|
||||
uint8 combat_mode;
|
||||
ActorAlignment alignment;
|
||||
|
||||
uint8 body_armor_class;
|
||||
uint8 readied_armor_class;
|
||||
|
||||
string name;
|
||||
|
||||
ReadiedObj *readied_objects[ACTOR_MAX_READIED_OBJECTS];
|
||||
|
||||
Schedule **sched;
|
||||
int num_schedules;
|
||||
|
||||
U6LList *obj_inventory;
|
||||
|
||||
//current schedule pos;
|
||||
uint16 sched_pos;
|
||||
|
||||
list<Obj *> surrounding_objects; //used for multi-tile actors.
|
||||
Common::HashMap<uint16, uint16> *custom_tile_tbl;
|
||||
|
||||
public:
|
||||
|
||||
Actor(Map *m, ObjManager *om, GameClock *c);
|
||||
virtual ~Actor();
|
||||
|
||||
virtual bool init(uint8 obj_status = NO_OBJ_STATUS);
|
||||
void init_from_obj(Obj *obj, bool change_base_obj = false);
|
||||
|
||||
bool is_avatar() const {
|
||||
return id_n == ACTOR_AVATAR_ID_N;
|
||||
}
|
||||
bool is_onscreen() const {
|
||||
return MapCoord(x, y, z).is_visible();
|
||||
}
|
||||
bool is_in_party() const {
|
||||
return ((status_flags & ACTOR_STATUS_IN_PARTY) == ACTOR_STATUS_IN_PARTY);
|
||||
}
|
||||
bool is_in_vehicle() const;
|
||||
bool is_visible() const {
|
||||
return visible_flag;
|
||||
}
|
||||
bool is_alive() const {
|
||||
return (status_flags & ACTOR_STATUS_DEAD) ? false : true;
|
||||
}
|
||||
bool is_nearby(const Actor *other) const;
|
||||
bool is_nearby(uint8 actor_num) const;
|
||||
bool is_nearby(const MapCoord &where, uint8 thresh = 5) const;
|
||||
bool is_at_position(const Obj *obj) const;
|
||||
virtual bool is_passable() const;
|
||||
bool is_temp() const {
|
||||
return temp_actor;
|
||||
}
|
||||
virtual bool isFlying() const {
|
||||
return false;
|
||||
}
|
||||
virtual bool isNonBlocking() const {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @brief Does any tile of this actor occupy the given world location?
|
||||
* @param lx world coordinate
|
||||
* @param ly world coordinate
|
||||
* @param lz level
|
||||
* @param incDoubleTile include all tiles of double width/height actors
|
||||
* @param incSurroundingObjs include surrounding actor objects
|
||||
* @return true if actor occupies location, false otherwise
|
||||
*/
|
||||
virtual bool doesOccupyLocation(uint16 lx, uint16 ly, uint8 lz, bool incDoubleTile = true, bool incSurroundingObjs = true) const;
|
||||
|
||||
//for lack of a better name:
|
||||
bool is_met() const {
|
||||
return talk_flags & 0x01;
|
||||
}
|
||||
bool is_poisoned() const {
|
||||
return status_flags & ACTOR_STATUS_POISONED;
|
||||
}
|
||||
bool is_invisible() const {
|
||||
return obj_flags & OBJ_STATUS_INVISIBLE;
|
||||
}
|
||||
virtual bool is_immobile() const; // frozen by worktype or status
|
||||
virtual bool is_sleeping() const {
|
||||
return status_flags & ACTOR_STATUS_ASLEEP;
|
||||
}
|
||||
virtual bool is_paralyzed() const {
|
||||
return status_flags & ACTOR_STATUS_PARALYZED;
|
||||
}
|
||||
virtual bool is_protected() const {
|
||||
return status_flags & ACTOR_STATUS_PROTECTED;
|
||||
}
|
||||
virtual bool is_charmed() const {
|
||||
return obj_flags & OBJ_STATUS_CHARMED;
|
||||
}
|
||||
virtual bool is_cursed() const {
|
||||
return obj_flags & OBJ_STATUS_CURSED;
|
||||
}
|
||||
virtual bool get_corpser_flag() const {
|
||||
return false;
|
||||
}
|
||||
bool is_hit() const {
|
||||
return movement_flags & ACTOR_MOVEMENT_HIT_FLAG;
|
||||
}
|
||||
|
||||
void set_name(const char *actor_name) {
|
||||
name = actor_name;
|
||||
}
|
||||
const char *get_name(bool force_real_name = false);
|
||||
|
||||
void get_location(uint16 *ret_x, uint16 *ret_y, uint8 *ret_level) const;
|
||||
MapCoord get_location() const;
|
||||
|
||||
uint16 get_tile_num() const;
|
||||
Tile *get_tile() const;
|
||||
virtual uint16 get_downward_facing_tile_num() const;
|
||||
uint8 get_actor_num() const {
|
||||
return id_n;
|
||||
}
|
||||
uint8 get_talk_flags() const {
|
||||
return talk_flags;
|
||||
}
|
||||
virtual ActorTileType get_tile_type() const {
|
||||
return ACTOR_ST;
|
||||
}
|
||||
|
||||
uint16 get_frame_n() const {
|
||||
return frame_n;
|
||||
}
|
||||
uint16 get_old_frame_n() const {
|
||||
return old_frame_n;
|
||||
}
|
||||
uint16 get_x() const {
|
||||
return x;
|
||||
}
|
||||
uint16 get_y() const {
|
||||
return y;
|
||||
}
|
||||
uint8 get_z() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
uint8 get_strength() const {
|
||||
return strength;
|
||||
}
|
||||
uint8 get_dexterity() const {
|
||||
return dex;
|
||||
}
|
||||
uint8 get_intelligence() const {
|
||||
return intelligence;
|
||||
}
|
||||
uint8 get_hp() const {
|
||||
return hp;
|
||||
}
|
||||
virtual uint8 get_hp_text_color() const {
|
||||
return 0;
|
||||
}
|
||||
virtual uint8 get_str_text_color() const {
|
||||
return 0;
|
||||
}
|
||||
virtual uint8 get_dex_text_color() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 get_level() const {
|
||||
return level;
|
||||
}
|
||||
uint16 get_exp() const {
|
||||
return exp;
|
||||
}
|
||||
uint8 get_magic() const {
|
||||
return magic;
|
||||
}
|
||||
ActorAlignment get_alignment() const {
|
||||
return alignment;
|
||||
}
|
||||
uint8 get_old_alignment() const {
|
||||
return ((movement_flags & ACTOR_MOVEMENT_FLAGS_OLD_ALIGNMENT_MASK) >> 5) + 1;
|
||||
}
|
||||
sint8 get_moves_left() const {
|
||||
return moves;
|
||||
}
|
||||
virtual uint8 get_maxhp() const {
|
||||
return 0;
|
||||
}
|
||||
virtual uint8 get_maxmagic() const {
|
||||
return 0;
|
||||
}
|
||||
bool get_obj_flag(uint8 bitFlag) const {
|
||||
return bitFlag < 8 ? (obj_flags & (1 << bitFlag)) : false;
|
||||
}
|
||||
bool get_status_flag(uint8 bitFlag) const {
|
||||
return bitFlag < 8 ? (status_flags & (1 << bitFlag)) : false;
|
||||
}
|
||||
|
||||
uint16 get_base_obj_n() const {
|
||||
return base_obj_n;
|
||||
}
|
||||
virtual void change_base_obj_n(uint16 val) {
|
||||
base_obj_n = obj_n = val;
|
||||
frame_n = 0;
|
||||
}
|
||||
void set_obj_n(uint16 val) {
|
||||
obj_n = val;
|
||||
}
|
||||
void set_frame_n(uint16 val) {
|
||||
frame_n = val;
|
||||
}
|
||||
void set_strength(uint8 val) {
|
||||
strength = val;
|
||||
}
|
||||
void set_dexterity(uint8 val) {
|
||||
dex = val;
|
||||
}
|
||||
void set_intelligence(uint8 val) {
|
||||
intelligence = val;
|
||||
}
|
||||
void set_hp(uint8 val);
|
||||
void set_level(uint8 val) {
|
||||
level = val;
|
||||
}
|
||||
void set_exp(uint16 val) {
|
||||
exp = clamp_max(val, 9999);
|
||||
}
|
||||
void set_magic(uint8 val) {
|
||||
magic = val;
|
||||
}
|
||||
void set_alignment(ActorAlignment a) {
|
||||
alignment = a;
|
||||
}
|
||||
void set_old_alignment(ActorAlignment a) {
|
||||
if (a > 0 && a < 5) {
|
||||
movement_flags |= (a - 1) << 5;
|
||||
}
|
||||
}
|
||||
uint8 get_light_level() const;
|
||||
void add_light(uint8 val);
|
||||
void subtract_light(uint8 val);
|
||||
void heal() {
|
||||
set_hp(get_maxhp());
|
||||
}
|
||||
void cure();
|
||||
void set_moves_left(sint8 val);
|
||||
void set_dead_flag(bool value);
|
||||
virtual void update_time() {
|
||||
set_moves_left(get_moves_left() + get_dexterity());
|
||||
}
|
||||
void set_poisoned(bool poisoned);
|
||||
virtual void set_paralyzed(bool paralyzed) {
|
||||
return;
|
||||
}
|
||||
virtual void set_protected(bool val) {
|
||||
return;
|
||||
}
|
||||
virtual void set_charmed(bool val) {
|
||||
return;
|
||||
}
|
||||
virtual void set_corpser_flag(bool val) {
|
||||
return;
|
||||
}
|
||||
virtual void set_cursed(bool val) {
|
||||
return;
|
||||
}
|
||||
virtual void set_asleep(bool val) {
|
||||
return;
|
||||
}
|
||||
void set_obj_flag(uint8 bitFlag, bool value);
|
||||
void set_status_flag(uint8 bitFlag, bool value);
|
||||
void set_hit_flag(bool val);
|
||||
|
||||
void set_invisible(bool invisible);
|
||||
void set_custom_tile_num(uint16 obj_num, uint16 tile_num);
|
||||
|
||||
uint8 get_worktype();
|
||||
uint8 get_sched_worktype();
|
||||
virtual void set_worktype(uint8 new_worktype, bool init = false);
|
||||
uint8 get_combat_mode() const {
|
||||
return combat_mode;
|
||||
}
|
||||
void set_combat_mode(uint8 new_mode);
|
||||
virtual void revert_worktype() { }
|
||||
|
||||
NuvieDir get_direction() const {
|
||||
return direction;
|
||||
}
|
||||
void set_direction(sint16 rel_x, sint16 rel_y);
|
||||
virtual void set_direction(NuvieDir d);
|
||||
void face_location(const MapCoord &loc);
|
||||
virtual void face_location(uint16 lx, uint16 ly);
|
||||
void face_actor(Actor *a);
|
||||
|
||||
void set_talk_flags(uint8 newflags) {
|
||||
talk_flags = newflags;
|
||||
}
|
||||
uint8 get_flag(uint8 bitflag);
|
||||
void set_flag(uint8 bitflag);
|
||||
void clear_flag(uint8 bitflag);
|
||||
void show();
|
||||
void hide();
|
||||
void set_error(ActorErrorCode err);
|
||||
void clear_error();
|
||||
ActorError *get_error();
|
||||
|
||||
const list<Obj *> &get_surrounding_obj_list() const {
|
||||
return surrounding_objects;
|
||||
}
|
||||
void add_surrounding_obj(Obj *obj);
|
||||
void unlink_surrounding_objects(bool make_objects_temporary = false);
|
||||
|
||||
bool moveRelative(sint16 rel_x, sint16 rel_y, ActorMoveFlags flags = 0);
|
||||
virtual bool move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags = 0);
|
||||
virtual bool check_move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags = 0);
|
||||
bool check_moveRelative(sint16 rel_x, sint16 rel_y, ActorMoveFlags flags = 0);
|
||||
|
||||
virtual bool can_be_moved();
|
||||
virtual bool can_be_passed(const Actor *other, bool ignoreParty = false) const;
|
||||
virtual void update();
|
||||
void set_in_party(bool state);
|
||||
void set_pathfinder(ActorPathFinder *new_pf, Path *path_type = 0);
|
||||
ActorPathFinder *get_pathfinder() {
|
||||
return pathfinder;
|
||||
}
|
||||
void delete_pathfinder();
|
||||
virtual void pathfind_to(const MapCoord &d);
|
||||
void pathfind_to(uint16 gx, uint16 gy, uint8 gz = 255);
|
||||
bool walk_path();
|
||||
virtual void preform_worktype() {
|
||||
return;
|
||||
}
|
||||
|
||||
// combat methods
|
||||
//void attack(const MapCoord &pos); // attack at a given map location
|
||||
Obj *get_weapon_obj(sint8 readied_obj_location);
|
||||
void attack(sint8 readied_obj_location, MapCoord target, Actor *foe = nullptr);
|
||||
const CombatType *get_weapon(sint8 readied_obj_location);
|
||||
void attract_to(Actor *target);
|
||||
void repel_from(Actor *target);
|
||||
|
||||
void hit(uint8 dmg, bool force_hit = false);
|
||||
void reduce_hp(uint8 amount);
|
||||
virtual void die(bool create_body = true);
|
||||
void resurrect(const MapCoord &new_position, Obj *body_obj = nullptr);
|
||||
uint8 get_range(uint16 target_x, uint16 target_y);
|
||||
bool weapon_can_hit(const CombatType *weapon, uint16 target_x, uint16 target_y);
|
||||
virtual bool weapon_can_hit(const CombatType *weapon, Actor *target, uint16 *hit_x, uint16 *hit_y) {
|
||||
*hit_x = target->get_x();
|
||||
*hit_y = target->get_y();
|
||||
return true;
|
||||
}
|
||||
void display_condition();
|
||||
ActorList *find_enemies(); // returns list or 0 if no enemies nearby
|
||||
|
||||
U6LList *get_inventory_list();
|
||||
const U6LList *get_inventory_list() const;
|
||||
bool inventory_has_object(uint16 obj_n, uint8 qual = 0, bool match_quality = OBJ_MATCH_QUALITY, uint8 frame_n = 0, bool match_frame_n = OBJ_NOMATCH_FRAME_N);
|
||||
uint32 inventory_count_objects(bool inc_readied_objects) const;
|
||||
uint32 inventory_count_object(uint16 obj_n);
|
||||
Obj *inventory_get_object(uint16 obj_n, uint8 qual = 0, bool match_quality = OBJ_MATCH_QUALITY, uint8 frame_n = 0, bool match_frame_n = OBJ_NOMATCH_FRAME_N);
|
||||
bool is_double_handed_obj_readied();
|
||||
Obj *inventory_get_readied_object(uint8 location);
|
||||
sint16 inventory_get_readied_obj_n(uint8 location) {
|
||||
return (inventory_get_readied_object(location) == nullptr ? -1 : inventory_get_readied_object(location)->obj_n);
|
||||
}
|
||||
virtual Obj *inventory_get_food(Obj *container = 0) {
|
||||
return 0;
|
||||
}
|
||||
const CombatType *inventory_get_readied_object_combat_type(uint8 location);
|
||||
bool inventory_add_object(Obj *obj, Obj *container = 0, bool stack = true);
|
||||
bool inventory_add_object_nostack(Obj *obj, Obj *container = 0) {
|
||||
return inventory_add_object(obj, container, false);
|
||||
}
|
||||
void inventory_del_all_objs();
|
||||
bool inventory_remove_obj(Obj *obj, bool run_usecode = true);
|
||||
Obj *inventory_new_object(uint16 obj_n, uint32 qty, uint8 quality = 0);
|
||||
uint32 inventory_del_object(uint16 obj_n, uint32 qty, uint8 quality);
|
||||
float inventory_get_max_weight() const {
|
||||
return strength * 2;
|
||||
}
|
||||
float get_inventory_weight() const;
|
||||
float get_inventory_equip_weight();
|
||||
void inventory_drop_all();
|
||||
void all_items_to_container(Obj *container_obj, bool stack);
|
||||
bool can_carry_weight(Obj *obj) const;
|
||||
bool can_carry_weight(float obj_weight) const; // return from get_obj_weight()
|
||||
virtual bool can_carry_object(uint16 obj_n, uint32 qty = 0) const;
|
||||
virtual bool can_carry_object(Obj *obj) const;
|
||||
|
||||
virtual uint8 get_object_readiable_location(Obj *obj);
|
||||
virtual const CombatType *get_object_combat_type(uint16 objN) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool can_ready_obj(Obj *obj);
|
||||
bool add_readied_object(Obj *obj);
|
||||
void remove_readied_object(Obj *obj, bool run_usecode = true); // run_usecode to stop from running usecode twice or an infinite loop
|
||||
void remove_readied_object(uint8 location, bool run_usecode = true);
|
||||
|
||||
void remove_all_readied_objects();
|
||||
bool has_readied_objects();
|
||||
sint8 count_readied_objects(sint32 obj_n = -1, sint16 frame_n = -1, sint16 quality = -1);
|
||||
|
||||
virtual void twitch() {
|
||||
return;
|
||||
}
|
||||
bool push(Actor *pusher, uint8 where = ACTOR_PUSH_ANYWHERE);
|
||||
|
||||
Obj *make_obj();
|
||||
uint16 get_obj_n() const {
|
||||
return obj_n;
|
||||
}
|
||||
virtual void clear();
|
||||
virtual bool morph(uint16 obj_n); // change actor type
|
||||
|
||||
bool get_schedule_location(MapCoord *loc) const;
|
||||
bool is_at_scheduled_location() const;
|
||||
int get_number_of_schedules() const {
|
||||
return num_schedules;
|
||||
}
|
||||
Schedule *get_schedule(uint8 index);
|
||||
virtual bool will_not_talk() const {
|
||||
return false;
|
||||
}
|
||||
uint16 get_custom_tile_num(uint16 obj_num) const;
|
||||
protected:
|
||||
|
||||
void loadSchedule(const unsigned char *schedule_data, uint16 num);
|
||||
virtual bool updateSchedule(uint8 hour, bool teleport = false);
|
||||
uint16 getSchedulePos(uint8 hour);
|
||||
// uint16 getSchedulePos(uint8 hour, uint8 day_of_week);
|
||||
// inline uint16 Actor::getSchedulePos(uint8 hour);
|
||||
|
||||
void inventory_parse_readied_objects(); //this is used to initialise the readied_objects array on load.
|
||||
|
||||
virtual const CombatType *get_hand_combat_type() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void set_ethereal(bool val) {
|
||||
ethereal = val;
|
||||
}
|
||||
virtual void print();
|
||||
virtual void handle_lightsource(uint8 hour) {
|
||||
return;
|
||||
}
|
||||
virtual const char *get_worktype_string(uint32 wt) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Obj *find_body();
|
||||
uint16 get_tile_num(uint16 obj_num) const;
|
||||
uint8 get_num_light_sources() const {
|
||||
return light_source.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
const char *get_actor_alignment_str(ActorAlignment alignment);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1218
engines/ultima/nuvie/actors/actor_manager.cpp
Normal file
1218
engines/ultima/nuvie/actors/actor_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
173
engines/ultima/nuvie/actors/actor_manager.h
Normal file
173
engines/ultima/nuvie/actors/actor_manager.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_ACTORS_ACTOR_MANAGER_H
|
||||
#define NUVIE_ACTORS_ACTOR_MANAGER_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/misc/actor_list.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Map;
|
||||
class TileManager;
|
||||
class GameClock;
|
||||
class MapCoord;
|
||||
|
||||
|
||||
#define ACTORMANAGER_MAX_ACTORS 256
|
||||
|
||||
class ActorManager {
|
||||
const Configuration *config;
|
||||
TileManager *tile_manager;
|
||||
ObjManager *obj_manager;
|
||||
|
||||
bool update; // ActorManager is not paused
|
||||
bool wait_for_player; // Player's turn; wait until updateActors() is called
|
||||
bool combat_movement; // Defines actor movement type (individual/party)
|
||||
bool should_clean_temp_actors; // If set, temp actors are cleaned when > 19 tiles from player.
|
||||
|
||||
Map *map;
|
||||
Actor *actors[ACTORMANAGER_MAX_ACTORS];
|
||||
uint8 player_actor;
|
||||
uint8 temp_actor_offset;
|
||||
GameClock *_clock;
|
||||
|
||||
uint16 last_obj_blk_x, last_obj_blk_y;
|
||||
uint8 last_obj_blk_z;
|
||||
uint16 cur_x, cur_y;
|
||||
uint8 cur_z;
|
||||
MapCoord *cmp_actor_loc; // data for sort_distance() & cmp_distance_to_loc()
|
||||
|
||||
public:
|
||||
|
||||
ActorManager(const Configuration *cfg, Map *m, TileManager *tm, ObjManager *om, GameClock *c);
|
||||
~ActorManager();
|
||||
|
||||
void init();
|
||||
void clean();
|
||||
|
||||
bool load(NuvieIO *objlist);
|
||||
bool save(NuvieIO *objlist);
|
||||
// ActorList
|
||||
ActorList *get_actor_list(); // *returns a NEW list*
|
||||
ActorList *sort_nearest(ActorList *list, uint16 x, uint16 y, uint8 z); // ascending distance
|
||||
ActorList *filter_distance(ActorList *list, uint16 x, uint16 y, uint8 z, uint16 dist);
|
||||
ActorList *filter_alignment(ActorList *list, ActorAlignment align);
|
||||
ActorList *filter_party(ActorList *list);
|
||||
|
||||
Actor *get_actor(uint8 actor_num) const;
|
||||
Actor *get_actor(uint16 x, uint16 y, uint8 z, bool inc_surrounding_objs = true, Actor *excluded_actor = nullptr);
|
||||
Actor *get_actor_holding_obj(Obj *obj);
|
||||
|
||||
private:
|
||||
Actor *findActorAtImpl(uint16 x, uint16 y, uint8 z, bool(*predicateWrapper)(void *, const Actor *), bool incDoubleTile, bool incSurroundingObjs, void *predicate) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Find first actor at location for which predicate function returns true
|
||||
* @param x world coordinate
|
||||
* @param y world coordinate
|
||||
* @param z level
|
||||
* @param predicate predicate function/lambda of the form: bool f(const Actor*)
|
||||
* @param incDoubleTile include all tiles of double width/height actors
|
||||
* @param incSurroundingObjs include surrounding actor objects
|
||||
* @return pointer to actor or nullptr
|
||||
*/
|
||||
template<typename F>
|
||||
Actor *findActorAt(uint16 x, uint16 y, uint8 z, F predicate, bool incDoubleTile = true, bool incSurroundingObjs = true) {
|
||||
// This is a template so it can take lambdas.
|
||||
// To keep the implementation out of the header, the type of the passed lambda/function
|
||||
// is hidden inside a wrapping lambda, which is then passed as a function pointer.
|
||||
// TODO: Use a class for this.
|
||||
auto predicateWrapper = +[](void *wrappedPredicate, const Actor *a) -> bool {
|
||||
return (*(F*)wrappedPredicate)(a);
|
||||
};
|
||||
return findActorAtImpl(x, y, z, predicateWrapper, incDoubleTile, incSurroundingObjs, &predicate);
|
||||
}
|
||||
|
||||
Actor *get_avatar();
|
||||
|
||||
Actor *get_player();
|
||||
void set_player(Actor *a);
|
||||
|
||||
const char *look_actor(const Actor *a, bool show_prefix = true);
|
||||
|
||||
void set_update(bool u) {
|
||||
update = u;
|
||||
}
|
||||
bool get_update() const {
|
||||
return update;
|
||||
}
|
||||
void set_combat_movement(bool c);
|
||||
|
||||
void updateActors(uint16 x, uint16 y, uint8 z);
|
||||
void twitchActors();
|
||||
void moveActors();
|
||||
void startActors();
|
||||
void updateSchedules(bool teleport = false);
|
||||
|
||||
void clear_actor(Actor *actor);
|
||||
bool resurrect_actor(Obj *actor_obj, MapCoord new_position);
|
||||
|
||||
bool is_temp_actor(Actor *actor);
|
||||
bool is_temp_actor(uint8 id_n);
|
||||
bool create_temp_actor(uint16 obj_n, uint8 obj_status, uint16 x, uint16 y, uint8 z, ActorAlignment alignment, uint8 worktype, Actor **new_actor = nullptr);
|
||||
bool clone_actor(Actor *actor, Actor **new_actor, MapCoord new_location);
|
||||
bool toss_actor(Actor *actor, uint16 xrange, uint16 yrange);
|
||||
bool toss_actor_get_location(uint16 start_x, uint16 start_y, uint8 start_z, uint16 xrange, uint16 yrange, MapCoord *location);
|
||||
void print_actor(Actor *actor);
|
||||
bool can_put_actor(const MapCoord &location);
|
||||
void enable_temp_actor_cleaning(bool value) {
|
||||
should_clean_temp_actors = value;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Actor *get_multi_tile_actor(uint16 x, uint16 y, uint8 z);
|
||||
|
||||
bool loadActorSchedules();
|
||||
inline Actor *find_free_temp_actor();
|
||||
inline ActorList *filter_active_actors(ActorList *list, uint16 x, uint16 y, uint8 z);
|
||||
|
||||
void update_temp_actors(uint16 x, uint16 y, uint8 z);
|
||||
void clean_temp_actors_from_level(uint8 level);
|
||||
void clean_temp_actors_from_area(uint16 x, uint16 y);
|
||||
|
||||
inline void clean_temp_actor(Actor *actor);
|
||||
|
||||
private:
|
||||
|
||||
bool loadCustomTiles(nuvie_game_t game_type);
|
||||
void loadNPCTiles(const Common::Path &datadir);
|
||||
void loadAvatarTiles(const Common::Path &datadir);
|
||||
void loadCustomBaseTiles();
|
||||
Std::vector<Std::string> getCustomTileFilenames(const Common::Path &datadir, const Std::string &filenamePrefix);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
150
engines/ultima/nuvie/actors/md_actor.cpp
Normal file
150
engines/ultima/nuvie/actors/md_actor.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/actors/md_actor.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/pathfinder/dir_finder.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define MD_DOWNWARD_FACING_FRAME_N 9
|
||||
|
||||
extern const uint8 walk_frame_tbl[4];
|
||||
|
||||
MDActor::MDActor(Map *m, ObjManager *om, GameClock *c) : WOUActor(m, om, c) {
|
||||
}
|
||||
|
||||
MDActor::~MDActor() {
|
||||
}
|
||||
|
||||
bool MDActor::init(uint8) {
|
||||
Actor::init();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MDActor::will_not_talk() const {
|
||||
if (worktype == 0xa0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MDActor::is_immobile() const {
|
||||
return (obj_n == 294 || obj_n == 295 || obj_n == 318 || obj_n == 319); //avatar wall walking objects
|
||||
}
|
||||
|
||||
bool MDActor::check_move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags) {
|
||||
if (ethereal)
|
||||
return true;
|
||||
|
||||
if (Actor::check_move(new_x, new_y, new_z, flags) == false)
|
||||
return false;
|
||||
|
||||
if (z == new_z) { //FIXME check if new pos is adjacent to current position
|
||||
NuvieDir movement_dir = DirFinder::get_nuvie_dir(x, y, new_x, new_y, z);
|
||||
// printf("%d (%d,%d) -> (%d,%d) move = %d %s\n", id_n, x, y, new_x, new_y, movement_dir, get_direction_name(movement_dir));
|
||||
return map->is_passable(new_x, new_y, new_z, movement_dir);
|
||||
}
|
||||
|
||||
return map->is_passable(new_x, new_y, new_z);
|
||||
}
|
||||
|
||||
uint16 MDActor::get_downward_facing_tile_num() const {
|
||||
return get_tile_num(base_obj_n) + (uint16) MD_DOWNWARD_FACING_FRAME_N;
|
||||
}
|
||||
|
||||
uint8 MDActor::get_hp_text_color() const {
|
||||
if (is_poisoned())
|
||||
return 4;
|
||||
|
||||
if (get_status_flag(ACTOR_MD_STATUS_FLAG_COLD))
|
||||
return 0xf;
|
||||
|
||||
if (get_hp() <= 10)
|
||||
return 0xc;
|
||||
|
||||
if (get_obj_flag(ACTOR_MD_OBJ_FLAG_HYPOXIA))
|
||||
return 9;
|
||||
|
||||
if (get_obj_flag(ACTOR_MD_OBJ_FLAG_FRENZY) && id_n != 1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 MDActor::get_str_text_color() const {
|
||||
uint8 color = 0;
|
||||
if (get_obj_flag(ACTOR_MD_OBJ_FLAG_HYPOXIA))
|
||||
color = 9;
|
||||
|
||||
if (id_n <= 0xf && Game::get_game()->get_clock()->get_purple_berry_counter(id_n) > 0) {
|
||||
color = 0xd;
|
||||
} else if (get_obj_flag(ACTOR_MD_OBJ_FLAG_FRENZY)) { //battle frenzy
|
||||
color = 1;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
uint8 MDActor::get_dex_text_color() const {
|
||||
uint8 color = 0;
|
||||
if (get_obj_flag(ACTOR_MD_OBJ_FLAG_HYPOXIA))
|
||||
color = 9;
|
||||
else if (get_obj_flag(ACTOR_MD_OBJ_FLAG_FRENZY)) {
|
||||
color = 1;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void MDActor::set_direction(NuvieDir d) {
|
||||
if (!is_alive() || is_immobile())
|
||||
return;
|
||||
|
||||
if (d < 4)
|
||||
direction = d;
|
||||
|
||||
if (obj_n == 391) { //mother only has two (downward facing) tiles
|
||||
frame_n = (uint16)(frame_n ? 0 : 1);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 num_walk_frames = 2;
|
||||
|
||||
if (obj_n >= 342 && obj_n <= 358) {
|
||||
num_walk_frames = 4;
|
||||
}
|
||||
|
||||
walk_frame = (uint8)((walk_frame + 1) % num_walk_frames);
|
||||
|
||||
frame_n = direction * num_walk_frames + walk_frame_tbl[walk_frame];
|
||||
}
|
||||
|
||||
bool MDActor::is_passable() const {
|
||||
if (obj_n == 391) { //FIXME hack for mother.
|
||||
return false;
|
||||
}
|
||||
return Actor::is_passable();
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
56
engines/ultima/nuvie/actors/md_actor.h
Normal file
56
engines/ultima/nuvie/actors/md_actor.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_ACTORS_MD_ACTOR_H
|
||||
#define NUVIE_ACTORS_MD_ACTOR_H
|
||||
|
||||
#include "ultima/nuvie/actors/wou_actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class MDActor: public WOUActor {
|
||||
public:
|
||||
|
||||
MDActor(Map *m, ObjManager *om, GameClock *c);
|
||||
~MDActor() override;
|
||||
|
||||
bool init(uint8 unused = 0) override;
|
||||
bool will_not_talk() const override;
|
||||
uint8 get_maxhp() const override {
|
||||
return (((level * 24 + strength * 2) < 255) ? (level * 24 + strength * 2) : 255);
|
||||
}
|
||||
uint8 get_hp_text_color() const override;
|
||||
uint8 get_str_text_color() const override;
|
||||
uint8 get_dex_text_color() const override;
|
||||
bool is_immobile() const override;
|
||||
|
||||
bool check_move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags = 0) override;
|
||||
uint16 get_downward_facing_tile_num() const override;
|
||||
void set_direction(NuvieDir d) override;
|
||||
bool is_passable() const override;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
45
engines/ultima/nuvie/actors/se_actor.cpp
Normal file
45
engines/ultima/nuvie/actors/se_actor.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/actors/se_actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
SEActor::SEActor(Map *m, ObjManager *om, GameClock *c): WOUActor(m, om, c) {
|
||||
}
|
||||
|
||||
SEActor::~SEActor() {
|
||||
}
|
||||
|
||||
bool SEActor::init(uint8) {
|
||||
Actor::init();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SEActor::will_not_talk() const {
|
||||
if (worktype == 0x07 || worktype == 0x8 || worktype == 0x9b)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
47
engines/ultima/nuvie/actors/se_actor.h
Normal file
47
engines/ultima/nuvie/actors/se_actor.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_ACTORS_SE_ACTOR_H
|
||||
#define NUVIE_ACTORS_SE_ACTOR_H
|
||||
|
||||
#include "ultima/nuvie/actors/wou_actor.h"
|
||||
#include "ultima/nuvie/misc/actor_list.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class SEActor: public WOUActor {
|
||||
public:
|
||||
|
||||
SEActor(Map *m, ObjManager *om, GameClock *c);
|
||||
~SEActor() override;
|
||||
|
||||
bool init(uint8 unused = 0) override;
|
||||
bool will_not_talk() const override;
|
||||
uint8 get_maxhp() const override {
|
||||
return (((level * 4 + strength * 2) < 255) ? (level * 4 + strength * 2) : 255);
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1689
engines/ultima/nuvie/actors/u6_actor.cpp
Normal file
1689
engines/ultima/nuvie/actors/u6_actor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
205
engines/ultima/nuvie/actors/u6_actor.h
Normal file
205
engines/ultima/nuvie/actors/u6_actor.h
Normal file
@@ -0,0 +1,205 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_ACTORS_U6_ACTOR_H
|
||||
#define NUVIE_ACTORS_U6_ACTOR_H
|
||||
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/misc/actor_list.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
enum ActorMovetype {
|
||||
MOVETYPE_U6_NONE = 0,
|
||||
MOVETYPE_U6_LAND = 1,
|
||||
MOVETYPE_U6_WATER_LOW = 2, // skiffs, rafts
|
||||
MOVETYPE_U6_WATER_HIGH = 3, // ships
|
||||
MOVETYPE_U6_AIR_LOW = 4, // balloon, birds... this movetype cannot cross mountain tops.
|
||||
MOVETYPE_U6_AIR_HIGH = 5, // dragons
|
||||
MOVETYPE_U6_ETHEREAL = 6,
|
||||
};
|
||||
|
||||
#define REMOVE_SURROUNDING_OBJS true
|
||||
|
||||
#define ACTOR_MOVEMENT_FLAGS_CORPSER 0x10
|
||||
|
||||
typedef struct {
|
||||
uint16 base_obj_n;
|
||||
uint8 frames_per_direction;
|
||||
uint8 tiles_per_direction;
|
||||
uint8 tiles_per_frame;
|
||||
uint8 tile_start_offset; //used for ships where the frame_n starts at 8
|
||||
uint16 dead_obj_n;
|
||||
uint8 dead_frame_n;
|
||||
bool can_laydown;
|
||||
bool can_sit;
|
||||
ActorTileType tile_type;
|
||||
ActorMovetype movetype;
|
||||
uint16 twitch_rand; //used to control how frequently an actor twitches, lower numbers twitch more
|
||||
uint8 body_armor_class;
|
||||
} U6ActorType;
|
||||
|
||||
class U6Actor: public Actor {
|
||||
protected:
|
||||
|
||||
const U6ActorType *actor_type;
|
||||
const U6ActorType *base_actor_type;
|
||||
ActorMovetype current_movetype;
|
||||
|
||||
sint8 walk_frame_inc; // added to walk_frame each step
|
||||
|
||||
public:
|
||||
|
||||
U6Actor(Map *m, ObjManager *om, GameClock *c);
|
||||
~U6Actor() override;
|
||||
|
||||
bool init(uint8 obj_status = NO_OBJ_STATUS) override;
|
||||
uint16 get_downward_facing_tile_num() const override;
|
||||
bool updateSchedule(uint8 hour, bool teleport = false) override;
|
||||
void set_worktype(uint8 new_worktype, bool init = false) override;
|
||||
void revert_worktype() override;
|
||||
void change_base_obj_n(uint16 val) override;
|
||||
void set_direction(NuvieDir d) override;
|
||||
void face_location(uint16 lx, uint16 ly) override;
|
||||
void clear() override;
|
||||
bool move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags = 0) override;
|
||||
bool check_move(uint16 new_x, uint16 new_y, uint8 new_z, ActorMoveFlags flags = 0) override;
|
||||
void twitch() override;
|
||||
void do_twitch();
|
||||
void die(bool create_body = true) override;
|
||||
void set_paralyzed(bool paralyzed) override;
|
||||
void set_protected(bool val) override;
|
||||
void set_charmed(bool val) override;
|
||||
void set_corpser_flag(bool val) override;
|
||||
void set_cursed(bool val) override;
|
||||
void set_asleep(bool val) override;
|
||||
void set_ethereal(bool val) override {
|
||||
current_movetype = val ? MOVETYPE_U6_ETHEREAL : actor_type->movetype;
|
||||
ethereal = val;
|
||||
}
|
||||
|
||||
uint8 get_object_readiable_location(Obj *obj) override;
|
||||
const CombatType *get_object_combat_type(uint16 objN) override;
|
||||
ActorTileType get_tile_type() const override {
|
||||
return (actor_type->tile_type);
|
||||
}
|
||||
Obj *inventory_get_food(Obj *container = 0) override;
|
||||
uint8 get_maxhp() const override {
|
||||
return (((level * 30) <= 255) ? (level * 30) : 255); // U6
|
||||
}
|
||||
uint8 get_maxmagic() const override;
|
||||
|
||||
bool weapon_can_hit(const CombatType *weapon, Actor *target, uint16 *hit_x, uint16 *hit_y) override;
|
||||
|
||||
bool is_immobile() const override; // frozen by worktype or status
|
||||
bool can_twitch();
|
||||
|
||||
bool get_corpser_flag() const override {
|
||||
return (movement_flags & ACTOR_MOVEMENT_FLAGS_CORPSER);
|
||||
}
|
||||
bool can_be_passed(const Actor *other, bool ignoreParty) const override;
|
||||
bool will_not_talk() const override;
|
||||
|
||||
void set_actor_obj_n(uint16 new_obj_n);
|
||||
void pathfind_to(const MapCoord &d) override;
|
||||
void handle_lightsource(uint8 hour) override;
|
||||
|
||||
uint8 get_hp_text_color() const override;
|
||||
uint8 get_str_text_color() const override {
|
||||
return 0x48;
|
||||
}
|
||||
uint8 get_dex_text_color() const override {
|
||||
return 0x48;
|
||||
}
|
||||
bool isFlying() const override {
|
||||
// FIXME: Get flying flag from lua actor_tbl
|
||||
// in devtools/create_ultima/files/ultima6/scripts/u6/actor.lua
|
||||
const uint16 flyingObjs[] = {
|
||||
OBJ_U6_INSECTS, OBJ_U6_GIANT_BAT, OBJ_U6_GAZER, OBJ_U6_BIRD,
|
||||
OBJ_U6_WINGED_GARGOYLE, OBJ_U6_DAEMON, OBJ_U6_DRAKE,
|
||||
OBJ_U6_MONGBAT, OBJ_U6_DRAGON, OBJ_U6_INFLATED_BALLOON };
|
||||
|
||||
for (const auto flyingObj : flyingObjs)
|
||||
if (obj_n == flyingObj)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool isNonBlocking() const override {
|
||||
// These are hard-coded in original U6
|
||||
const uint16 u6NonBlockingObjs[] = {
|
||||
OBJ_U6_INSECTS, OBJ_U6_MOUSE, OBJ_U6_BIRD, OBJ_U6_CORPSER,
|
||||
OBJ_U6_RABBIT };
|
||||
|
||||
for (const auto nonBlockingObj : u6NonBlockingObjs)
|
||||
if (obj_n == nonBlockingObj)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool init_ship();
|
||||
bool init_splitactor(uint8 obj_status); //cows, horses etc.
|
||||
bool init_dragon();
|
||||
bool init_hydra();
|
||||
bool init_silver_serpent();
|
||||
void init_new_silver_serpent();
|
||||
void gather_snake_objs_from_map(Obj *start_obj, uint16 ax, uint16 ay, uint16 az);
|
||||
inline bool check_move_silver_serpent(uint16 x, uint16 y);
|
||||
bool sit_on_chair(Obj *obj);
|
||||
|
||||
inline void discover_direction();
|
||||
void setup_walk_to_location();
|
||||
void wt_sleep(bool init = false);
|
||||
void wt_play_lute();
|
||||
|
||||
inline const U6ActorType *get_actor_type(uint16 new_obj_n);
|
||||
|
||||
inline bool has_surrounding_objs();
|
||||
inline void remove_surrounding_objs_from_map();
|
||||
inline void add_surrounding_objs_to_map();
|
||||
inline void move_surrounding_objs_relative(sint16 rel_x, sint16 rel_y);
|
||||
inline void move_silver_serpent_objs_relative(sint16 rel_x, sint16 rel_y);
|
||||
inline void set_direction_of_surrounding_objs(NuvieDir new_direction);
|
||||
inline void set_direction_of_surrounding_ship_objs(NuvieDir new_direction);
|
||||
inline void set_direction_of_surrounding_splitactor_objs(NuvieDir new_direction);
|
||||
inline void set_direction_of_surrounding_dragon_objs(NuvieDir new_direction);
|
||||
|
||||
inline void twitch_surrounding_objs();
|
||||
inline void twitch_surrounding_dragon_objs();
|
||||
inline void twitch_surrounding_hydra_objs();
|
||||
inline void twitch_obj(Obj *obj);
|
||||
|
||||
inline void clear_surrounding_objs_list(bool delete_objs = false);
|
||||
inline void init_surrounding_obj(uint16 x, uint16 y, uint8 z, uint16 actor_obj_n, uint16 obj_frame_n);
|
||||
|
||||
const CombatType *get_hand_combat_type() const override;
|
||||
|
||||
void print() override;
|
||||
const char *get_worktype_string(uint32 wt) const override;
|
||||
void inventory_make_all_objs_ok_to_take();
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
307
engines/ultima/nuvie/actors/u6_actor_types.h
Normal file
307
engines/ultima/nuvie/actors/u6_actor_types.h
Normal file
@@ -0,0 +1,307 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA_ULTIMA6_ACTORS_U6_ACTOR_TYPES_H
|
||||
#define ULTIMA_ULTIMA6_ACTORS_U6_ACTOR_TYPES_H
|
||||
|
||||
#include "ultima/nuvie/actors/u6_actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/*
|
||||
base_obj_n
|
||||
frames_per_direction
|
||||
tiles_per_direction
|
||||
tiles_per_frame
|
||||
tile_start_offset
|
||||
dead_obj_n
|
||||
dead_frame_n // 255 means same frame as they died
|
||||
can_laydown
|
||||
can_sit
|
||||
tile_type
|
||||
movetype
|
||||
twitch_rand
|
||||
body_armor_class
|
||||
*/
|
||||
|
||||
const U6ActorType u6ActorTypes[] = {
|
||||
// 4x1 tile actors AC
|
||||
{OBJ_U6_INSECTS, 0, 0, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_AIR_LOW, 2, 0},
|
||||
{OBJ_U6_GIANT_SQUID, 0, 0, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_WATER_HIGH, 50, 0},
|
||||
{OBJ_U6_GHOST, 0, 0, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 45, 0},
|
||||
{OBJ_U6_ACID_SLUG, 0, 0, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_WISP, 0, 0, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_AIR_LOW, 50, 0},
|
||||
{OBJ_U6_GIANT_BAT, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_AIR_LOW, 30, 0},
|
||||
{OBJ_U6_REAPER, 0, 0, 1, 0, OBJ_U6_REAPER, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 40, 4},
|
||||
{OBJ_U6_GREMLIN, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 20, 0},
|
||||
{OBJ_U6_GAZER, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 30, 0},
|
||||
{OBJ_U6_BIRD, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_AIR_LOW, 20, 0},
|
||||
{OBJ_U6_CORPSER, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_RABBIT, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 25, 0},
|
||||
{OBJ_U6_ROT_WORMS, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_HYDRA, 0, 0, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_MT, MOVETYPE_U6_LAND, 5, 0},
|
||||
{OBJ_U6_MOUSE, 1, 1, 1, 0, OBJ_U6_MOUSE, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 0, 0},
|
||||
{OBJ_U6_CAT, 1, 1, 1, 0, OBJ_U6_CAT, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 40, 0},
|
||||
{OBJ_U6_TANGLE_VINE, 1, 1, 1, 0, OBJ_U6_TANGLE_VINE, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 0, 4},
|
||||
|
||||
// 4x2
|
||||
{OBJ_U6_SEA_SERPENT, 2, 2, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_WATER_HIGH, 60, 2},
|
||||
{OBJ_U6_GIANT_RAT, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 40, 0},
|
||||
{OBJ_U6_SHEEP, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 35, 0},
|
||||
{OBJ_U6_DOG, 2, 2, 1, 0, OBJ_U6_DOG, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 35, 0},
|
||||
{OBJ_U6_DEER, 2, 2, 1, 0, OBJ_U6_DEER, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 20, 0},
|
||||
{OBJ_U6_WOLF, 2, 2, 1, 0, OBJ_U6_WOLF, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 20, 0},
|
||||
{OBJ_U6_SNAKE, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 20, 1},
|
||||
{OBJ_U6_GIANT_SPIDER, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 20, 0},
|
||||
{OBJ_U6_DRAKE, 2, 2, 1, 0, OBJ_U6_DRAKE, 255, false, false, ACTOR_ST, MOVETYPE_U6_AIR_LOW, 15, 4},
|
||||
{OBJ_U6_MONGBAT, 2, 2, 1, 0, OBJ_U6_MONGBAT, 255, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 15, 4},
|
||||
{OBJ_U6_DAEMON, 2, 2, 1, 0, OBJ_U6_DEAD_BODY, 0, true, false, ACTOR_ST, MOVETYPE_U6_LAND, 30, 10},
|
||||
{OBJ_U6_SKELETON, 2, 2, 1, 0, OBJ_U6_DEAD_BODY, 9, true, false, ACTOR_ST, MOVETYPE_U6_LAND, 30, 0},
|
||||
{OBJ_U6_HEADLESS, 2, 2, 1, 0, OBJ_U6_DEAD_BODY, 1, true, false, ACTOR_ST, MOVETYPE_U6_LAND, 30, 2},
|
||||
{OBJ_U6_TROLL, 2, 2, 1, 0, OBJ_U6_DEAD_BODY, 0, true, false, ACTOR_ST, MOVETYPE_U6_LAND, 40, 4},
|
||||
{OBJ_U6_CYCLOPS, 2, 8, 4, 0, OBJ_U6_DEAD_CYCLOPS, 3, true, false, ACTOR_QT, MOVETYPE_U6_LAND, 45, 4},
|
||||
|
||||
// 4x3 fix dead frame
|
||||
{OBJ_U6_WINGED_GARGOYLE, 3, 12, 4, 0, OBJ_U6_DEAD_GARGOYLE, 3, true, false, ACTOR_QT, MOVETYPE_U6_LAND, 60, 10},
|
||||
{OBJ_U6_GARGOYLE, 3, 3, 1, 0, OBJ_U6_DEAD_BODY, 0, true, false, ACTOR_ST, MOVETYPE_U6_LAND, 50, 5},
|
||||
|
||||
// 4x5
|
||||
{OBJ_U6_FIGHTER, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 6, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_SWASHBUCKLER, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 5, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_MAGE, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 3, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_VILLAGER, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 2, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_MERCHANT, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 2, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_CHILD, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 8, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 30, 0},
|
||||
{OBJ_U6_GUARD, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 4, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 35, 0},
|
||||
{OBJ_U6_JESTER, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 8, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 5, 0},
|
||||
{OBJ_U6_PEASANT, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 5, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0}, //not sure of stats here
|
||||
{OBJ_U6_FARMER, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 8, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 40, 0},
|
||||
{OBJ_U6_MUSICIAN, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 7, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_WOMAN, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 3, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
{OBJ_U6_LORD_BRITISH, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 2, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 60, 30}, //does LB have a dead frame!? ;)
|
||||
{OBJ_U6_AVATAR, 3, 4, 1, 0, OBJ_U6_DEAD_BODY, 7, true, true, ACTOR_ST, MOVETYPE_U6_LAND, 50, 0},
|
||||
|
||||
{OBJ_U6_MUSICIAN_PLAYING, 2, 2, 1, 0, OBJ_U6_NOTHING, 0, false, true, ACTOR_ST, MOVETYPE_U6_LAND, 3, 0},
|
||||
|
||||
{OBJ_U6_SHIP, 1, 2, 2, 8, OBJ_U6_NOTHING, 0, false, false, ACTOR_MT, MOVETYPE_U6_WATER_HIGH, 0, 30},
|
||||
{OBJ_U6_SKIFF, 1, 1, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_WATER_LOW, 0, 0},
|
||||
{OBJ_U6_INFLATED_BALLOON, 0, 0, 0, 4, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_AIR_LOW, 0, 0},
|
||||
|
||||
{OBJ_U6_GIANT_SCORPION, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_DT, MOVETYPE_U6_LAND, 30, 3},
|
||||
{OBJ_U6_GIANT_ANT, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_DT, MOVETYPE_U6_LAND, 30, 3},
|
||||
{OBJ_U6_COW, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_DT, MOVETYPE_U6_LAND, 40, 0},
|
||||
{OBJ_U6_ALLIGATOR, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_DT, MOVETYPE_U6_LAND, 30, 6},
|
||||
{OBJ_U6_HORSE, 2, 2, 1, 0, OBJ_U6_HORSE_CARCASS, 1, false, false, ACTOR_DT, MOVETYPE_U6_LAND, 20, 0},
|
||||
{OBJ_U6_HORSE_WITH_RIDER, 2, 2, 1, 0, OBJ_U6_BLOOD, 0, false, false, ACTOR_DT, MOVETYPE_U6_LAND, 20, 0},
|
||||
|
||||
{OBJ_U6_DRAGON, 2, 2, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_MT, MOVETYPE_U6_AIR_LOW, 10, 12},
|
||||
{OBJ_U6_SILVER_SERPENT, 1, 2, 1, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_MT, MOVETYPE_U6_LAND, 0, 15},
|
||||
|
||||
// 2x1 FIXME
|
||||
{OBJ_U6_RAFT, 0, 0, 0, 1, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_WATER_LOW, 0, 0}, // FIX might need to fix this
|
||||
{OBJ_U6_TANGLE_VINE_POD, 2, 2, 1, 0, OBJ_U6_TANGLE_VINE_POD, 255, false, false, ACTOR_ST, MOVETYPE_U6_NONE, 20, 4}, // Movement type is probably a temp fix
|
||||
|
||||
{OBJ_U6_SLIME, 0, 0, 0, 0, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 0, 0},
|
||||
|
||||
// 1x1
|
||||
{OBJ_U6_CHEST, 0, 0, 0, 1, OBJ_U6_CHEST, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 5, 0},
|
||||
|
||||
{OBJ_U6_NOTHING, 0, 0, 0, 1, OBJ_U6_NOTHING, 0, false, false, ACTOR_ST, MOVETYPE_U6_LAND, 0, 0} //end indicator
|
||||
};
|
||||
|
||||
// A list of readiable objects and their readied location.
|
||||
|
||||
static const struct {
|
||||
uint16 obj_n;
|
||||
uint8 readiable_location;
|
||||
uint8 defence;
|
||||
uint8 attack;
|
||||
}
|
||||
readiable_objects[] = {
|
||||
{OBJ_U6_LEATHER_HELM, ACTOR_HEAD, 1, 0 },
|
||||
{OBJ_U6_CHAIN_COIF, ACTOR_HEAD, 2, 0 },
|
||||
{OBJ_U6_IRON_HELM, ACTOR_HEAD, 3, 0 },
|
||||
{OBJ_U6_SPIKED_HELM, ACTOR_HEAD, 3, 4 },
|
||||
{OBJ_U6_WINGED_HELM, ACTOR_HEAD, 2, 0 },
|
||||
{OBJ_U6_BRASS_HELM, ACTOR_HEAD, 2, 0 },
|
||||
{OBJ_U6_GARGOYLE_HELM, ACTOR_HEAD, 3, 0 },
|
||||
{OBJ_U6_MAGIC_HELM, ACTOR_HEAD, 5, 0 },
|
||||
{OBJ_U6_WOODEN_SHIELD, ACTOR_ARM, 2, 0 },
|
||||
{OBJ_U6_CURVED_HEATER, ACTOR_ARM, 3, 0 },
|
||||
{OBJ_U6_WINGED_SHIELD, ACTOR_ARM, 3, 0 },
|
||||
{OBJ_U6_KITE_SHIELD, ACTOR_ARM, 3, 0 },
|
||||
{OBJ_U6_SPIKED_SHIELD, ACTOR_ARM, 2, 4 },
|
||||
{OBJ_U6_BLACK_SHIELD, ACTOR_ARM, 2, 0 },
|
||||
{OBJ_U6_DOOR_SHIELD, ACTOR_ARM, 4, 0 },
|
||||
{OBJ_U6_MAGIC_SHIELD, ACTOR_ARM, 5, 0 },
|
||||
{OBJ_U6_CLOTH_ARMOUR, ACTOR_BODY, 1, 0 },
|
||||
{OBJ_U6_LEATHER_ARMOR, ACTOR_BODY, 2, 0 },
|
||||
{OBJ_U6_RING_MAIL, ACTOR_BODY, 3, 0 },
|
||||
{OBJ_U6_SCALE_MAIL, ACTOR_BODY, 4, 0 },
|
||||
{OBJ_U6_CHAIN_MAIL, ACTOR_BODY, 5, 0 },
|
||||
{OBJ_U6_PLATE_MAIL, ACTOR_BODY, 7, 0 },
|
||||
{OBJ_U6_MAGIC_ARMOUR, ACTOR_BODY, 10, 0 },
|
||||
{OBJ_U6_SPIKED_COLLAR, ACTOR_NECK, 2, 0 },
|
||||
{OBJ_U6_GUILD_BELT, ACTOR_BODY, 0, 0 },
|
||||
{OBJ_U6_GARGOYLE_BELT, ACTOR_BODY, 0, 0 },
|
||||
{OBJ_U6_LEATHER_BOOTS, ACTOR_FOOT, 0, 0 },
|
||||
{OBJ_U6_SWAMP_BOOTS, ACTOR_FOOT, 0, 0 },
|
||||
|
||||
{OBJ_U6_SLING, ACTOR_ARM, 0, 6 },
|
||||
{OBJ_U6_CLUB, ACTOR_ARM, 0, 8 },
|
||||
{OBJ_U6_MAIN_GAUCHE, ACTOR_ARM, 1, 8 },
|
||||
{OBJ_U6_SPEAR, ACTOR_ARM, 0, 10 },
|
||||
{OBJ_U6_THROWING_AXE, ACTOR_ARM, 0, 10 },
|
||||
{OBJ_U6_DAGGER, ACTOR_ARM, 0, 6 },
|
||||
{OBJ_U6_MACE, ACTOR_ARM, 0, 15 },
|
||||
{OBJ_U6_MORNING_STAR, ACTOR_ARM, 0, 15 },
|
||||
{OBJ_U6_BOW, ACTOR_ARM_2, 0, 10 },
|
||||
{OBJ_U6_CROSSBOW, ACTOR_ARM_2, 0, 12 },
|
||||
{OBJ_U6_SWORD, ACTOR_ARM, 0, 15 },
|
||||
{OBJ_U6_TWO_HANDED_HAMMER, ACTOR_ARM_2, 0, 20 },
|
||||
{OBJ_U6_TWO_HANDED_AXE, ACTOR_ARM_2, 0, 20 },
|
||||
{OBJ_U6_TWO_HANDED_SWORD, ACTOR_ARM_2, 0, 20 },
|
||||
{OBJ_U6_HALBERD, ACTOR_ARM_2, 0, 30 },
|
||||
{OBJ_U6_GLASS_SWORD, ACTOR_ARM, 0, 255 },
|
||||
{OBJ_U6_BOOMERANG, ACTOR_ARM, 0, 8 },
|
||||
{OBJ_U6_TRIPLE_CROSSBOW, ACTOR_ARM_2, 0, 12 * 3 }, // ?? how to handle this
|
||||
|
||||
{OBJ_U6_MAGIC_BOW, ACTOR_ARM_2, 0, 20 },
|
||||
{OBJ_U6_SPELLBOOK, ACTOR_ARM, 0, 0 },
|
||||
|
||||
{OBJ_U6_ANKH_AMULET, ACTOR_NECK, 0, 0 },
|
||||
{OBJ_U6_SNAKE_AMULET, ACTOR_NECK, 0, 0 },
|
||||
{OBJ_U6_AMULET_OF_SUBMISSION, ACTOR_NECK, 0, 0 },
|
||||
|
||||
{OBJ_U6_STAFF, ACTOR_ARM, 0, 4 },
|
||||
{OBJ_U6_LIGHTNING_WAND, ACTOR_ARM, 0, 30 },
|
||||
{OBJ_U6_FIRE_WAND, ACTOR_ARM, 0, 20 },
|
||||
{OBJ_U6_STORM_CLOAK, ACTOR_BODY, 0, 0 },
|
||||
{OBJ_U6_RING, ACTOR_HAND, 0, 0 },
|
||||
{OBJ_U6_FLASK_OF_OIL, ACTOR_ARM, 0, 4 },
|
||||
|
||||
{OBJ_U6_TORCH, ACTOR_ARM, 0, 0 },
|
||||
|
||||
{OBJ_U6_SCYTHE, ACTOR_ARM, 0, 0 },
|
||||
{OBJ_U6_PITCHFORK, ACTOR_ARM, 0, 0 },
|
||||
{OBJ_U6_RAKE, ACTOR_ARM, 0, 0 },
|
||||
{OBJ_U6_PICK, ACTOR_ARM, 0, 0 },
|
||||
{OBJ_U6_SHOVEL, ACTOR_ARM, 0, 0 },
|
||||
{OBJ_U6_HOE, ACTOR_ARM, 0, 0 },
|
||||
|
||||
{OBJ_U6_ROLLING_PIN, ACTOR_ARM, 0, 2 },
|
||||
|
||||
{OBJ_U6_CLEAVER, ACTOR_ARM, 0, 4 },
|
||||
{OBJ_U6_KNIFE, ACTOR_ARM, 0, 4 },
|
||||
|
||||
{OBJ_U6_TUNIC, ACTOR_BODY, 0, 0 },
|
||||
{OBJ_U6_DRESS, ACTOR_BODY, 0, 0 },
|
||||
{OBJ_U6_PANTS, ACTOR_BODY, 0, 0 },
|
||||
|
||||
{OBJ_U6_LUTE, ACTOR_ARM, 0, 0 },
|
||||
|
||||
{OBJ_U6_PLIERS, ACTOR_ARM, 0, 0 },
|
||||
{OBJ_U6_HAMMER, ACTOR_ARM, 0, 0 },
|
||||
|
||||
{OBJ_U6_PROTECTION_RING, ACTOR_HAND, 0, 0 },
|
||||
{OBJ_U6_REGENERATION_RING, ACTOR_HAND, 0, 0 },
|
||||
{OBJ_U6_INVISIBILITY_RING, ACTOR_HAND, 0, 0 },
|
||||
|
||||
{OBJ_U6_ZU_YLEM, ACTOR_ARM, 0, 0},
|
||||
|
||||
{OBJ_U6_NOTHING, ACTOR_NOT_READIABLE, 0, 0 }
|
||||
}; // this last element terminates the array.
|
||||
|
||||
|
||||
// obj_n, defence, attack, hit_range, attack_type, missle_tile_num, thrown_obj_n, breaks_on_contact
|
||||
|
||||
const CombatType u6combat_hand = {OBJ_U6_NOTHING, {0}, {4}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false};
|
||||
const CombatType u6combat_ship_cannon = {OBJ_U6_SHIP, {0}, {30}, 4, ATTACK_TYPE_MISSLE, TILE_U6_LIGHTNING, OBJ_U6_NOTHING, false };
|
||||
|
||||
const CombatType u6combat_objects[] = {
|
||||
{OBJ_U6_LEATHER_HELM, {1}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_CHAIN_COIF, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_IRON_HELM, {3}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_SPIKED_HELM, {3}, {4}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_WINGED_HELM, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_BRASS_HELM, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_GARGOYLE_HELM, {3}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_MAGIC_HELM, {5}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_WOODEN_SHIELD, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_CURVED_HEATER, {3}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_WINGED_SHIELD, {3}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_KITE_SHIELD, {3}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_SPIKED_SHIELD, {2}, {4}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_BLACK_SHIELD, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_DOOR_SHIELD, {4}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_MAGIC_SHIELD, {5}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_CLOTH_ARMOUR, {1}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_LEATHER_ARMOR, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_RING_MAIL, {3}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_SCALE_MAIL, {4}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_CHAIN_MAIL, {5}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_PLATE_MAIL, {7}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_MAGIC_ARMOUR, {10}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_SPIKED_COLLAR, {2}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
|
||||
{OBJ_U6_SLING, {0}, {6}, 3, ATTACK_TYPE_MISSLE, TILE_U6_SLING_STONE, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_CLUB, {0}, {8}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_MAIN_GAUCHE, {1}, {8}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_SPEAR, {0}, {10}, 4, ATTACK_TYPE_THROWN, 0, OBJ_U6_SPEAR, false },
|
||||
{OBJ_U6_THROWING_AXE, {0}, {10}, 3, ATTACK_TYPE_THROWN, 0, OBJ_U6_THROWING_AXE, false },
|
||||
{OBJ_U6_DAGGER, {0}, {6}, 2, ATTACK_TYPE_THROWN, 0, OBJ_U6_DAGGER, false }, //melee weapon if at range 1
|
||||
{OBJ_U6_MACE, {0}, {15}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_MORNING_STAR, {0}, {15}, 2, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_BOW, {0}, {10}, 5, ATTACK_TYPE_MISSLE, TILE_U6_ARROW, OBJ_U6_ARROW, false },
|
||||
{OBJ_U6_CROSSBOW, {0}, {12}, 0, ATTACK_TYPE_MISSLE, TILE_U6_BOLT, OBJ_U6_BOLT, false },
|
||||
{OBJ_U6_SWORD, {0}, {15}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_TWO_HANDED_HAMMER, {0}, {20}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_TWO_HANDED_AXE, {0}, {20}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_TWO_HANDED_SWORD, {0}, {20}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_HALBERD, {0}, {30}, 2, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_GLASS_SWORD, {0}, {255}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, true },
|
||||
{OBJ_U6_BOOMERANG, {0}, {8}, 0, ATTACK_TYPE_THROWN, 0, OBJ_U6_BOOMERANG, false },
|
||||
{OBJ_U6_TRIPLE_CROSSBOW, {0}, {12 * 3}, 0, ATTACK_TYPE_MISSLE, TILE_U6_BOLT, OBJ_U6_NOTHING, false }, // ?? how to handle this
|
||||
|
||||
{OBJ_U6_MAGIC_BOW, {0}, {20}, 0, ATTACK_TYPE_MISSLE, TILE_U6_ARROW, OBJ_U6_NOTHING, false },
|
||||
|
||||
{OBJ_U6_STAFF, {0}, {4}, 0, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_LIGHTNING_WAND, {0}, {30}, 0, ATTACK_TYPE_MISSLE, TILE_U6_LIGHTNING, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_FIRE_WAND, {0}, {20}, 0, ATTACK_TYPE_MISSLE, TILE_U6_FIREBALL, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_FLASK_OF_OIL, {0}, {4}, 0, ATTACK_TYPE_THROWN, 0, OBJ_U6_FLASK_OF_OIL, false },
|
||||
|
||||
{OBJ_U6_ROLLING_PIN, {0}, {2}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
|
||||
{OBJ_U6_CLEAVER, {0}, {4}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
{OBJ_U6_KNIFE, {0}, {4}, 1, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
|
||||
{OBJ_U6_PROTECTION_RING, {5}, {0}, 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false },
|
||||
|
||||
{OBJ_U6_ZU_YLEM, {0}, {1}, 0, ATTACK_TYPE_HAND, 0, OBJ_U6_NOTHING, false },
|
||||
|
||||
{OBJ_U6_NOTHING, {0}, {0} , 0, ATTACK_TYPE_NONE, 0, OBJ_U6_NOTHING, false} // this last element terminates the array.
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
66
engines/ultima/nuvie/actors/u6_work_types.h
Normal file
66
engines/ultima/nuvie/actors/u6_work_types.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA_ULTIMA6_ACTORS_U6_WORK_TYPES_H
|
||||
#define ULTIMA_ULTIMA6_ACTORS_U6_WORK_TYPES_H
|
||||
|
||||
// Worktype codes
|
||||
#define WORKTYPE_U6_MOTIONLESS 0x0
|
||||
#define WORKTYPE_U6_IN_PARTY 0x1
|
||||
#define WORKTYPE_U6_PLAYER 0x2
|
||||
|
||||
#define WORKTYPE_U6_COMBAT_COMMAND 0x2
|
||||
#define WORKTYPE_U6_COMBAT_FRONT 0x3
|
||||
#define WORKTYPE_U6_COMBAT_REAR 0x4
|
||||
#define WORKTYPE_U6_COMBAT_FLANK 0x5
|
||||
#define WORKTYPE_U6_COMBAT_BERSERK 0x6
|
||||
#define WORKTYPE_U6_COMBAT_RETREAT 0x7
|
||||
#define WORKTYPE_U6_COMBAT_ASSAULT 0x8
|
||||
#define WORKTYPE_U6_COMBAT_WILD 0x8
|
||||
#define WORKTYPE_U6_COMBAT_SHY 0x9
|
||||
#define WORKTYPE_U6_COMBAT_LIKE 0xa
|
||||
#define WORKTYPE_U6_COMBAT_UNFRIENDLY 0xb
|
||||
|
||||
#define WORKTYPE_U6_ANIMAL_WANDER 0xc
|
||||
#define WORKTYPE_U6_TANGLE 0xd
|
||||
#define WORKTYPE_U6_IMMOBILE 0xe
|
||||
#define WORKTYPE_U6_GUARD_WALK_EAST_WEST 0xf
|
||||
#define WORKTYPE_U6_GUARD_WALK_NORTH_SOUTH 0x10
|
||||
|
||||
#define WORKTYPE_U6_LOOKOUT 0x11 // just a guess
|
||||
#define WORKTYPE_U6_WALK_TO_LOCATION 0x86
|
||||
|
||||
#define WORKTYPE_U6_FACE_NORTH 0x87
|
||||
#define WORKTYPE_U6_FACE_EAST 0x88
|
||||
#define WORKTYPE_U6_FACE_SOUTH 0x89
|
||||
#define WORKTYPE_U6_FACE_WEST 0x8a
|
||||
|
||||
#define WORKTYPE_U6_WALK_NORTH_SOUTH 0x8b
|
||||
#define WORKTYPE_U6_WALK_EAST_WEST 0x8c
|
||||
|
||||
#define WORKTYPE_U6_WANDER_AROUND 0x8f
|
||||
#define WORKTYPE_U6_WORK 0x90
|
||||
#define WORKTYPE_U6_SLEEP 0x91
|
||||
#define WORKTYPE_U6_PLAY_LUTE 0x95
|
||||
#define WORKTYPE_U6_BEG 0x96
|
||||
#define WORKTYPE_U6_ATTACK_PARTY 0x9b
|
||||
|
||||
#endif
|
||||
44
engines/ultima/nuvie/actors/wou_actor.cpp
Normal file
44
engines/ultima/nuvie/actors/wou_actor.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/actors/wou_actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
bool WOUActor::can_carry_object(Obj *obj) const {
|
||||
if (inventory_count_objects(INV_EXCLUDE_READIED_OBJECTS) >= 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Actor::can_carry_object(obj);
|
||||
}
|
||||
|
||||
bool WOUActor::can_carry_object(uint16 objN, uint32 qty) const {
|
||||
if (inventory_count_objects(INV_EXCLUDE_READIED_OBJECTS) >= 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Actor::can_carry_object(objN, qty);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
44
engines/ultima/nuvie/actors/wou_actor.h
Normal file
44
engines/ultima/nuvie/actors/wou_actor.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_ACTORS_WOU_ACTOR_H
|
||||
#define NUVIE_ACTORS_WOU_ACTOR_H
|
||||
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class WOUActor: public Actor {
|
||||
public:
|
||||
|
||||
WOUActor(Map *m, ObjManager *om, GameClock *c) : Actor(m, om, c) { }
|
||||
~WOUActor() override { }
|
||||
|
||||
bool can_carry_object(uint16 obj_n, uint32 qty = 0) const override;
|
||||
bool can_carry_object(Obj *obj) const override;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
85
engines/ultima/nuvie/conf/config_node.h
Normal file
85
engines/ultima/nuvie/conf/config_node.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CONF_CONFIG_NODE_H
|
||||
#define NUVIE_CONF_CONFIG_NODE_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class ConfigNode {
|
||||
friend class Configuration;
|
||||
ConfigNode(Configuration &config_, Std::string key_)
|
||||
: config(config_), key(key_) {
|
||||
}
|
||||
|
||||
public:
|
||||
~ConfigNode() { }
|
||||
|
||||
// fix "assignment operator could not be generated" warning
|
||||
const ConfigNode &operator = (const ConfigNode &other) {
|
||||
config = other.config;
|
||||
key = other.key;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Std::string get_string(const char *defaultvalue = "") {
|
||||
Std::string s;
|
||||
config.value(key, s, defaultvalue);
|
||||
return s;
|
||||
}
|
||||
int get_int(int defaultvalue = 0) {
|
||||
int i;
|
||||
config.value(key, i, defaultvalue);
|
||||
return i;
|
||||
}
|
||||
bool get_bool(bool defaultvalue = false) {
|
||||
bool b;
|
||||
config.value(key, b, defaultvalue);
|
||||
return b;
|
||||
}
|
||||
|
||||
void set(const Std::string &value) {
|
||||
config.set(key, value);
|
||||
}
|
||||
void set(const char *value) {
|
||||
config.set(key, value);
|
||||
}
|
||||
void set(int value) {
|
||||
config.set(key, value);
|
||||
}
|
||||
void set(bool value) {
|
||||
config.set(key, value);
|
||||
}
|
||||
|
||||
private:
|
||||
Configuration &config;
|
||||
Std::string key;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
441
engines/ultima/nuvie/conf/configuration.cpp
Normal file
441
engines/ultima/nuvie/conf/configuration.cpp
Normal file
@@ -0,0 +1,441 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/misc.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/conf/config_node.h"
|
||||
#include "ultima/shared/conf/xml_tree.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Configuration::Configuration() : _configChanged(false) {
|
||||
// Set up keys that will be stored locally, since we don't want them being
|
||||
// written out to the ScummVM configuration
|
||||
_localKeys["GameType"] = "";
|
||||
_localKeys["GameName"] = "";
|
||||
_localKeys["GameID"] = "";
|
||||
_localKeys["datadir"] = "data"; // This maps to ultima6/ in ultima.dat
|
||||
_localKeys["quit"] = "no";
|
||||
}
|
||||
|
||||
Configuration::~Configuration() {
|
||||
for (Shared::XMLTree *t : _trees) {
|
||||
delete(t);
|
||||
}
|
||||
|
||||
if (_configChanged)
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
bool Configuration::readConfigFile(const Std::string &fname, const Std::string &root,
|
||||
bool readonly) {
|
||||
_configFilename = fname;
|
||||
Shared::XMLTree *tree = new Shared::XMLTree();
|
||||
|
||||
if (!tree->readConfigFile(Common::Path(fname))) {
|
||||
delete tree;
|
||||
return false;
|
||||
}
|
||||
|
||||
_trees.push_back(tree);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Configuration::write() {
|
||||
for (Shared::XMLTree *t : _trees) {
|
||||
if (!t->isReadonly())
|
||||
t->write();
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::clear() {
|
||||
for (Shared::XMLTree *t : _trees) {
|
||||
delete(t);
|
||||
}
|
||||
_trees.clear();
|
||||
}
|
||||
|
||||
void Configuration::value(const Std::string &key, Std::string &ret,
|
||||
const char *defaultvalue) const {
|
||||
// Check for a .cfg file value in the trees
|
||||
for (int i = _trees.size() - 1; i >= 0; --i) {
|
||||
const Shared::XMLTree *tree = _trees[i];
|
||||
if (tree->hasNode(key)) {
|
||||
tree->value(key, ret, defaultvalue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(key.hasPrefix("config/"));
|
||||
Std::string k = key.substr(7);
|
||||
|
||||
// Check for local entry
|
||||
if (_localKeys.contains(k)) {
|
||||
ret = _localKeys[k];
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for ScummVM entry
|
||||
if (_settings.contains(k)) {
|
||||
ret = _settings[k];
|
||||
return;
|
||||
}
|
||||
|
||||
ret = defaultvalue;
|
||||
}
|
||||
|
||||
void Configuration::value(const Std::string &key, int &ret, int defaultvalue) const {
|
||||
// Check for a .cfg file value in the trees
|
||||
for (int i = _trees.size() - 1; i >= 0; --i) {
|
||||
const Shared::XMLTree *tree = _trees[i];
|
||||
if (tree->hasNode(key)) {
|
||||
tree->value(key, ret, defaultvalue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(key.hasPrefix("config/"));
|
||||
Std::string k = key.substr(7);
|
||||
|
||||
// Check for local entry
|
||||
if (_localKeys.contains(k)) {
|
||||
ret = atoi(_localKeys[k].c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for ScummVM key
|
||||
if (_settings.contains(k)) {
|
||||
ret = atoi(_settings[k].c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ret = defaultvalue;
|
||||
}
|
||||
|
||||
void Configuration::value(const Std::string &key, bool &ret, bool defaultvalue) const {
|
||||
// Check for a .cfg file value in the trees
|
||||
for (int i = _trees.size() - 1; i >= 0; --i) {
|
||||
const Shared::XMLTree *tree = _trees[i];
|
||||
if (tree->hasNode(key)) {
|
||||
tree->value(key, ret, defaultvalue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(key.hasPrefix("config/"));
|
||||
Std::string k = key.substr(7);
|
||||
|
||||
// Check for local entry
|
||||
if (_localKeys.contains(k)) {
|
||||
ret = _localKeys[k].hasPrefixIgnoreCase("y") ||
|
||||
_localKeys[k].hasPrefixIgnoreCase("t");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for ScummVM key
|
||||
if (_settings.contains(k)) {
|
||||
ret = _settings[k].hasPrefixIgnoreCase("y") ||
|
||||
_settings[k].hasPrefixIgnoreCase("t");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = defaultvalue;
|
||||
}
|
||||
|
||||
void Configuration::pathFromValue(const Std::string &key, const Std::string &file, Common::Path &full_path) const {
|
||||
Std::string tmp;
|
||||
value(key, tmp);
|
||||
|
||||
full_path = Common::Path(tmp).joinInPlace(file);
|
||||
}
|
||||
|
||||
bool Configuration::set(const Std::string &key, const Std::string &value) {
|
||||
// Currently a value is written to the last writable tree with
|
||||
// the correct root.
|
||||
|
||||
for (int i = _trees.size() - 1; i >= 0; --i) {
|
||||
Shared::XMLTree *tree = _trees[i];
|
||||
if (!(tree->isReadonly()) &&
|
||||
tree->checkRoot(key)) {
|
||||
tree->set(key, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
assert(key.hasPrefix("config/"));
|
||||
Std::string k = key.substr(7);
|
||||
|
||||
if (_localKeys.contains(k)) {
|
||||
_localKeys[k] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
_settings[k] = value;
|
||||
ConfMan.set(k, value);
|
||||
_configChanged = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Configuration::set(const Std::string &key, const char *value) {
|
||||
return set(key, Std::string(value));
|
||||
}
|
||||
|
||||
|
||||
bool Configuration::set(const Std::string &key, int value) {
|
||||
// Currently a value is written to the last writable tree with
|
||||
// the correct root.
|
||||
|
||||
for (int i = _trees.size() - 1; i >= 0; --i) {
|
||||
Shared::XMLTree *tree = _trees[i];
|
||||
if (!(tree->isReadonly()) &&
|
||||
tree->checkRoot(key)) {
|
||||
tree->set(key, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
assert(key.hasPrefix("config/"));
|
||||
Std::string k = key.substr(7);
|
||||
|
||||
if (_localKeys.contains(k)) {
|
||||
_localKeys[k] = Common::String::format("%d", value);
|
||||
return true;
|
||||
}
|
||||
|
||||
_settings[k] = Common::String::format("%d", value);
|
||||
ConfMan.setInt(k, value);
|
||||
_configChanged = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Configuration::set(const Std::string &key, bool value) {
|
||||
// Currently a value is written to the last writable tree with
|
||||
// the correct root.
|
||||
|
||||
for (int i = _trees.size() - 1; i >= 0; --i) {
|
||||
Shared::XMLTree *tree = _trees[i];
|
||||
if (!(tree->isReadonly()) &&
|
||||
tree->checkRoot(key)) {
|
||||
tree->set(key, value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
assert(key.hasPrefix("config/"));
|
||||
Std::string k = key.substr(7);
|
||||
Common::String strValue = value ? "yes" : "no";
|
||||
|
||||
if (_localKeys.contains(k)) {
|
||||
_localKeys[k] = strValue;
|
||||
} else {
|
||||
_settings[k] = strValue;
|
||||
ConfMan.setBool(k, value);
|
||||
_configChanged = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigNode *Configuration::getNode(const Std::string &key) {
|
||||
return new ConfigNode(*this, key);
|
||||
}
|
||||
|
||||
void Configuration::getSubkeys(KeyTypeList &ktl, const Std::string &basekey) {
|
||||
for (Shared::XMLTree *tree : _trees) {
|
||||
Shared::XMLTree::KeyTypeList l;
|
||||
tree->getSubkeys(l, basekey);
|
||||
|
||||
for (const auto &i : l) {
|
||||
bool found = false;
|
||||
for (auto &j : ktl) {
|
||||
if (j.first == i.first) {
|
||||
// already have this subkey, so just replace the value
|
||||
j.second = i.second;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// new subkey
|
||||
ktl.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::load(GameId gameId, bool isEnhanced) {
|
||||
// Load basic defaults for enhanced vs unehanced
|
||||
if (isEnhanced)
|
||||
setEnhancedDefaults(gameId);
|
||||
else
|
||||
setUnenhancedDefaults(gameId);
|
||||
|
||||
// nuvie.cfg in the game folder can supersede any ScummVM settings
|
||||
if (Common::File::exists("nuvie.cfg"))
|
||||
(void)readConfigFile("nuvie.cfg", "config");
|
||||
|
||||
// Load any further settings from scummvm.ini
|
||||
const Common::ConfigManager::Domain &domain = *ConfMan.getActiveDomain();
|
||||
Common::ConfigManager::Domain::const_iterator it;
|
||||
for (it = domain.begin(); it != domain.end(); ++it) {
|
||||
_settings[(*it)._key] = (*it)._value;
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::setCommonDefaults(GameId gameType) {
|
||||
_settings["video/non_square_pixels"] = "no";
|
||||
|
||||
#ifdef TODO
|
||||
_settings["audio/enabled"] = true;
|
||||
_settings["audio/enable_music"] = true;
|
||||
_settings["audio/enable_sfx"] = true;
|
||||
_settings["audio/music_volume"] = 100;
|
||||
_settings["audio/sfx_volume"] = 255;
|
||||
#endif
|
||||
_settings["audio/combat_changes_music"] = "yes";
|
||||
_settings["audio/vehicles_change_music"] = "yes";
|
||||
_settings["audio/conversations_stop_music"] = "no"; // original stopped music - maybe due to memory and disk swapping
|
||||
_settings["audio/stop_music_on_group_change"] = "yes";
|
||||
|
||||
_settings["input/enable_doubleclick"] = "yes";
|
||||
_settings["input/enabled_dragging"] = "yes";
|
||||
_settings["input/look_on_left_click"] = "yes";
|
||||
_settings["input/walk_with_left_button"] = "yes";
|
||||
_settings["input/direction_selects_target"] = "yes";
|
||||
|
||||
_settings["general/dither_mode"] = "none";
|
||||
_settings["general/enable_cursors"] = "yes";
|
||||
_settings["general/party_formation"] = "standard";
|
||||
|
||||
// Only show the startup console if in ScummVM debug mode
|
||||
_settings["general/show_console"] = gDebugLevel > 0 ? "yes" : "no";
|
||||
|
||||
_settings["cheats/enabled"] = "no";
|
||||
_settings["cheats/enable_hackmove"] = "no";
|
||||
_settings["cheats/min_brightness"] = "0";
|
||||
_settings["cheats/party_all_the_time"] = "no";
|
||||
|
||||
// game specific settings
|
||||
uint8 bg_color[] = { 218, 136, 216 }; // U6, MD, SE
|
||||
uint8 border_color[] = { 220, 133, 219 }; // U6, MD, SE
|
||||
|
||||
int i = 0;
|
||||
if (gameType == GAME_MARTIAN_DREAMS)
|
||||
i = 1;
|
||||
else if (gameType == GAME_SAVAGE_EMPIRE)
|
||||
i = 2;
|
||||
|
||||
#ifdef TODO
|
||||
_settings["language", "en";
|
||||
_settings["music", "native";
|
||||
_settings["sfx", "native";
|
||||
|
||||
if (i == 0) // U6
|
||||
_settings["enable_speech", "yes";
|
||||
#endif
|
||||
_settings["skip_intro"] = "no";
|
||||
_settings["show_eggs"] = "no";
|
||||
if (i == 0) { // U6
|
||||
_settings["show_stealing"] = "no";
|
||||
_settings["roof_mode"] = "no";
|
||||
}
|
||||
_settings["use_new_dolls"] = "no";
|
||||
_settings["cb_position"] = "default";
|
||||
_settings["show_orig_style_cb"] = "default";
|
||||
if (i == 0) // U6
|
||||
_settings["cb_text_color"] = "115";
|
||||
_settings["map_tile_lighting"] = i == 1 ? "no": "yes"; // MD has canals lit up so disable
|
||||
_settings["custom_actor_tiles"] = "default";
|
||||
_settings["converse_solid_bg"] = "no";
|
||||
_settings["converse_bg_color"] =
|
||||
Common::String::format("%d", bg_color[i]);
|
||||
_settings["converse_width"] = "default";
|
||||
_settings["converse_height"] = "default";
|
||||
if (i == 0) { // U6
|
||||
_settings["displayed_wind_dir"] = "from";
|
||||
_settings["free_balloon_movement"] = "no";
|
||||
}
|
||||
_settings["game_specific_keys"] = "(default)";
|
||||
_settings["newscroll/width"] = "30";
|
||||
_settings["newscroll/height"] = "19";
|
||||
_settings["newscroll/solid_bg"] = "no";
|
||||
_settings["newscroll/bg_color"] =
|
||||
Common::String::format("%d", bg_color[i]);
|
||||
_settings["newscroll/border_color"] =
|
||||
Common::String::format("%d", border_color[i]);
|
||||
|
||||
_settings["townsdir"] = "townsu6";
|
||||
|
||||
// _settings["newgamedata/name"] = "Avatar";
|
||||
// _settings["newgamedata/gender"] = "0";
|
||||
// _settings["newgamedata/portrait"] = "0";
|
||||
// _settings["newgamedata/str"] = "15";
|
||||
// _settings["newgamedata/dex"] = "15";
|
||||
// _settings["newgamedata/int"] = "15";
|
||||
}
|
||||
|
||||
void Configuration::setUnenhancedDefaults(GameId gameType) {
|
||||
setCommonDefaults(gameType);
|
||||
|
||||
_settings["video/screen_width"] = "320";
|
||||
_settings["video/screen_height"] = "200";
|
||||
_settings["video/game_width"] = "320";
|
||||
_settings["video/game_height"] = "200";
|
||||
_settings["video/game_style"] = "original";
|
||||
_settings["video/game_position"] = "center";
|
||||
|
||||
_settings["general/converse_gump"] = "default";
|
||||
_settings["general/lighting"] = "original";
|
||||
_settings["general/use_text_gumps"] = "no";
|
||||
|
||||
_settings["input/doubleclick_opens_containers"] = "no";
|
||||
_settings["input/party_view_targeting"] = "no";
|
||||
_settings["input/new_command_bar"] = "no";
|
||||
_settings["input/interface"] = "normal";
|
||||
}
|
||||
|
||||
void Configuration::setEnhancedDefaults(GameId gameType) {
|
||||
setCommonDefaults(gameType);
|
||||
|
||||
_settings["video/screen_width"] = "640";
|
||||
_settings["video/screen_height"] = "400";
|
||||
_settings["video/game_width"] = "640";
|
||||
_settings["video/game_height"] = "400";
|
||||
_settings["video/game_style"] = "original+_full_map";
|
||||
_settings["video/game_position"] = "center";
|
||||
|
||||
_settings["general/converse_gump"] = "yes";
|
||||
_settings["general/lighting"] = "smooth";
|
||||
_settings["general/use_text_gumps"] = "yes";
|
||||
|
||||
_settings["input/doubleclick_opens_containers"] = "yes";
|
||||
_settings["input/party_view_targeting"] = "yes";
|
||||
_settings["input/new_command_bar"] = "yes";
|
||||
_settings["input/interface"] = "fullscreen";
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
120
engines/ultima/nuvie/conf/configuration.h
Normal file
120
engines/ultima/nuvie/conf/configuration.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CONF_CONFIGURATION_H
|
||||
#define NUVIE_CONF_CONFIGURATION_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/detection.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Shared {
|
||||
class XMLTree;
|
||||
} // End of namespace Shared
|
||||
|
||||
namespace Nuvie {
|
||||
|
||||
class ConfigNode;
|
||||
|
||||
#define NUVIE_CONF_READONLY true
|
||||
#define NUVIE_CONF_READWRITE false
|
||||
|
||||
/**
|
||||
* Configuration class.
|
||||
*
|
||||
* Configuration values are stored in one of two ways -either as a standalone
|
||||
* nuvie.cfg file, or otherwise from the ScummVM domain for the added game.
|
||||
*
|
||||
* When the nuvie.cfg file is present, it's contents are stored as an XML tree
|
||||
* (or a forest, technically). All values are stored as strings, but access
|
||||
* functions for ints and bools are provided
|
||||
* You should only store values in leaf nodes. (This isn't enforced everywhere,
|
||||
* but contents of non-leaf nodes can disappear without warning)
|
||||
*
|
||||
* You can load multiple config files, which can be read-only.
|
||||
* Each config file contains a single tree.
|
||||
* Values in files loaded last override values in files loaded earlier.
|
||||
* Values are written to the last-loaded writable config file with the right root.
|
||||
* Because of this it's important to make sure the last-loaded config file with
|
||||
* a given root is writable. (The idea is that you can load a system-wide config
|
||||
* file first, and a user's config file after that.)
|
||||
*/
|
||||
class Configuration {
|
||||
private:
|
||||
Std::vector<Shared::XMLTree*> _trees;
|
||||
Common::HashMap<Common::String, Common::String, Common::IgnoreCase_Hash,
|
||||
Common::IgnoreCase_EqualTo> _localKeys;
|
||||
Common::HashMap<Common::String, Common::String, Common::IgnoreCase_Hash,
|
||||
Common::IgnoreCase_EqualTo> _settings;
|
||||
Std::string _configFilename;
|
||||
bool _configChanged;
|
||||
|
||||
// Sets default configurations common to both enhanced and unhenaced
|
||||
void setCommonDefaults(GameId gameType);
|
||||
|
||||
// sets up unenhanced version defaults
|
||||
void setUnenhancedDefaults(GameId gameType);
|
||||
|
||||
// sets up enhanced version defaults
|
||||
void setEnhancedDefaults(GameId gameType);
|
||||
public:
|
||||
Configuration();
|
||||
~Configuration();
|
||||
|
||||
// read config file. Multiple files may be read. Order is important.
|
||||
bool readConfigFile(const Std::string &fname, const Std::string &root, bool readonly = true);
|
||||
|
||||
// Loads up the configuration settings
|
||||
void load(GameId gameId, bool isEnhanced);
|
||||
|
||||
// write all (writable) config files
|
||||
void write();
|
||||
|
||||
// clear everything
|
||||
void clear();
|
||||
|
||||
// get value
|
||||
void value(const Std::string &key, Std::string &ret, const char *defaultvalue = "") const;
|
||||
void value(const Std::string &key, int &ret, int defaultvalue = 0) const;
|
||||
void value(const Std::string &key, bool &ret, bool defaultvalue = false) const;
|
||||
|
||||
void pathFromValue(const Std::string &key, const Std::string &file, Common::Path &full_path) const;
|
||||
|
||||
// set value
|
||||
bool set(const Std::string &key, const Std::string &value);
|
||||
bool set(const Std::string &key, const char *value);
|
||||
bool set(const Std::string &key, int value);
|
||||
bool set(const Std::string &key, bool value);
|
||||
|
||||
// get node ref. (delete it afterwards)
|
||||
ConfigNode *getNode(const Std::string &key);
|
||||
|
||||
typedef Common::Pair<Common::String, Common::String> KeyType;
|
||||
typedef Common::Array<KeyType> KeyTypeList;
|
||||
|
||||
void getSubkeys(KeyTypeList &ktl, const Std::string &basekey);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
37
engines/ultima/nuvie/conf/misc.cpp
Normal file
37
engines/ultima/nuvie/conf/misc.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/conf/misc.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Std::string readLine(Common::ReadStream *stream) {
|
||||
Std::string line;
|
||||
char c;
|
||||
while (!stream->eos() && (c = stream->readByte()) != '\n')
|
||||
line += c;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
38
engines/ultima/nuvie/conf/misc.h
Normal file
38
engines/ultima/nuvie/conf/misc.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#ifndef NUVIE_CONF_MISC_H
|
||||
#define NUVIE_CONF_MISC_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
extern bool string_i_compare(const Std::string &s1, const Std::string &s2);
|
||||
|
||||
extern Std::string readLine(Common::ReadStream *stream);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1365
engines/ultima/nuvie/core/anim_manager.cpp
Normal file
1365
engines/ultima/nuvie/core/anim_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
493
engines/ultima/nuvie/core/anim_manager.h
Normal file
493
engines/ultima/nuvie/core/anim_manager.h
Normal file
@@ -0,0 +1,493 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_ANIM_MANAGER_H
|
||||
#define NUVIE_CORE_ANIM_MANAGER_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/core/timed_event.h"
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
#include "ultima/nuvie/misc/map_entity.h"
|
||||
#include "ultima/nuvie/misc/u6_line_walker.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::list;
|
||||
using Std::string;
|
||||
using Std::vector;
|
||||
|
||||
class Actor;
|
||||
class CallBack;
|
||||
class AnimManager;
|
||||
class NuvieAnim;
|
||||
class Screen;
|
||||
class Font;
|
||||
|
||||
#define MESG_TIMED CB_TIMED
|
||||
|
||||
typedef Std::list<NuvieAnim *>::iterator AnimIterator;
|
||||
|
||||
/* Each viewable area has it's own AnimManager. (but I can only think of
|
||||
* animations in the MapWindow using this, so that could very well change)
|
||||
*/
|
||||
class AnimManager {
|
||||
MapWindow *map_window;
|
||||
Screen *viewsurf;
|
||||
Common::Rect viewport; // clip anims to location
|
||||
Std::list<NuvieAnim *> anim_list; // in paint order
|
||||
uint32 next_id;
|
||||
|
||||
uint8 tile_pitch;
|
||||
|
||||
sint16 mapwindow_x_offset;
|
||||
sint16 mapwindow_y_offset;
|
||||
|
||||
AnimIterator get_anim_iterator(uint32 anim_id);
|
||||
|
||||
public:
|
||||
AnimManager(sint16 x, sint16 y, Screen *screen = nullptr, Common::Rect *clipto = nullptr);
|
||||
~AnimManager() {
|
||||
destroy_all();
|
||||
}
|
||||
|
||||
void update();
|
||||
void display(bool top_anims = false);
|
||||
|
||||
Screen *get_surface() {
|
||||
return viewsurf;
|
||||
}
|
||||
void set_surface(Screen *screen) {
|
||||
viewsurf = screen;
|
||||
}
|
||||
void set_area(Common::Rect clipto) {
|
||||
viewport = clipto;
|
||||
}
|
||||
void set_tile_pitch(uint8 p) {
|
||||
tile_pitch = p;
|
||||
}
|
||||
uint8 get_tile_pitch() const {
|
||||
return tile_pitch;
|
||||
}
|
||||
|
||||
//new_anim(new ExplosiveAnim(speed));
|
||||
sint32 new_anim(NuvieAnim *new_anim);
|
||||
void destroy_all();
|
||||
bool destroy_anim(uint32 anim_id);
|
||||
bool destroy_anim(NuvieAnim *anim_pt);
|
||||
|
||||
NuvieAnim *get_anim(uint32 anim_id);
|
||||
|
||||
void drawTile(const Tile *tile, uint16 x, uint16 y);
|
||||
void drawTileAtWorldCoords(const Tile *tile, uint16 wx, uint16 wy, uint16 add_x = 0, uint16 add_y = 0);
|
||||
void drawText(Font *font, const char *text, uint16 x, uint16 y);
|
||||
};
|
||||
|
||||
|
||||
/* Contains methods to support management, continuous display, and movement of
|
||||
* animation across viewport.
|
||||
*/
|
||||
/* FIXME: The return of update() is not very useful. If an anim isn't
|
||||
* redrawn then it just disappears on next MapWindow::Display(). If you don't
|
||||
* want it to appear just delete it.*/
|
||||
class NuvieAnim: public CallBack {
|
||||
protected:
|
||||
friend class AnimManager;
|
||||
AnimManager *anim_manager; // set by anim_manager when adding to list
|
||||
|
||||
uint32 id_n; // unique
|
||||
|
||||
sint32 vel_x, vel_y; // movement across viewport (pixels/second; min=10)
|
||||
uint32 px, py; // location on surface
|
||||
uint32 last_move_time; // last time when update_position() moved (ticks)
|
||||
|
||||
bool safe_to_delete; // can animmgr delete me?
|
||||
bool updated; // call display
|
||||
bool running;
|
||||
bool paused;
|
||||
bool top_anim; //animate on top of mapwindow.
|
||||
|
||||
// return false if animation doesn't need redraw
|
||||
virtual bool update() {
|
||||
return true;
|
||||
}
|
||||
virtual void display() = 0;
|
||||
|
||||
void update_position();
|
||||
|
||||
public:
|
||||
NuvieAnim();
|
||||
~NuvieAnim() override;
|
||||
|
||||
void pause() {
|
||||
paused = true;
|
||||
}
|
||||
void unpause() {
|
||||
paused = false;
|
||||
}
|
||||
bool is_paused() const {
|
||||
return paused;
|
||||
}
|
||||
|
||||
virtual MapCoord get_location() {
|
||||
return MapCoord(px, py, 0);
|
||||
}
|
||||
uint32 get_id() const {
|
||||
return id_n;
|
||||
}
|
||||
|
||||
void set_safe_to_delete(bool val) {
|
||||
safe_to_delete = val;
|
||||
}
|
||||
void set_velocity(sint32 sx, sint32 sy) {
|
||||
vel_x = sx;
|
||||
vel_y = sy;
|
||||
}
|
||||
void set_velocity_for_speed(sint16 xdir, sint16 ydir, uint32 spd);
|
||||
|
||||
virtual void stop() {
|
||||
updated = running = false;
|
||||
}
|
||||
virtual void start() { }
|
||||
uint16 message(uint16 msg, void *msg_data = nullptr, void *my_data = nullptr) {
|
||||
if (callback_target) return (CallBack::message(msg, msg_data, my_data));
|
||||
else return 0;
|
||||
}
|
||||
|
||||
virtual void move(uint32 x, uint32 y, uint32 add_x = 0, uint32 add_y = 0) {
|
||||
px = x;
|
||||
py = y;
|
||||
}
|
||||
virtual void shift(sint32 sx, sint32 sy) {
|
||||
px += sx;
|
||||
py += sy;
|
||||
}
|
||||
|
||||
// void set_flags();
|
||||
// ANIM_ONTOP
|
||||
// ANIM_ONBOTTOM
|
||||
};
|
||||
|
||||
|
||||
/* Tile placement & data for TileAnim
|
||||
*/
|
||||
typedef struct {
|
||||
sint16 pos_x, pos_y; // map position relative to Anim tx,ty
|
||||
uint16 px, py; // pixel offset from pos_x,pos_y
|
||||
Tile *tile;
|
||||
} PositionedTile;
|
||||
|
||||
|
||||
/* Animation using game tiles
|
||||
*/
|
||||
class TileAnim : public NuvieAnim {
|
||||
protected:
|
||||
MapWindow *_mapWindow;
|
||||
uint32 _tx, _ty, // location on surface: in increments of "tile_pitch"
|
||||
_px, _py; // location on surface: pixel offset from tx,ty
|
||||
|
||||
vector<PositionedTile *> _tiles;
|
||||
|
||||
void display() override;
|
||||
|
||||
public:
|
||||
TileAnim();
|
||||
~TileAnim() override;
|
||||
|
||||
MapCoord get_location() override {
|
||||
return MapCoord(_tx, _ty, 0);
|
||||
}
|
||||
void get_offset(uint32 &x_add, uint32 &y_add) const {
|
||||
x_add = _px;
|
||||
y_add = _py;
|
||||
}
|
||||
sint32 get_tile_id(PositionedTile *find_tile);
|
||||
|
||||
void move(uint32 x, uint32 y, uint32 add_x = 0, uint32 add_y = 0) override {
|
||||
_tx = x;
|
||||
_ty = y;
|
||||
_px = add_x;
|
||||
_py = add_y;
|
||||
}
|
||||
void shift(sint32 sx, sint32 sy) override;
|
||||
void shift_tile(uint32 ptile_num, sint32 sx, sint32 sy);
|
||||
void move_tile(PositionedTile *ptile, uint32 x, uint32 y);
|
||||
|
||||
|
||||
PositionedTile *add_tile(Tile *tile, sint16 x, sint16 y, uint16 add_x = 0, uint16 add_y = 0);
|
||||
void remove_tile(uint32 i = 0);
|
||||
void remove_tile(PositionedTile *p_tile);
|
||||
};
|
||||
|
||||
|
||||
/* TileAnim using a timed event.
|
||||
*/
|
||||
class TimedAnim: public TileAnim {
|
||||
protected:
|
||||
TimedCallback *timer;
|
||||
public:
|
||||
TimedAnim() {
|
||||
timer = nullptr;
|
||||
}
|
||||
~TimedAnim() override {
|
||||
stop_timer();
|
||||
}
|
||||
void start_timer(uint32 delay) {
|
||||
if (!timer) timer = new TimedCallback(this, nullptr, delay, true);
|
||||
}
|
||||
void stop_timer() {
|
||||
if (timer) {
|
||||
timer->clear_target();
|
||||
timer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
stop_timer();
|
||||
NuvieAnim::stop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// OR these together to tell a TossAnim what to intercept
|
||||
#define TOSS_TO_BLOCKING 0x01
|
||||
#define TOSS_TO_ACTOR 0x02
|
||||
#define TOSS_TO_OBJECT 0x04
|
||||
|
||||
/* A TileAnim that can intercept objects in the world. Start selected tile at
|
||||
* source, and move across viewport to target. The tile is rotated by the
|
||||
* degrees argument passed to the constructor.
|
||||
*/
|
||||
class TossAnim : public TileAnim {
|
||||
protected:
|
||||
ActorManager *actor_manager;
|
||||
ObjManager *obj_manager;
|
||||
Map *map;
|
||||
|
||||
MapCoord *src, *target;
|
||||
uint32 start_px, start_py, target_px, target_py;
|
||||
uint8 mapwindow_level; // level of map being viewed
|
||||
uint16 speed; // movement speed in pixels per second (X and Y speed can't be set independently)
|
||||
|
||||
Tile *toss_tile;
|
||||
uint8 blocking; // stop_flags
|
||||
uint8 tile_center; // tile_pitch / 2
|
||||
float tanS; // Ydiff/Xdiff, between src and target (for movement velocity)
|
||||
sint16 old_relpos; // when moving diagonally, last relative position on minor axis
|
||||
float x_left, y_left; // when unable to move in a call, fractional movement values are collected here
|
||||
uint16 x_dist, y_dist; // distances from start->target on X-axis & Y-axis
|
||||
|
||||
bool update() override;
|
||||
MapCoord get_location() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
public:
|
||||
TossAnim(const Tile *tile, const MapCoord &start, const MapCoord &stop, uint16 pixels_per_sec, uint8 stop_flags = 0);
|
||||
TossAnim(Obj *obj, uint16 degrees, const MapCoord &start, const MapCoord &stop, uint16 pixels_per_sec, uint8 stop_flags = 0);
|
||||
~TossAnim() override;
|
||||
|
||||
void init(const Tile *tile, uint16 degrees, const MapCoord &start, const MapCoord &stop, uint16 pixels_per_sec, uint8 stop_flags);
|
||||
void start() override;
|
||||
void stop() override;
|
||||
uint32 update_position(uint32 max_move = 0);
|
||||
inline void accumulate_moves(float moves, sint32 &x_move, sint32 &y_move, sint8 xdir, sint8 ydir);
|
||||
|
||||
// Virtual functions are called when the tile hits something.
|
||||
virtual void hit_target();
|
||||
virtual void hit_object(Obj *obj);
|
||||
virtual void hit_actor(Actor *actor);
|
||||
virtual void hit_blocking(const MapCoord &obj_loc);
|
||||
};
|
||||
|
||||
// This is for off-center tiles. The tile will be moved down by the
|
||||
// shift amount if moving right, and up if moving left. (and rotated)
|
||||
struct tossanim_tile_shifts_s {
|
||||
uint16 tile_num;
|
||||
sint8 shift; // plus or minus vertical position
|
||||
};
|
||||
extern const struct tossanim_tile_shifts_s tossanim_tile_shifts[];
|
||||
|
||||
/* a line of fire */
|
||||
typedef struct {
|
||||
PositionedTile *tile; // last associated sprite
|
||||
MapCoord direction; // where the explosion sprites are going
|
||||
uint32 travelled; // distance this fire line has travelled
|
||||
} ExplosiveAnimSegment;
|
||||
|
||||
/* SuperBomberman! Toss fireballs in multiple directions from source out.
|
||||
*/
|
||||
class ExplosiveAnim : public TimedAnim {
|
||||
MapCoord center;
|
||||
uint32 radius; // num. of spaces from center
|
||||
vector<ExplosiveAnimSegment> flame; // lines of fire from the center
|
||||
uint16 exploding_tile_num; // fireball effect tile_num
|
||||
vector<MapEntity> hit_items; // things the explosion has hit
|
||||
|
||||
public:
|
||||
ExplosiveAnim(const MapCoord &start, uint32 size);
|
||||
~ExplosiveAnim() override;
|
||||
void start() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
bool update() override;
|
||||
bool already_hit(const MapEntity &ent);
|
||||
void hit_object(Obj *obj);
|
||||
void hit_actor(Actor *actor);
|
||||
void get_shifted_location(uint16 &x, uint16 &y, uint16 &px, uint16 &py,
|
||||
uint32 sx, uint32 sy);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
MapCoord target;
|
||||
U6LineWalker *lineWalker;
|
||||
PositionedTile *p_tile;
|
||||
uint8 update_idx;
|
||||
uint16 rotation;
|
||||
uint16 rotation_amount;
|
||||
float current_deg;
|
||||
bool isRunning;
|
||||
} ProjectileLine;
|
||||
|
||||
class ProjectileAnim : public TileAnim {
|
||||
MapCoord src;
|
||||
vector<ProjectileLine> line;
|
||||
uint16 tile_num; // fireball effect tile_num
|
||||
uint8 src_tile_y_offset; //amount to offset src_tile when rotating. Used by arrows and bolts
|
||||
vector<MapEntity> hit_items; // things the projectile has hit
|
||||
uint16 stopped_count;
|
||||
uint8 speed; //number of pixels to move in a single update.
|
||||
|
||||
bool leaveTrailFlag;
|
||||
public:
|
||||
ProjectileAnim(uint16 tileNum, MapCoord *start, vector<MapCoord> target, uint8 animSpeed, bool leaveTrailFlag = false, uint16 initialTileRotation = 0, uint16 rotationAmount = 0, uint8 src_y_offset = 0);
|
||||
~ProjectileAnim() override;
|
||||
void start() override;
|
||||
|
||||
bool update() override;
|
||||
|
||||
protected:
|
||||
void hit_entity(MapEntity entity);
|
||||
bool already_hit(const MapEntity &ent);
|
||||
|
||||
};
|
||||
|
||||
class WingAnim : public TileAnim {
|
||||
MapCoord target;
|
||||
sint32 x, y, finish_x;
|
||||
sint16 x_inc;
|
||||
Tile *wing_top[2];
|
||||
Tile *wing_bottom[2];
|
||||
|
||||
PositionedTile *p_tile_top;
|
||||
PositionedTile *p_tile_bottom;
|
||||
|
||||
public:
|
||||
WingAnim(const MapCoord &target);
|
||||
~WingAnim() override;
|
||||
void start() override;
|
||||
bool update() override;
|
||||
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16 x, y;
|
||||
PositionedTile *p_tile;
|
||||
uint8 length_left;
|
||||
} Hailstone;
|
||||
|
||||
#define HAILSTORM_ANIM_MAX_STONES 6
|
||||
class HailstormAnim : public TileAnim {
|
||||
MapCoord target;
|
||||
|
||||
Tile *hailstone_tile;
|
||||
|
||||
Hailstone hailstones[HAILSTORM_ANIM_MAX_STONES];
|
||||
uint8 num_hailstones_left;
|
||||
uint8 num_active;
|
||||
|
||||
public:
|
||||
HailstormAnim(const MapCoord &t);
|
||||
~HailstormAnim() override;
|
||||
void start() override;
|
||||
bool update() override;
|
||||
|
||||
protected:
|
||||
sint8 find_free_hailstone();
|
||||
|
||||
};
|
||||
|
||||
/* Display hit effect over an actor or location for a certain duration.
|
||||
*/
|
||||
class HitAnim : public TimedAnim {
|
||||
Actor *hit_actor;
|
||||
|
||||
bool update() override;
|
||||
|
||||
public:
|
||||
HitAnim(const MapCoord &loc);
|
||||
HitAnim(Actor *actor);
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *msg_data) override;
|
||||
void start() override {
|
||||
start_timer(300);
|
||||
}
|
||||
};
|
||||
|
||||
class TextAnim : public TimedAnim {
|
||||
Std::string text;
|
||||
Font *font;
|
||||
uint32 duration;
|
||||
|
||||
public:
|
||||
TextAnim(Std::string text, MapCoord loc, uint32 dur);
|
||||
~TextAnim() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *msg_data) override;
|
||||
void start() override {
|
||||
start_timer(duration);
|
||||
}
|
||||
|
||||
void display() override;
|
||||
};
|
||||
|
||||
class TileFadeAnim : public TileAnim {
|
||||
uint16 pixel_count;
|
||||
Tile *anim_tile;
|
||||
Tile *to_tile;
|
||||
bool should_delete_to_tile;
|
||||
uint16 pixels_per_update; //the number of pixels to change in each update.
|
||||
unsigned char mask[256];
|
||||
|
||||
public:
|
||||
TileFadeAnim();
|
||||
TileFadeAnim(const MapCoord &loc, Tile *from, Tile *to, uint16 speed);
|
||||
TileFadeAnim(const MapCoord &loc, Tile *from, uint8 color_from, uint8 color_to, bool reverse, uint16 speed);
|
||||
~TileFadeAnim() override;
|
||||
|
||||
bool update() override;
|
||||
protected:
|
||||
void init(uint16 speed);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
58
engines/ultima/nuvie/core/book.cpp
Normal file
58
engines/ultima/nuvie/core/book.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/core/book.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Book::Book(const Configuration *cfg) : config(cfg), books(new U6Lib_n) {
|
||||
}
|
||||
|
||||
Book::~Book() {
|
||||
delete books;
|
||||
}
|
||||
|
||||
bool Book::init() {
|
||||
Common::Path filename;
|
||||
|
||||
config_get_path(config, "book.dat", filename);
|
||||
|
||||
if (books->open(filename, 2) == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char *Book::get_book_data(uint16 num) {
|
||||
if (num >= books->get_num_items())
|
||||
return nullptr;
|
||||
|
||||
return reinterpret_cast<char *>(books->get_item(num));
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
48
engines/ultima/nuvie/core/book.h
Normal file
48
engines/ultima/nuvie/core/book.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_BOOK_H
|
||||
#define NUVIE_CORE_BOOK_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class U6Lib_n;
|
||||
|
||||
class Book {
|
||||
const Configuration *config;
|
||||
|
||||
U6Lib_n *books;
|
||||
|
||||
public:
|
||||
|
||||
Book(const Configuration *cfg);
|
||||
~Book();
|
||||
|
||||
bool init();
|
||||
char *get_book_data(uint16 num);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
752
engines/ultima/nuvie/core/converse.cpp
Normal file
752
engines/ultima/nuvie/core/converse.cpp
Normal file
@@ -0,0 +1,752 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/core/player.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/views/view_manager.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/sound/sound_manager.h"
|
||||
#include "ultima/nuvie/core/events.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/core/converse_interpret.h"
|
||||
#include "ultima/nuvie/core/converse_speech.h"
|
||||
#include "ultima/nuvie/gui/widgets/converse_gump.h"
|
||||
#include "ultima/nuvie/core/converse.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
#include "ultima/nuvie/gui/widgets/background.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
//#define CONVERSE_DEBUG
|
||||
|
||||
|
||||
Converse::Converse() : config(nullptr), actors(nullptr), objects(nullptr),
|
||||
player(nullptr), views(nullptr), last_view(nullptr), scroll(nullptr),
|
||||
conv_i(nullptr), script(nullptr), npc(nullptr), npc_num(0), script_num(0),
|
||||
src(nullptr), src_num(0), allowed_input(nullptr), active(false),
|
||||
variables(nullptr), party_all_the_time(false), speech(nullptr),
|
||||
using_fmtowns(false), need_input(false), conversations_stop_music(false),
|
||||
gametype(NUVIE_GAME_NONE), _clock(nullptr) {
|
||||
ARRAYCLEAR(aname);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize global classes from the game.
|
||||
*/
|
||||
void
|
||||
Converse::init(const Configuration *cfg, nuvie_game_t t, MsgScroll *s, ActorManager *a,
|
||||
GameClock *c, Player *p, ViewManager *v, ObjManager *o) {
|
||||
Std::string townsdir;
|
||||
|
||||
config = cfg;
|
||||
scroll = s;
|
||||
actors = a;
|
||||
_clock = c;
|
||||
player = p;
|
||||
views = v;
|
||||
objects = o;
|
||||
gametype = t;
|
||||
|
||||
cfg->value("config/cheats/party_all_the_time", party_all_the_time);
|
||||
cfg->value("config/audio/conversations_stop_music", conversations_stop_music, false);
|
||||
|
||||
cfg->value("config/townsdir", townsdir, "");
|
||||
if (townsdir != "" && directory_exists(townsdir.c_str()))
|
||||
using_fmtowns = true;
|
||||
|
||||
speech = new ConverseSpeech();
|
||||
speech->init(config);
|
||||
}
|
||||
|
||||
|
||||
Converse::~Converse() {
|
||||
if (running()) {
|
||||
reset();
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "End conversation\n");
|
||||
}
|
||||
unload_conv();
|
||||
|
||||
delete speech;
|
||||
}
|
||||
|
||||
|
||||
/* Free up allocated memory, reset values for new conversation. (call only when
|
||||
* ending a conversation or quitting)
|
||||
*/
|
||||
void Converse::reset() {
|
||||
delete conv_i;
|
||||
conv_i = nullptr;
|
||||
set_input(""); // delete
|
||||
set_output(""); // clear output
|
||||
_name = ""; // clear name
|
||||
|
||||
if (script) {
|
||||
delete script;
|
||||
script = nullptr;
|
||||
}
|
||||
|
||||
if (allowed_input) {
|
||||
free(allowed_input);
|
||||
allowed_input = nullptr;
|
||||
}
|
||||
|
||||
player->set_quest_flag((uint8)get_var(U6TALK_VAR_QUESTF));
|
||||
player->set_gargish_flag((uint8)get_var(U6TALK_VAR_GARGF));
|
||||
|
||||
delete_variables();
|
||||
}
|
||||
|
||||
|
||||
/* Load `convfilename' as src.
|
||||
*/
|
||||
void Converse::load_conv(const Std::string &convfilename) {
|
||||
Common::Path conv_lib_str;
|
||||
if (gametype == NUVIE_GAME_U6 && using_fmtowns) {
|
||||
config->pathFromValue("config/townsdir", convfilename, conv_lib_str);
|
||||
} else {
|
||||
config_get_path(config, convfilename, conv_lib_str);
|
||||
}
|
||||
|
||||
unload_conv();
|
||||
src_num = 0;
|
||||
if (gametype == NUVIE_GAME_U6) {
|
||||
src = new U6Lib_n;
|
||||
src->open(conv_lib_str, 4);
|
||||
src_num = (convfilename == "converse.a") ? 1 : (convfilename == "converse.b") ? 2 : 0;
|
||||
} else { // MD or SE gametype
|
||||
src = new U6Lib_n;
|
||||
src->open(conv_lib_str, 4, gametype);
|
||||
src_num = 1;
|
||||
}
|
||||
|
||||
#ifdef CONVERSE_DEBUG
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Converse: load \"%s\"\n", convfilename.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32 Converse::get_script_num(uint8 a) {
|
||||
if (gametype == NUVIE_GAME_U6) {
|
||||
if (a > 200) { // (quick fix for U6: anything over 200 is a temporary npc)
|
||||
Actor *npcP = actors->get_actor(a);
|
||||
if (npcP->get_obj_n() == 373) // OBJ_U6_WISP
|
||||
a = 201;
|
||||
else if (npcP->get_obj_n() == 382) // OBJ_U6_GUARD
|
||||
a = 202;
|
||||
}
|
||||
//else if(a == 188) // U6: temp. fix for shrines
|
||||
// a = 191; // ??? -> Exodus
|
||||
//else if(a >= 191 && a <= 197) // shrines except spirituality & humility
|
||||
// a += 2;
|
||||
//else if(a == 198)
|
||||
// a = 192; // Spirituality -> Honesty
|
||||
//else if(a == 199)
|
||||
// a = 200; // Humility -> Singularity
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Check that loaded converse library (if any) has script for npc `a'. Load
|
||||
* another file if it doesn't.
|
||||
* Returns the real item number in the source.
|
||||
*/
|
||||
uint32 Converse::load_conv(uint8 a) {
|
||||
if (gametype == NUVIE_GAME_U6) {
|
||||
if (a <= 98) {
|
||||
if (src_num != 1)
|
||||
load_conv("converse.a");
|
||||
} else { // a >= 99
|
||||
if (src_num != 2)
|
||||
load_conv("converse.b");
|
||||
}
|
||||
} else {
|
||||
if (src_num != 1)
|
||||
load_conv("talk.lzc");
|
||||
}
|
||||
// we want to return the real item number in the converse file.
|
||||
if (gametype == NUVIE_GAME_U6 && a > 98) {
|
||||
a -= 99;
|
||||
} else if (gametype == NUVIE_GAME_SE) {
|
||||
a -= 2;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/* Returns name of loaded source file, identified by `src_num'.
|
||||
*/
|
||||
const char *Converse::src_name() {
|
||||
if (src_num == 0)
|
||||
return "";
|
||||
if (gametype == NUVIE_GAME_U6)
|
||||
return ((src_num == 1) ? "converse.a" : "converse.b");
|
||||
if (gametype == NUVIE_GAME_MD)
|
||||
return "talk.lzc";
|
||||
if (gametype == NUVIE_GAME_SE)
|
||||
return "talk.lzc";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/* Get an NPC conversation from the source file.
|
||||
* Returns new ConvScript object.
|
||||
*/
|
||||
ConvScript *Converse::load_script(uint32 n) {
|
||||
ConvScript *loaded = new ConvScript(src, n);
|
||||
if (!loaded->loaded()) {
|
||||
delete loaded;
|
||||
loaded = nullptr;
|
||||
} else
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "Read %s npc script (%s:%d)\n",
|
||||
loaded->compressed ? "encoded" : "unencoded", src_name(), (unsigned int)n);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize Converse variable list, and set globals from game.
|
||||
*/
|
||||
void Converse::init_variables() {
|
||||
if (variables)
|
||||
delete_variables();
|
||||
|
||||
variables = new converse_variables_s[U6TALK_VAR__LAST_ + 1];
|
||||
for (uint32 v = 0; v <= U6TALK_VAR__LAST_; v++) {
|
||||
variables[v].cv = 0;
|
||||
variables[v].sv = nullptr;
|
||||
}
|
||||
set_var(U6TALK_VAR_SEX, player->get_gender());
|
||||
set_var(U6TALK_VAR_KARMA, player->get_karma());
|
||||
set_var(U6TALK_VAR_GARGF, player->get_gargish_flag());
|
||||
set_var(U6TALK_VAR_PARTYLIVE, player->get_party()->get_party_size() - 1);
|
||||
// FIXME: count dead party members in PARTYALL, not in PARTYLIVE
|
||||
set_var(U6TALK_VAR_PARTYALL, get_var(U6TALK_VAR_PARTYLIVE));
|
||||
set_var(U6TALK_VAR_HP, player->get_actor()->get_hp());
|
||||
set_svar(U6TALK_VAR_NPC_NAME, npc_name(npc_num));
|
||||
set_svar(U6TALK_VAR_PLAYER_NAME, player->get_name());
|
||||
set_var(U6TALK_VAR_QUESTF, player->get_quest_flag());
|
||||
set_var(U6TALK_VAR_WORKTYPE, npc->get_worktype());
|
||||
}
|
||||
|
||||
|
||||
/* Free memory used by Converse variable list.
|
||||
*/
|
||||
void Converse::delete_variables() {
|
||||
for (uint32 v = 0; v <= U6TALK_VAR__LAST_; v++)
|
||||
if (variables[v].sv)
|
||||
free(variables[v].sv);
|
||||
delete [] variables;
|
||||
variables = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/* Create new script interpreter for the current game.
|
||||
* Returns pointer to object which is derived from ConverseInterpret.
|
||||
*/
|
||||
ConverseInterpret *Converse::new_interpreter() {
|
||||
ConverseInterpret *ci = nullptr;
|
||||
switch (gametype) {
|
||||
case NUVIE_GAME_U6:
|
||||
ci = (ConverseInterpret *)new U6ConverseInterpret(this);
|
||||
break;
|
||||
case NUVIE_GAME_MD:
|
||||
ci = (ConverseInterpret *)new MDTalkInterpret(this);
|
||||
break;
|
||||
case NUVIE_GAME_SE:
|
||||
ci = (ConverseInterpret *)new SETalkInterpret(this);
|
||||
break;
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
|
||||
/* Returns false if a conversation cannot be started with the NPC. This
|
||||
* represents an internal error, and doesn't have anything to do with the NPC
|
||||
* not wanting/being able to talk to the Avatar.
|
||||
*/
|
||||
bool Converse::start(uint8 n) {
|
||||
uint32 real_script_num = 0; // The script number in the converse file.
|
||||
|
||||
// load, but make sure previous script is unloaded first
|
||||
if (running())
|
||||
stop();
|
||||
if (!(npc = actors->get_actor(n)))
|
||||
return false;
|
||||
// get script num for npc number (and open file)
|
||||
script_num = get_script_num(n);
|
||||
real_script_num = load_conv(script_num);
|
||||
if (!src)
|
||||
return false;
|
||||
|
||||
script = load_script(real_script_num);
|
||||
|
||||
// begin
|
||||
if (script) {
|
||||
active = true;
|
||||
last_view = views->get_current_view();
|
||||
if (!(conv_i = new_interpreter())) {
|
||||
DEBUG(0, LEVEL_CRITICAL, "Can't talk: Unimplemented or unknown game type\n");
|
||||
return false;
|
||||
}
|
||||
views->close_all_gumps();
|
||||
// set current NPC and start conversation
|
||||
npc_num = n;
|
||||
init_variables();
|
||||
scroll->set_talking(true, actors->get_actor(npc_num));
|
||||
Game::get_game()->get_map_window()->set_walking(false);
|
||||
Game::get_game()->get_map_window()->set_looking(false);
|
||||
if (conversations_stop_music)
|
||||
Game::get_game()->get_sound_manager()->musicStop();
|
||||
//Game::get_game()->get_event()->set_mode(WAIT_MODE); // ignore player actions
|
||||
Game::get_game()->pause_user();
|
||||
Game::get_game()->get_gui()->unblock();
|
||||
scroll->set_autobreak(true);
|
||||
/* moved into ConverseGump::set_talking()
|
||||
if(Game::get_game()->is_new_style())
|
||||
{
|
||||
scroll->Show();
|
||||
scroll->set_input_mode(false);
|
||||
scroll->clear_scroll();
|
||||
((ConverseGump *)scroll)->set_found_break_char(true);
|
||||
//scroll->grab_focus();
|
||||
}
|
||||
*/
|
||||
show_portrait(npc_num);
|
||||
unwait();
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "Begin conversation with \"%s\" (npc %d)\n", npc_name(n), n);
|
||||
return true;
|
||||
}
|
||||
DEBUG(0, LEVEL_ERROR, "Failed to load npc %d from %s:%d\n",
|
||||
n, src_name(), script_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Stop execution of the current script.
|
||||
*/
|
||||
void Converse::stop() {
|
||||
scroll->set_talking(false);
|
||||
MsgScroll *system_scroll = Game::get_game()->get_scroll();
|
||||
|
||||
if ((Game::get_game()->using_new_converse_gump() || scroll != system_scroll) && !scroll->is_converse_finished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
reset(); // free memory
|
||||
|
||||
if (Game::get_game()->using_new_converse_gump()) {
|
||||
scroll->Hide();
|
||||
if (!Game::get_game()->is_new_style()) {
|
||||
Game::get_game()->get_event()->endAction(true);
|
||||
GUI::get_gui()->force_full_redraw(); // need to remove converse background
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
system_scroll->set_autobreak(false);
|
||||
system_scroll->display_string("\n");
|
||||
system_scroll->display_prompt();
|
||||
|
||||
if (scroll != system_scroll) { //if using an alternate scroll eg wou fullmap scroll.
|
||||
scroll->Hide();
|
||||
}
|
||||
}
|
||||
if (!Game::get_game()->is_new_style()) {
|
||||
if (last_view->set_party_member(last_view->get_party_member_num()) == false) // set party member left party
|
||||
last_view->prev_party_member(); // seems only needed with new converse gump but will leave here just in case
|
||||
views->set_current_view(last_view);
|
||||
}
|
||||
|
||||
Game::get_game()->unpause_user();
|
||||
if (conversations_stop_music) {
|
||||
SoundManager *sm = Game::get_game()->get_sound_manager();
|
||||
if (sm->is_audio_enabled() && sm->is_music_enabled())
|
||||
sm->musicPlay();
|
||||
}
|
||||
Game::get_game()->get_event()->set_mode(MOVE_MODE); // return control to player
|
||||
|
||||
active = false;
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "End conversation\n");
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if there is input available (placed at `in_str'.)
|
||||
*/
|
||||
bool Converse::input() {
|
||||
if (scroll->has_input()) {
|
||||
Std::string s = scroll->get_input();
|
||||
set_input(s);
|
||||
#ifdef CONVERSE_DEBUG
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Converse: INPUT \"%s\"\n\n", get_input().c_str());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Output string `s' or the current set output to the scroll view.
|
||||
*/
|
||||
void Converse::print(const char *s) {
|
||||
#ifdef CONVERSE_DEBUG
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Converse: PRINT \"%s\"\n\n", s ? s : get_output().c_str());
|
||||
#endif
|
||||
if (s)
|
||||
scroll->display_string(s, MSGSCROLL_NO_MAP_DISPLAY);
|
||||
else
|
||||
scroll->display_string(get_output(), MSGSCROLL_NO_MAP_DISPLAY);
|
||||
}
|
||||
|
||||
void Converse::print_prompt() {
|
||||
scroll->display_converse_prompt();
|
||||
}
|
||||
|
||||
/* Get string value of variable `varnum'.
|
||||
*/
|
||||
const char *Converse::get_svar(uint8 varnum) {
|
||||
if (varnum <= U6TALK_VAR__LAST_ && variables[varnum].sv)
|
||||
return (const char *)variables[varnum].sv;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/* Set string value of variable `varnum'.
|
||||
*/
|
||||
void Converse::set_svar(uint8 varnum, const char *set) {
|
||||
if (varnum <= U6TALK_VAR__LAST_)
|
||||
variables[varnum].sv = scumm_strdup(set);
|
||||
}
|
||||
|
||||
|
||||
/* Show portrait for npc `n'. The name will be shown for actors in the player
|
||||
* party, or those the player/avatar has met. The look-string will be shown for
|
||||
* anyone else.
|
||||
*/
|
||||
void Converse::show_portrait(uint8 n) {
|
||||
Game *game = Game::get_game();
|
||||
Actor *actor = (n == npc_num) ? npc : actors->get_actor(n);
|
||||
const char *nameret = nullptr;
|
||||
if (!actor)
|
||||
return;
|
||||
bool statue = (gametype == NUVIE_GAME_U6 && n >= 189 && n <= 191);
|
||||
if (gametype == NUVIE_GAME_U6 && n == 0) { // Pushme Pullyu
|
||||
Actor *real_actor = game->get_actor_manager()->get_actor(130);
|
||||
if (real_actor->is_met() || player->get_party()->contains_actor(real_actor))
|
||||
nameret = npc_name(130);
|
||||
else
|
||||
nameret = actors->look_actor(real_actor, false);
|
||||
} else if ((actor->is_met() || player->get_party()->contains_actor(actor))
|
||||
&& !statue) // they need to display statue of names
|
||||
nameret = npc_name(n);
|
||||
else
|
||||
nameret = actors->look_actor(actor, false);
|
||||
if (game->using_new_converse_gump()) {
|
||||
if ((game->is_original_plus() && game->get_converse_gump()->W() > game->get_game_width() - game->get_background()->get_border_width())
|
||||
|| game->is_orig_style())
|
||||
views->close_current_view();
|
||||
((ConverseGump *)scroll)->set_actor_portrait(actor);
|
||||
} else
|
||||
views->set_portrait_mode(actor, nameret);
|
||||
}
|
||||
|
||||
|
||||
/* Copy the NPC num's name from their conversation script. This is very U6
|
||||
* specific.
|
||||
* Returns the name as a non-modifiable string of 16 characters maximum. */
|
||||
const char *Converse::npc_name(uint8 num) {
|
||||
ConvScript *temp_script;
|
||||
convscript_buffer s_pt;
|
||||
aname[15] = '\0';
|
||||
|
||||
// FIX (crashing)
|
||||
// if(actors->get_actor(num))
|
||||
// actors->get_actor(num)->set_name(name);
|
||||
|
||||
if ((num == npc_num) && !_name.empty()) // use NPC name
|
||||
strncpy(aname, _name.c_str(), 15);
|
||||
else { // or load another script
|
||||
// uint32 temp_num = num;
|
||||
num = load_conv(get_script_num(num)); // get idx number; won't actually reload file
|
||||
temp_script = new ConvScript(src, num);
|
||||
s_pt = temp_script->get_buffer();
|
||||
if (!s_pt) {
|
||||
delete temp_script;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// read name up to LOOK section, convert "_" to "."
|
||||
uint32 c;
|
||||
for (c = 0; s_pt[c + 2] != 0xf1 && s_pt[c + 2] != 0xf3 && c <= 14; c++)
|
||||
aname[c] = s_pt[c + 2] != '_' ? s_pt[c + 2] : '.';
|
||||
|
||||
aname[c] = '\0';
|
||||
delete temp_script;
|
||||
}
|
||||
return aname;
|
||||
}
|
||||
|
||||
|
||||
/* Start checking i/o object for some input, (optionally block all but allowed
|
||||
* input) and tell interpreter to wait.
|
||||
*/
|
||||
void Converse::poll_input(const char *allowed, bool nonblock) {
|
||||
if (allowed_input)
|
||||
free(allowed_input);
|
||||
allowed_input = nullptr;
|
||||
allowed_input = (allowed && strlen(allowed)) ? scumm_strdup(allowed) : nullptr;
|
||||
|
||||
scroll->set_input_mode(true, allowed_input, nonblock);
|
||||
need_input = true;
|
||||
conv_i->wait();
|
||||
}
|
||||
|
||||
|
||||
/* Stop polling i/o, tell interpreter to stop waiting.
|
||||
*/
|
||||
void Converse::unwait() {
|
||||
need_input = false;
|
||||
conv_i->unwait();
|
||||
}
|
||||
|
||||
|
||||
/* Check talk input and determine if it needs to be handled before being passed
|
||||
* to the interpreter.
|
||||
* Returns false if the conversation should be stopped.
|
||||
*/
|
||||
bool Converse::override_input() {
|
||||
bool overide_cheat = Game::get_game()->are_cheats_enabled() && party_all_the_time;
|
||||
if (in_str.empty())
|
||||
in_str = "bye";
|
||||
else if (in_str == "look") {
|
||||
print("You see ");
|
||||
print(_desc.c_str());
|
||||
script->seek(script->pos() - 1); // back to ASK command
|
||||
} else if (overide_cheat && in_str == "join") {
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_U6 // altars and statues
|
||||
&& (npc->get_actor_num() >= 189 && npc->get_actor_num() <= 200))
|
||||
return true;
|
||||
else if (!npc->is_alive()) {
|
||||
print("\"How can I join you when I'm dead?\"\n*");
|
||||
return true;
|
||||
}
|
||||
if (!player->get_party()->contains_actor(npc))
|
||||
player->get_party()->add_actor(npc);
|
||||
print("\"Friends of Nuvie? Sure, I'll come along!\"\n*");
|
||||
return false;
|
||||
} else if (overide_cheat && in_str == "leave") {
|
||||
if (player->get_party()->contains_actor(npc))
|
||||
player->get_party()->remove_actor(npc);
|
||||
print("\"For Nuvie!\"\n*");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Converse::collect_input() {
|
||||
if (!Game::get_game()->using_new_converse_gump()) {
|
||||
print_prompt();
|
||||
}
|
||||
poll_input();
|
||||
}
|
||||
|
||||
/* If not waiting, continue the active script. If waiting for input, check i/o
|
||||
* object (scroll), taking the input if available. Else wait until the scroll's
|
||||
* page is unbroken.
|
||||
*/
|
||||
void Converse::continue_script() {
|
||||
speech->update();
|
||||
|
||||
if (running()) {
|
||||
if (!conv_i->waiting())
|
||||
conv_i->step();
|
||||
else if (need_input && input()) {
|
||||
print("\n\n");
|
||||
if (!override_input()) {
|
||||
need_input = false;
|
||||
conv_i->stop();
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
// assign value to declared input variable
|
||||
if (conv_i->var_input())
|
||||
conv_i->assign_input();
|
||||
set_svar(U6TALK_VAR_INPUT, get_input().c_str()); // set $Z
|
||||
unwait();
|
||||
} else if (!need_input && !scroll->get_page_break() && scroll->is_converse_finished()) {
|
||||
// if page unbroken, unpause script
|
||||
unwait();
|
||||
}
|
||||
// interpreter has stopped itself
|
||||
if (conv_i->end())
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*** ConvScript ***/
|
||||
|
||||
/* Init. and read data from U6Lib.
|
||||
*/
|
||||
ConvScript::ConvScript(U6Lib_n *s, uint32 idx) {
|
||||
buf = nullptr;
|
||||
buf_len = 0;
|
||||
src = s;
|
||||
src_index = idx;
|
||||
|
||||
ref = 0;
|
||||
cpy = nullptr;
|
||||
|
||||
read_script();
|
||||
|
||||
rewind();
|
||||
}
|
||||
|
||||
|
||||
/* Init. and use data from another ConvScript.
|
||||
*/
|
||||
ConvScript::ConvScript(ConvScript *orig) {
|
||||
src = nullptr;
|
||||
buf = nullptr;
|
||||
buf_len = 0;
|
||||
src_index = 0;
|
||||
compressed = false;
|
||||
|
||||
cpy = orig;
|
||||
ref = 1;
|
||||
cpy->ref += 1;
|
||||
|
||||
rewind();
|
||||
}
|
||||
|
||||
|
||||
ConvScript::~ConvScript() {
|
||||
if (ref == 0)
|
||||
free(buf);
|
||||
else if (cpy)
|
||||
cpy->ref -= 1;
|
||||
}
|
||||
|
||||
|
||||
/* Read (decode if necessary) the script data (with the pre-set item index) from
|
||||
* the loaded converse library.
|
||||
*/
|
||||
void ConvScript::read_script() {
|
||||
unsigned char *undec_script = 0; // item as it appears in library
|
||||
unsigned char *dec_script = 0; // decoded
|
||||
uint32 undec_len = 0, dec_len = 0;
|
||||
U6Lzw decoder;
|
||||
uint8 gametype = src->get_game_type();
|
||||
|
||||
undec_len = src->get_item_size(src_index);
|
||||
if (undec_len > 4) {
|
||||
undec_script = src->get_item(src_index);
|
||||
if (gametype == NUVIE_GAME_U6) {
|
||||
// decode
|
||||
if (!(undec_script[0] == 0 && undec_script[1] == 0
|
||||
&& undec_script[2] == 0 && undec_script[3] == 0)) {
|
||||
compressed = true;
|
||||
dec_script =
|
||||
decoder.decompress_buffer(undec_script, undec_len, dec_len);
|
||||
free(undec_script);
|
||||
} else {
|
||||
compressed = false;
|
||||
dec_len = undec_len - 4;
|
||||
dec_script = (unsigned char *)malloc(dec_len);
|
||||
memcpy(dec_script, undec_script + 4, dec_len);
|
||||
free(undec_script);
|
||||
}
|
||||
} else {
|
||||
// MD/SE compression handled by lzc library
|
||||
compressed = false;
|
||||
dec_len = undec_len;
|
||||
dec_script = undec_script;
|
||||
}
|
||||
}
|
||||
if (dec_len) {
|
||||
buf = (convscript_buffer)dec_script;
|
||||
buf_len = dec_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Returns 8bit value from current script location in LSB-first form.
|
||||
*/
|
||||
converse_value ConvScript::read(uint32 advance) {
|
||||
uint8 val = 0;
|
||||
while (advance--) {
|
||||
val = *buf_pt;
|
||||
++buf_pt;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/* Returns 16bit value from current script location in LSB-first form.
|
||||
*/
|
||||
converse_value ConvScript::read2() {
|
||||
uint16 val = 0;
|
||||
val = *(buf_pt++);
|
||||
val += *(buf_pt++) << 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/* Returns 32bit value from current script location in LSB-first form.
|
||||
*/
|
||||
converse_value ConvScript::read4() {
|
||||
uint32 val = 0;
|
||||
val = *(buf_pt++);
|
||||
val += *(buf_pt++) << 8;
|
||||
val += *(buf_pt++) << 16;
|
||||
val += *(buf_pt++) << 24;
|
||||
return val;
|
||||
}
|
||||
|
||||
void ConvScript::write2(converse_value val) {
|
||||
*(buf_pt++) = val & 0xff;
|
||||
*(buf_pt++) = (val >> 8) & 0xff;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ConverseGumpType get_converse_gump_type_from_config(const Configuration *config) {
|
||||
Std::string configvalue;
|
||||
config->value("config/general/converse_gump", configvalue, "default");
|
||||
|
||||
if (string_i_compare(configvalue, "default")) {
|
||||
return CONVERSE_GUMP_DEFAULT;
|
||||
} else if (string_i_compare(configvalue, "u7style")) {
|
||||
return CONVERSE_GUMP_U7_STYLE;
|
||||
} else if (string_i_compare(configvalue, "wou")) {
|
||||
return CONVERSE_GUMP_WOU_STYLE;
|
||||
}
|
||||
|
||||
return CONVERSE_GUMP_DEFAULT;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
269
engines/ultima/nuvie/core/converse.h
Normal file
269
engines/ultima/nuvie/core/converse.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_CONVERSE_H
|
||||
#define NUVIE_CORE_CONVERSE_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/views/view.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Actor;
|
||||
class ActorManager;
|
||||
class Configuration;
|
||||
class MsgScroll;
|
||||
class GameClock;
|
||||
class ObjManager;
|
||||
class Player;
|
||||
class ViewManager;
|
||||
class U6Lib_n;
|
||||
class U6Lzw;
|
||||
|
||||
class ConverseInterpret;
|
||||
class ConverseSpeech;
|
||||
class ConvScript;
|
||||
|
||||
using Std::string;
|
||||
|
||||
ConverseGumpType get_converse_gump_type_from_config(const Configuration *config);
|
||||
|
||||
typedef uint32 converse_value; // any single value read from a script
|
||||
typedef unsigned char *convscript_buffer;
|
||||
|
||||
typedef struct {
|
||||
uint8 type;
|
||||
converse_value val;
|
||||
} converse_typed_value;
|
||||
|
||||
#define U6TALK_VAR_SEX 0x10 // sex of avatar: male=0 female=1
|
||||
#define U6TALK_VAR_KARMA 0x14 // avatar's karma
|
||||
#define U6TALK_VAR_GARGF 0x15 // 1=player knows Gargish
|
||||
#define U6TALK_VAR_NPC_NAME 0x17
|
||||
#define U6TALK_VAR_PARTYLIVE 0x17 // number of people (living) following avatar
|
||||
#define U6TALK_VAR_PARTYALL 0x18 // number of people (total) following avatar
|
||||
#define U6TALK_VAR_HP 0x19 // avatar's health
|
||||
#define U6TALK_VAR_PLAYER_NAME 0x19
|
||||
#define U6TALK_VAR_QUESTF 0x1A // 0="Thou art not upon a sacred quest!"
|
||||
#define WOUTALK_VAR_ADD_TO_INVENTORY_FAILED 0x1D
|
||||
#define U6TALK_VAR_WORKTYPE 0x20 // current activity of npc, from schedule
|
||||
#define U6TALK_VAR_YSTRING 0x22 // value of $Y variable.
|
||||
#define U6TALK_VAR_INPUT 0x23 // previous input from player ($Z)
|
||||
#define U6TALK_VAR__LAST_ 0x25 // (all above 36 appear uninitialized)
|
||||
|
||||
/* Conversation engine, apart from the interpreter. Loads converse files,
|
||||
* and reads script into buffer. Also manages input/output and has npc-related
|
||||
* support functions. This class handles all game types.
|
||||
*/
|
||||
class Converse {
|
||||
friend class ConverseInterpret;
|
||||
friend class SETalkInterpret;
|
||||
friend class MDTalkInterpret;
|
||||
friend class WOUConverseInterpret;
|
||||
friend class U6ConverseInterpret;
|
||||
|
||||
// game system objects from nuvie
|
||||
const Configuration *config;
|
||||
GameClock *_clock;
|
||||
ActorManager *actors;
|
||||
ObjManager *objects;
|
||||
Player *player;
|
||||
ViewManager *views;
|
||||
MsgScroll *scroll; // i/o
|
||||
|
||||
nuvie_game_t gametype; // what game is being played?
|
||||
U6Lib_n *src;
|
||||
uint8 src_num; // identify source file: 0=unset/unused
|
||||
const char *src_name();
|
||||
|
||||
ConverseInterpret *conv_i; // interpreter
|
||||
ConvScript *script;
|
||||
View *last_view;
|
||||
Actor *npc;
|
||||
uint8 npc_num;
|
||||
uint8 script_num; //this could differ from npc_num when talking to guards or wisps etc.
|
||||
Std::string _name, _desc;
|
||||
|
||||
bool active; // running npc script? (either paused or unpaused)
|
||||
bool need_input; // waiting for text input
|
||||
bool party_all_the_time; // force NPCs to join player's party?
|
||||
|
||||
string in_str; // last input from player
|
||||
string out_str; // text that is to be printed
|
||||
char *allowed_input; // characters requested for single-character input
|
||||
|
||||
char aname[16]; // return from npc_name()
|
||||
struct converse_variables_s {
|
||||
converse_value cv;
|
||||
char *sv;
|
||||
} *variables; /* initialized for [U6TALK_VAR__LAST_+1] items */
|
||||
|
||||
ConverseSpeech *speech;
|
||||
bool using_fmtowns;
|
||||
|
||||
void reset();
|
||||
|
||||
public:
|
||||
Converse();
|
||||
~Converse();
|
||||
void init(const Configuration *cfg, nuvie_game_t t, MsgScroll *s, ActorManager *a,
|
||||
GameClock *c, Player *p, ViewManager *v, ObjManager *o);
|
||||
|
||||
uint32 get_script_num(uint8 a);
|
||||
void load_conv(const Std::string &convfilename);
|
||||
uint32 load_conv(uint8 a);
|
||||
void unload_conv() {
|
||||
delete src;
|
||||
src = nullptr;
|
||||
}
|
||||
ConvScript *load_script(uint32 n);
|
||||
ConverseInterpret *new_interpreter();
|
||||
|
||||
bool start(Actor *a) {
|
||||
return start(a->get_actor_num());
|
||||
}
|
||||
bool start(uint8 n);
|
||||
void continue_script();
|
||||
void stop();
|
||||
|
||||
bool running() const {
|
||||
return active;
|
||||
}
|
||||
bool is_waiting_for_scroll() {
|
||||
return scroll->get_page_break();
|
||||
}
|
||||
void unwait();
|
||||
void poll_input(const char *allowed = nullptr, bool nonblock = true);
|
||||
bool override_input();
|
||||
void collect_input();
|
||||
|
||||
bool input();
|
||||
void print(const char *s = nullptr);
|
||||
const Std::string &get_input() const {
|
||||
return in_str;
|
||||
}
|
||||
const Std::string &get_output() const {
|
||||
return out_str;
|
||||
}
|
||||
void set_input(Std::string s) {
|
||||
in_str = s;
|
||||
}
|
||||
void set_output(Std::string s) {
|
||||
out_str = s;
|
||||
}
|
||||
|
||||
void set_party_all_the_time(bool val) {
|
||||
party_all_the_time = val;
|
||||
}
|
||||
const char *npc_name(uint8 num);
|
||||
void show_portrait(uint8 n);
|
||||
converse_value get_var(uint8 varnum) const {
|
||||
return (varnum <= U6TALK_VAR__LAST_ ? variables[varnum].cv : 0x00);
|
||||
}
|
||||
const char *get_svar(uint8 varnum);
|
||||
void set_var(uint8 varnum, uint32 val) {
|
||||
if (varnum <= U6TALK_VAR__LAST_) variables[varnum].cv = val;
|
||||
}
|
||||
void set_svar(uint8 varnum, const char *set);
|
||||
void init_variables();
|
||||
void delete_variables();
|
||||
|
||||
ConverseSpeech *get_speech() {
|
||||
return speech;
|
||||
};
|
||||
|
||||
bool conversations_stop_music;
|
||||
private:
|
||||
void print_prompt();
|
||||
};
|
||||
|
||||
|
||||
/* Conversation script container. Maintains current position in the script. The
|
||||
* object only exists if it has data loaded. Different classes with an identical
|
||||
* interface can be created to handle different games' file formats.
|
||||
*/
|
||||
class ConvScript {
|
||||
friend class Converse;
|
||||
|
||||
convscript_buffer buf;
|
||||
uint32 buf_len;
|
||||
convscript_buffer buf_pt; // pointer into script (current location)
|
||||
|
||||
U6Lib_n *src;
|
||||
uint32 src_index;
|
||||
bool compressed; // was the original file (LZW) compressed?
|
||||
|
||||
uint8 ref; // Multiple objects can use the same buffer
|
||||
ConvScript *cpy;
|
||||
|
||||
public:
|
||||
ConvScript(U6Lib_n *s, uint32 idx);
|
||||
ConvScript(ConvScript *orig);
|
||||
~ConvScript();
|
||||
|
||||
void read_script();
|
||||
bool loaded() const {
|
||||
return ((buf && buf_len)); // script is loaded?
|
||||
}
|
||||
|
||||
/* Reading */
|
||||
converse_value read(uint32 advance = 1);
|
||||
converse_value read2();
|
||||
converse_value read4();
|
||||
converse_value peek(uint32 displacement = 0) {
|
||||
return ((converse_value) * (buf_pt + displacement));
|
||||
}
|
||||
|
||||
/* Writing */
|
||||
void write2(converse_value val);
|
||||
|
||||
/* Seeking - update script pointer */
|
||||
void rewind() {
|
||||
buf_pt = buf;
|
||||
}
|
||||
void skip(uint32 bytes = 1) {
|
||||
buf_pt += bytes;
|
||||
}
|
||||
void seek(uint32 offset = 0) {
|
||||
rewind();
|
||||
skip(offset);
|
||||
}
|
||||
|
||||
uint32 pos() const {
|
||||
return buf_pt - buf;
|
||||
}
|
||||
bool overflow(uint32 ptadd = 0) const {
|
||||
return (((pos() + ptadd) >= buf_len));
|
||||
}
|
||||
convscript_buffer get_buffer(uint32 ptadd = 0) {
|
||||
return ((!ptadd || (ptadd < buf_len)) ? buf + ptadd : nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1554
engines/ultima/nuvie/core/converse_interpret.cpp
Normal file
1554
engines/ultima/nuvie/core/converse_interpret.cpp
Normal file
File diff suppressed because it is too large
Load Diff
338
engines/ultima/nuvie/core/converse_interpret.h
Normal file
338
engines/ultima/nuvie/core/converse_interpret.h
Normal file
@@ -0,0 +1,338 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_CONVERSE_INTERPRET_H
|
||||
#define NUVIE_CORE_CONVERSE_INTERPRET_H
|
||||
|
||||
#include "ultima/nuvie/core/converse.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::string;
|
||||
using Std::vector;
|
||||
|
||||
/* Control and value opcodes for op() & evop() (U6) */
|
||||
#define U6OP_GT 0x81
|
||||
#define U6OP_GE 0x82
|
||||
#define U6OP_LT 0x83
|
||||
#define U6OP_LE 0x84
|
||||
#define U6OP_NE 0x85
|
||||
#define U6OP_EQ 0x86
|
||||
#define U6OP_ADD 0x90
|
||||
#define U6OP_SUB 0x91
|
||||
#define U6OP_MUL 0x92
|
||||
#define U6OP_DIV 0x93
|
||||
#define U6OP_LOR 0x94
|
||||
#define U6OP_LAND 0x95
|
||||
#define U6OP_CANCARRY 0x9a
|
||||
#define U6OP_WEIGHT 0x9b
|
||||
#define U6OP_HORSED 0x9d
|
||||
#define U6OP_HASOBJ 0x9f
|
||||
#define U6OP_RAND 0xa0
|
||||
#define U6OP_EVAL 0xa7
|
||||
#define U6OP_FLAG 0xab
|
||||
#define U6OP_VAR 0xb2
|
||||
#define U6OP_SVAR 0xb3
|
||||
#define U6OP_DATA 0xb4
|
||||
#define U6OP_OBJCOUNT 0xbb
|
||||
#define U6OP_INPARTY 0xc6
|
||||
#define U6OP_OBJINPARTY 0xc7
|
||||
#define U6OP_JOIN 0xca
|
||||
#define U6OP_LEAVE 0xcc
|
||||
#define U6OP_NPCNEARBY 0xd7
|
||||
#define U6OP_WOUNDED 0xda
|
||||
#define U6OP_POISONED 0xdc
|
||||
#define U6OP_NPC 0xdd
|
||||
#define U6OP_EXP 0xe0
|
||||
#define U6OP_LVL 0xe1
|
||||
#define U6OP_STR 0xe2
|
||||
#define U6OP_INT 0xe3
|
||||
#define U6OP_DEX 0xe4
|
||||
|
||||
#define U6OP_HORSE 0x9c
|
||||
#define U6OP_SLEEP 0x9e
|
||||
#define U6OP_IF 0xa1
|
||||
#define U6OP_ENDIF 0xa2
|
||||
#define U6OP_ELSE 0xa3
|
||||
#define U6OP_SETF 0xa4
|
||||
#define U6OP_CLEARF 0xa5
|
||||
#define U6OP_DECL 0xa6
|
||||
#define U6OP_ASSIGN 0xa8
|
||||
#define U6OP_JUMP 0xb0
|
||||
#define U6OP_DPRINT 0xb5
|
||||
#define U6OP_BYE 0xb6
|
||||
#define U6OP_INDEXOF 0xb7
|
||||
#define U6OP_NEW 0xb9
|
||||
#define U6OP_DELETE 0xba
|
||||
#define U6OP_INVENTORY 0xbe
|
||||
#define U6OP_PORTRAIT 0xbf
|
||||
#define U6OP_ADDKARMA 0xc4
|
||||
#define U6OP_SUBKARMA 0xc5
|
||||
#define U6OP_GIVE 0xc9
|
||||
#define U6OP_WAIT 0xcb
|
||||
#define U6OP_WORKTYPE 0xcd
|
||||
#define U6OP_RESURRECT 0xd6
|
||||
#define U6OP_SETNAME 0xd8
|
||||
#define U6OP_HEAL 0xd9
|
||||
#define U6OP_CURE 0xdb
|
||||
#define U6OP_ENDANSWER 0xee
|
||||
#define U6OP_KEYWORDS 0xef
|
||||
#define U6OP_SLOOK 0xf1
|
||||
#define U6OP_SCONVERSE 0xf2
|
||||
#define U6OP_SPREFIX 0xf3
|
||||
#define U6OP_ANSWER 0xf6
|
||||
#define U6OP_ASK 0xf7
|
||||
#define U6OP_ASKC 0xf8
|
||||
#define U6OP_INPUTSTR 0xf9
|
||||
#define U6OP_INPUT 0xfb
|
||||
#define U6OP_INPUTNUM 0xfc
|
||||
#define U6OP_SIDENT 0xff
|
||||
|
||||
#define U6OP_ENDDATA 0xb8
|
||||
|
||||
#define MDOP_MISC_ACTION 0xd1
|
||||
|
||||
/* Script is executed as it is stepped through byte-by-byte, and can have
|
||||
* text, data, and control codes. Flow is controlled by run-level stack.
|
||||
*/
|
||||
class ConverseInterpret {
|
||||
protected:
|
||||
Converse *converse; // to get data from container
|
||||
|
||||
bool is_waiting; // return control to Converse, paused waiting for something
|
||||
bool stopped; // conversation will end, after control returns to Converse
|
||||
uint8 answer_mode; // should a response has been triggered by input?
|
||||
#define ANSWER_NO 0 /* keywords don't match */
|
||||
#define ANSWER_YES 1 /* keywords match */
|
||||
#define ANSWER_DONE 2 /* already answered */
|
||||
|
||||
// input values (from the script)
|
||||
struct in_val_s {
|
||||
converse_value v; // data
|
||||
uint8 d; // data-size or 0x00
|
||||
};
|
||||
// ONE data item from a converse script db
|
||||
struct converse_db_s {
|
||||
uint8 type; // 0=s 1=i
|
||||
char *s;
|
||||
uint8 i;
|
||||
};
|
||||
// frame around blocks of code that may or may not execute
|
||||
struct convi_frame_s {
|
||||
uint32 start;
|
||||
converse_value start_c; // enter on c
|
||||
bool run; // run(true) or skip(false) instructions
|
||||
converse_value break_c; // will toggle run setting
|
||||
};
|
||||
|
||||
vector<struct in_val_s> in; // control values (input/instruction)
|
||||
uint32 in_start;
|
||||
string text; // input text from script
|
||||
vector<Std::string> rstrings; // string value(s) returned by op
|
||||
string ystring; // modified by SETNAME, accessed with "$Y"
|
||||
uint8 decl_v; // declared/initialized variable number
|
||||
uint8 decl_t; // declared variable type: 0x00=none,0xb2=int,0xb3=string
|
||||
Common::Stack<struct convi_frame_s *> *b_frame;
|
||||
|
||||
bool db_lvar;
|
||||
converse_value db_loc;
|
||||
converse_value db_offset;
|
||||
|
||||
|
||||
const char *get_rstr(uint32 sn) const {
|
||||
return ((sn < rstrings.size()) ? rstrings[sn].c_str() : "");
|
||||
}
|
||||
const string &get_ystr() const {
|
||||
return ystring;
|
||||
}
|
||||
void set_ystr(const char *s);
|
||||
void set_rstr(uint32 sn, const char *s);
|
||||
converse_value add_rstr(const char *s);
|
||||
|
||||
void let(uint8 v, uint8 t) {
|
||||
decl_v = v;
|
||||
decl_t = t;
|
||||
}
|
||||
void let() {
|
||||
decl_v = decl_t = 0x00;
|
||||
}
|
||||
|
||||
void enter(converse_value c);
|
||||
void leave();
|
||||
void leave_all() {
|
||||
while (b_frame && !b_frame->empty()) leave();
|
||||
}
|
||||
struct convi_frame_s *top_frame() const {
|
||||
return ((b_frame && !b_frame->empty()) ? b_frame->top() : nullptr);
|
||||
}
|
||||
void do_frame(converse_value c);
|
||||
|
||||
void set_break(converse_value c) {
|
||||
if (top_frame()) top_frame()->break_c = c;
|
||||
}
|
||||
converse_value get_break() const {
|
||||
return (top_frame() ? top_frame()->break_c : 0x00);
|
||||
}
|
||||
void clear_break() {
|
||||
set_break(0x00);
|
||||
}
|
||||
void set_run(bool r) {
|
||||
if (top_frame()) top_frame()->run = r;
|
||||
}
|
||||
bool get_run() const {
|
||||
return (top_frame() ? top_frame()->run : true);
|
||||
}
|
||||
|
||||
public:
|
||||
ConverseInterpret(Converse *owner);
|
||||
virtual ~ConverseInterpret();
|
||||
|
||||
bool waiting() const {
|
||||
return is_waiting;
|
||||
}
|
||||
void wait() {
|
||||
is_waiting = true;
|
||||
}
|
||||
void unwait() {
|
||||
is_waiting = false;
|
||||
}
|
||||
void stop() {
|
||||
stopped = true;
|
||||
wait();
|
||||
}
|
||||
bool end() {
|
||||
return stopped;
|
||||
}
|
||||
|
||||
void step();
|
||||
|
||||
protected:
|
||||
/* collecting from script */
|
||||
virtual void collect_input();
|
||||
virtual struct in_val_s read_value();
|
||||
void eval(uint32 vi = 0);
|
||||
|
||||
void add_val(converse_value c, uint8 d = 0);
|
||||
void add_text(unsigned char c = 0);
|
||||
|
||||
/* manipulating collected input */
|
||||
uint32 val_count() const {
|
||||
return in.size();
|
||||
}
|
||||
converse_value get_val(uint32 vi);
|
||||
uint8 get_val_size(uint32 vi);
|
||||
converse_value pop_val();
|
||||
uint8 pop_val_size();
|
||||
const Std::string &get_text() const {
|
||||
return text;
|
||||
}
|
||||
void flush() {
|
||||
in.resize(0);
|
||||
in_start = 0;
|
||||
text.clear();
|
||||
}
|
||||
|
||||
/* operating on input */
|
||||
void exec();
|
||||
void do_ctrl();
|
||||
void do_text();
|
||||
string get_formatted_text(const char *c_str);
|
||||
converse_value pop_arg(Common::Stack<converse_typed_value> &vs);
|
||||
converse_typed_value pop_typed_arg(Common::Stack<converse_typed_value> &vs);
|
||||
virtual bool evop(Common::Stack<converse_typed_value> &i);
|
||||
virtual bool op(Common::Stack<converse_typed_value> &i);
|
||||
|
||||
virtual bool op_create_new(Common::Stack<converse_typed_value> &i);
|
||||
|
||||
converse_value evop_eq(Common::Stack<converse_typed_value> &vs);
|
||||
|
||||
public:
|
||||
virtual uint8 npc_num(uint32 n);//uint8 npc_num(uint32 n){return((n!=0xeb)?n:converse->npc_num);}
|
||||
bool check_keywords(Std::string keystr, Std::string instr);
|
||||
bool var_input() const {
|
||||
return decl_t != 0x00;
|
||||
}
|
||||
void assign_input(); // set declared variable to Converse input
|
||||
struct converse_db_s *get_db(uint32 loc, uint32 i);
|
||||
converse_value get_db_integer(uint32 loc, uint32 i);
|
||||
void set_db_integer(uint32 loc, uint32 i, converse_value val);
|
||||
char *get_db_string(uint32 loc, uint32 i);
|
||||
converse_value find_db_string(uint32 loc, const char *dstring);
|
||||
|
||||
/* value tests */
|
||||
virtual bool is_print(converse_value check) const {
|
||||
return (((check == 0x0a) || (check >= 0x20 && check <= 0x7a) || (check == 0x7e) || (check == 0x7b))); //added '~' 0x7e, '{' 0x7b for fm towns.
|
||||
}
|
||||
virtual bool is_ctrl(converse_value code) const {
|
||||
return (((code >= 0xa1 || code == 0x9c || code == 0x9e) && !is_valop(code) && !is_datasize(code)));
|
||||
}
|
||||
virtual bool is_datasize(converse_value check) const {
|
||||
return ((check == 0xd3 || check == 0xd2 || check == 0xd4));
|
||||
}
|
||||
virtual bool is_valop(converse_value check) const {
|
||||
return (((check == 0x81) || (check == 0x82) || (check == 0x83)
|
||||
|| (check == 0x84) || (check == 0x85) || (check == 0x86)
|
||||
|| (check == 0x90) || (check == 0x91) || (check == 0x92)
|
||||
|| (check == 0x93) || (check == 0x94) || (check == 0x95)
|
||||
|| (check == 0x9a) || (check == 0x9b) || (check == 0x9d)
|
||||
|| (check == 0x9f) || (check == 0xa0) || (check == 0xa7)
|
||||
|| (check == 0xab) || (check == 0xb2) || (check == 0xb3)
|
||||
|| (check == 0xb4) || (check == 0xb7) || (check == 0xbb) || (check == 0xc6)
|
||||
|| (check == 0xc7) || (check == 0xca) || (check == 0xcc)
|
||||
|| (check == 0xd7) || (check == 0xda) || (check == 0xdc)
|
||||
|| (check == 0xdd) || (check == 0xe0) || (check == 0xe1)
|
||||
|| (check == 0xe2) || (check == 0xe3) || (check == 0xe4)));
|
||||
}
|
||||
const char *evop_str(converse_value op);
|
||||
const char *op_str(converse_value op);
|
||||
};
|
||||
|
||||
|
||||
class U6ConverseInterpret : public ConverseInterpret {
|
||||
public:
|
||||
U6ConverseInterpret(Converse *owner) : ConverseInterpret(owner) { }
|
||||
// ~U6ConverseInterpret();
|
||||
};
|
||||
|
||||
class WOUConverseInterpret : public ConverseInterpret {
|
||||
public:
|
||||
WOUConverseInterpret(Converse *owner) : ConverseInterpret(owner) { }
|
||||
|
||||
protected:
|
||||
bool op_create_new(Common::Stack<converse_typed_value> &i) override;
|
||||
};
|
||||
|
||||
class SETalkInterpret : public ConverseInterpret {
|
||||
public:
|
||||
SETalkInterpret(Converse *owner) : ConverseInterpret(owner) { }
|
||||
};
|
||||
|
||||
|
||||
class MDTalkInterpret : public WOUConverseInterpret {
|
||||
public:
|
||||
MDTalkInterpret(Converse *owner) : WOUConverseInterpret(owner) { }
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
217
engines/ultima/nuvie/core/converse_speech.cpp
Normal file
217
engines/ultima/nuvie/core/converse_speech.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/core/converse_speech.h"
|
||||
#include "ultima/nuvie/sound/sound_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
ConverseSpeech::ConverseSpeech() : config(nullptr) {
|
||||
}
|
||||
|
||||
|
||||
/* Initialize global classes from the game.
|
||||
*/
|
||||
void ConverseSpeech::init(const Configuration *cfg) {
|
||||
config = cfg;
|
||||
}
|
||||
|
||||
|
||||
ConverseSpeech::~ConverseSpeech() {
|
||||
}
|
||||
|
||||
void ConverseSpeech::update() {
|
||||
TownsSound sound;
|
||||
SoundManager *sm = Game::get_game()->get_sound_manager();
|
||||
|
||||
if (!sm->is_audio_enabled() || !sm->is_speech_enabled())
|
||||
return;
|
||||
|
||||
if (!list.empty()) {
|
||||
if (sm->isSoundPLaying(handle) == false) {
|
||||
list.pop_front();
|
||||
if (!list.empty()) {
|
||||
sound = list.front();
|
||||
handle = sm->playTownsSound(sound.filename, sound.sample_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConverseSpeech::play_speech(uint16 actor_num, uint16 sample_num) {
|
||||
Common::Path sample_file;
|
||||
char filename[20]; // "/speech/charxxx.sam"
|
||||
TownsSound sound;
|
||||
SoundManager *sm = Game::get_game()->get_sound_manager();
|
||||
|
||||
if (!sm->is_audio_enabled() || !sm->is_speech_enabled())
|
||||
return;
|
||||
|
||||
//translate the converse sample number into the CHAR number in the SPEECH directory if required.
|
||||
|
||||
if (actor_num == 202) //GUARDS
|
||||
actor_num = 228;
|
||||
|
||||
if (actor_num == 201) //WISPS
|
||||
actor_num = 229;
|
||||
|
||||
sample_num--;
|
||||
|
||||
Common::sprintf_s(filename, "speech%cchar%u.sam", U6PATH_DELIMITER, actor_num);
|
||||
|
||||
config->pathFromValue("config/townsdir", filename, sample_file);
|
||||
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Loading Speech Sample %s:%d\n", sample_file.toString().c_str(), sample_num);
|
||||
|
||||
sound.filename = sample_file;
|
||||
sound.sample_num = sample_num;
|
||||
|
||||
if (list.empty())
|
||||
handle = sm->playTownsSound(sample_file, sample_num);
|
||||
|
||||
list.push_back(sound);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NuvieIOBuffer *ConverseSpeech::load_speech(const Common::Path &filename, uint16 sample_num) {
|
||||
unsigned char *compressed_data, *raw_audio, *wav_data;
|
||||
sint16 *converted_audio;
|
||||
uint32 decomp_size;
|
||||
uint32 upsampled_size;
|
||||
sint16 sample = 0, prev_sample;
|
||||
U6Lib_n sam_file;
|
||||
U6Lzw lzw;
|
||||
NuvieIOBuffer *wav_buffer = 0;
|
||||
uint32 j, k;
|
||||
|
||||
sam_file.open(filename, 4);
|
||||
|
||||
compressed_data = sam_file.get_item(sample_num, nullptr);
|
||||
raw_audio = lzw.decompress_buffer(compressed_data, sam_file.get_item_size(sample_num), decomp_size);
|
||||
|
||||
free(compressed_data);
|
||||
|
||||
if (raw_audio != nullptr) {
|
||||
wav_buffer = new NuvieIOBuffer();
|
||||
upsampled_size = decomp_size + (int)floor((decomp_size - 1) / 4) * (2 + 2 + 2 + 1);
|
||||
|
||||
switch ((decomp_size - 1) % 4) {
|
||||
case 1 :
|
||||
upsampled_size += 2;
|
||||
break;
|
||||
case 2 :
|
||||
upsampled_size += 4;
|
||||
break;
|
||||
case 3 :
|
||||
upsampled_size += 6;
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG(0, LEVEL_DEBUGGING, "decomp_size %d, upsampled_size %d\n", decomp_size, upsampled_size);
|
||||
|
||||
wav_data = (unsigned char *)malloc(upsampled_size * sizeof(sint16) + 44); // 44 = size of wav header
|
||||
|
||||
wav_buffer->open(wav_data, upsampled_size * sizeof(sint16) + 44, false);
|
||||
wav_init_header(wav_buffer, upsampled_size);
|
||||
|
||||
converted_audio = (sint16 *)&wav_data[44];
|
||||
|
||||
prev_sample = convert_sample(raw_audio[0]);
|
||||
|
||||
for (j = 1, k = 0; j < decomp_size; j++, k++) {
|
||||
converted_audio[k] = prev_sample;
|
||||
|
||||
sample = convert_sample(raw_audio[j]);
|
||||
|
||||
switch (j % 4) { // calculate the in-between samples using linear interpolation.
|
||||
case 0 :
|
||||
case 1 :
|
||||
case 2 :
|
||||
converted_audio[k + 1] = (sint16)(0.666 * (float)prev_sample + 0.333 * (float)sample);
|
||||
converted_audio[k + 2] = (sint16)(0.333 * (float)prev_sample + 0.666 * (float)sample);
|
||||
k += 2;
|
||||
break;
|
||||
case 3 :
|
||||
converted_audio[k + 1] = (sint16)(0.5 * (float)(prev_sample + sample));
|
||||
k += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
prev_sample = sample;
|
||||
}
|
||||
|
||||
converted_audio[k] = sample;
|
||||
}
|
||||
|
||||
free(raw_audio);
|
||||
|
||||
return wav_buffer;
|
||||
}
|
||||
|
||||
inline sint16 ConverseSpeech::convert_sample(uint16 raw_sample) {
|
||||
sint16 sample;
|
||||
|
||||
if (raw_sample & 128)
|
||||
sample = ((sint16)(abs(128 - raw_sample) * 256) ^ 0xffff) + 1;
|
||||
else
|
||||
sample = raw_sample * 256;
|
||||
|
||||
// FIXME: Following code is for Big Endian sample conversion
|
||||
// This was required for older libSDL audio output.
|
||||
// May not be needed for ScummVM audio output?
|
||||
#if 0
|
||||
sint16 temp_sample = sample >> 8;
|
||||
temp_sample |= (sample & 0xff) << 8;
|
||||
sample = temp_sample;
|
||||
#endif
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
void ConverseSpeech::wav_init_header(NuvieIOBuffer *wav_buffer, uint32 audio_length) const {
|
||||
wav_buffer->writeBuf((const unsigned char *)"RIFF", 4);
|
||||
wav_buffer->write4(36 + audio_length * 2); //length of RIFF chunk
|
||||
wav_buffer->writeBuf((const unsigned char *)"WAVE", 4);
|
||||
wav_buffer->writeBuf((const unsigned char *)"fmt ", 4);
|
||||
wav_buffer->write4(16); // length of format chunk
|
||||
wav_buffer->write2(1); // PCM encoding
|
||||
wav_buffer->write2(1); // mono
|
||||
wav_buffer->write4(44100); // sample frequency 16KHz
|
||||
wav_buffer->write4(44100 * 2); // sample rate
|
||||
wav_buffer->write2(2); // BlockAlign
|
||||
wav_buffer->write2(16); // Bits per sample
|
||||
|
||||
wav_buffer->writeBuf((const unsigned char *)"data", 4);
|
||||
wav_buffer->write4(audio_length * 2); // length of data chunk
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
63
engines/ultima/nuvie/core/converse_speech.h
Normal file
63
engines/ultima/nuvie/core/converse_speech.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_CONVERSE_SPEECH_H
|
||||
#define NUVIE_CORE_CONVERSE_SPEECH_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class U6Lib_n;
|
||||
class U6Lzw;
|
||||
class NuvieIOBuffer;
|
||||
|
||||
typedef struct TownsSound {
|
||||
Common::Path filename;
|
||||
uint16 sample_num;
|
||||
} TownsSound;
|
||||
|
||||
class ConverseSpeech {
|
||||
// game system objects from nuvie
|
||||
const Configuration *config;
|
||||
Audio::SoundHandle handle;
|
||||
Std::list<TownsSound> list;
|
||||
|
||||
public:
|
||||
ConverseSpeech();
|
||||
~ConverseSpeech();
|
||||
void init(const Configuration *cfg);
|
||||
void update();
|
||||
void play_speech(uint16 actor_num, uint16 sample_num);
|
||||
|
||||
protected:
|
||||
NuvieIOBuffer *load_speech(const Common::Path &filename, uint16 sample_num);
|
||||
inline sint16 convert_sample(uint16 raw_sample);
|
||||
void wav_init_header(NuvieIOBuffer *wav_buffer, uint32 audio_length) const;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
264
engines/ultima/nuvie/core/cursor.cpp
Normal file
264
engines/ultima/nuvie/core/cursor.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_shape.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/core/cursor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::string;
|
||||
using Std::vector;
|
||||
|
||||
|
||||
Cursor::Cursor() : cursor_id(0), cur_x(-1), cur_y(-1), cleanup(nullptr),
|
||||
hidden(false), screen(nullptr), config(nullptr), screen_w(0), screen_h(0) {
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if mouse pointers file was loaded.
|
||||
*/
|
||||
bool Cursor::init(const Configuration *c, Screen *s, nuvie_game_t game_type) {
|
||||
Std::string file;
|
||||
Common::Path filename;
|
||||
bool enable_cursors;
|
||||
|
||||
config = c;
|
||||
screen = s;
|
||||
|
||||
screen_w = screen->get_width();
|
||||
screen_h = screen->get_height();
|
||||
|
||||
config->value("config/general/enable_cursors", enable_cursors, true);
|
||||
|
||||
if (!enable_cursors)
|
||||
return false;
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
file = "u6mcga.ptr";
|
||||
break;
|
||||
case NUVIE_GAME_SE :
|
||||
file = "secursor.ptr";
|
||||
break;
|
||||
case NUVIE_GAME_MD :
|
||||
file = "mdcursor.ptr";
|
||||
break;
|
||||
}
|
||||
|
||||
config_get_path(config, file, filename);
|
||||
|
||||
if (filename != "")
|
||||
if (load_all(filename, game_type) > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Load pointers from `filename'. (lzw -> s_lib_32 -> shapes)
|
||||
* Returns the number found in the file.
|
||||
*/
|
||||
uint32 Cursor::load_all(const Common::Path &filename, nuvie_game_t game_type) {
|
||||
U6Lzw decompressor;
|
||||
U6Lib_n pointer_list;
|
||||
NuvieIOBuffer iobuf;
|
||||
uint32 slib32_len = 0;
|
||||
unsigned char *slib32_data;
|
||||
if (game_type != NUVIE_GAME_U6) {
|
||||
U6Lib_n file;
|
||||
file.open(filename, 4, game_type);
|
||||
slib32_data = file.get_item(0);
|
||||
slib32_len = file.get_item_size(0);
|
||||
} else {
|
||||
slib32_data = decompressor.decompress_file(filename, slib32_len);
|
||||
}
|
||||
|
||||
if (slib32_len == 0)
|
||||
return 0;
|
||||
// FIXME: u6lib_n assumes u6 libs have no filesize header
|
||||
iobuf.open(slib32_data, slib32_len);
|
||||
free(slib32_data);
|
||||
|
||||
if (!pointer_list.open(&iobuf, 4, NUVIE_GAME_MD))
|
||||
return 0;
|
||||
|
||||
|
||||
uint32 num_read = 0, num_total = pointer_list.get_num_items();
|
||||
cursors.resize(num_total);
|
||||
while (num_read < num_total) { // read each into a new MousePointer
|
||||
MousePointer *ptr = nullptr;
|
||||
U6Shape *shape = new U6Shape;
|
||||
unsigned char *data = pointer_list.get_item(num_read);
|
||||
if (!shape->load(data)) {
|
||||
free(data);
|
||||
delete shape;
|
||||
break;
|
||||
}
|
||||
ptr = new MousePointer; // set from shape data
|
||||
shape->get_hot_point(&(ptr->point_x), &(ptr->point_y));
|
||||
shape->get_size(&(ptr->w), &(ptr->h));
|
||||
ptr->shapedat = (unsigned char *)malloc(ptr->w * ptr->h);
|
||||
memcpy(ptr->shapedat, shape->get_data(), ptr->w * ptr->h);
|
||||
cursors[num_read++] = ptr;
|
||||
|
||||
free(data);
|
||||
delete shape;
|
||||
}
|
||||
pointer_list.close();
|
||||
iobuf.close();
|
||||
return num_read;
|
||||
}
|
||||
|
||||
|
||||
/* Free data.
|
||||
*/
|
||||
void Cursor::unload_all() {
|
||||
for (uint32 i = 0; i < cursors.size(); i++) {
|
||||
if (cursors[i] && cursors[i]->shapedat)
|
||||
free(cursors[i]->shapedat);
|
||||
delete cursors[i];
|
||||
}
|
||||
if (cleanup)
|
||||
free(cleanup);
|
||||
}
|
||||
|
||||
|
||||
/* Set active pointer.
|
||||
*/
|
||||
bool Cursor::set_pointer(uint8 ptr_num) {
|
||||
if (ptr_num >= cursors.size() || !cursors[ptr_num])
|
||||
return false;
|
||||
|
||||
cursor_id = ptr_num;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Draw self on screen at px,py, or at mouse location if px or py is -1.
|
||||
* Returns false on failure.
|
||||
*/
|
||||
bool Cursor::display(int px, int py) {
|
||||
if (cursors.empty() || !cursors[cursor_id])
|
||||
return false;
|
||||
if (hidden)
|
||||
return true;
|
||||
if (px == -1 || py == -1) {
|
||||
screen->get_mouse_location(&px, &py);
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"mouse pos: %d,%d", px, py);
|
||||
}
|
||||
MousePointer *ptr = cursors[cursor_id];
|
||||
|
||||
fix_position(ptr, px, py); // modifies px, py
|
||||
save_backing((uint32)px, (uint32)py, (uint32)ptr->w, (uint32)ptr->h);
|
||||
|
||||
screen->blit((uint16)px, (uint16)py, ptr->shapedat, 8, ptr->w, ptr->h, ptr->w, true);
|
||||
|
||||
// screen->update(px, py, ptr->w, ptr->h);
|
||||
add_update(px, py, ptr->w, ptr->h);
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Restore backing behind cursor (hide until next display). Must call update()
|
||||
* sometime after to remove from screen.
|
||||
*/
|
||||
void Cursor::clear() {
|
||||
if (cleanup) {
|
||||
screen->restore_area(cleanup, &cleanup_area);
|
||||
cleanup = nullptr;
|
||||
// screen->update(cleanup_area.left, cleanup_area.top, cleanup_area.w, cleanup_area.h);
|
||||
add_update(cleanup_area.left, cleanup_area.top, cleanup_area.width(), cleanup_area.height());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Offset requested position px,py by pointer hotspot, and screen boundary.
|
||||
*/
|
||||
inline void Cursor::fix_position(MousePointer *ptr, int &px, int &py) {
|
||||
if ((px - ptr->point_x) < 0) // offset by hotspot
|
||||
px = 0;
|
||||
else
|
||||
px -= ptr->point_x;
|
||||
if ((py - ptr->point_y) < 0)
|
||||
py = 0;
|
||||
else
|
||||
py -= ptr->point_y;
|
||||
if ((px + ptr->w) >= screen_w) // don't draw offscreen
|
||||
px = screen_w - ptr->w - 1;
|
||||
if ((py + ptr->h) >= screen_h)
|
||||
py = screen_h - ptr->h - 1;
|
||||
}
|
||||
|
||||
|
||||
/* Copy cleanup area (cursor backingstore) from screen.
|
||||
*/
|
||||
void Cursor::save_backing(uint32 px, uint32 py, uint32 w, uint32 h) {
|
||||
if (cleanup) {
|
||||
free(cleanup);
|
||||
cleanup = nullptr;
|
||||
}
|
||||
|
||||
cleanup_area.left = px; // cursor must be drawn LAST for this to work
|
||||
cleanup_area.top = py;
|
||||
cleanup_area.setWidth(w);
|
||||
cleanup_area.setHeight(h);
|
||||
cleanup = screen->copy_area(&cleanup_area);
|
||||
}
|
||||
|
||||
|
||||
/* Mark update_area (cleared/displayed) as updated on the screen.
|
||||
*/
|
||||
void Cursor::update() {
|
||||
screen->update(update_area.left, update_area.top, update_area.width(), update_area.height());
|
||||
update_area = Common::Rect();
|
||||
}
|
||||
|
||||
|
||||
/* Add to update_area.
|
||||
*/
|
||||
void Cursor::add_update(uint16 x, uint16 y, uint16 w, uint16 h) {
|
||||
if (update_area.width() == 0 || update_area.height() == 0) {
|
||||
update_area.left = x;
|
||||
update_area.top = y;
|
||||
update_area.setWidth(w);
|
||||
update_area.setHeight(h);
|
||||
} else {
|
||||
uint16 x2 = x + w, y2 = y + h,
|
||||
update_x2 = update_area.right, update_y2 = update_area.bottom;
|
||||
if (x <= update_area.left) update_area.left = x;
|
||||
if (y <= update_area.top) update_area.top = y;
|
||||
if (x2 >= update_x2) update_x2 = x2;
|
||||
if (y2 >= update_y2) update_y2 = y2;
|
||||
update_area.setWidth(update_x2 - update_area.left);
|
||||
update_area.setHeight(update_y2 - update_area.top);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
112
engines/ultima/nuvie/core/cursor.h
Normal file
112
engines/ultima/nuvie/core/cursor.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_CURSOR_H
|
||||
#define NUVIE_CORE_CURSOR_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Screen;
|
||||
class U6Shape;
|
||||
|
||||
typedef struct {
|
||||
uint16 point_x, point_y; // hotspot
|
||||
unsigned char *shapedat;
|
||||
uint16 w, h;
|
||||
} MousePointer;
|
||||
|
||||
|
||||
/* Contains all mouse pointers, with hotspot and draw methods that work on the
|
||||
* active cursor.
|
||||
*/
|
||||
class Cursor {
|
||||
friend class Screen;
|
||||
Screen *screen;
|
||||
const Configuration *config;
|
||||
sint32 cur_x, cur_y; // location on screen, unused normally
|
||||
Std::vector<MousePointer *> cursors; // pointer list
|
||||
uint8 cursor_id; // which pointer is active
|
||||
|
||||
unsigned char *cleanup; // restore image behind cursor
|
||||
Common::Rect cleanup_area;
|
||||
Common::Rect update_area; // clear & display are updated at once (avoid flicker)
|
||||
|
||||
bool hidden;
|
||||
|
||||
uint16 screen_w, screen_h;
|
||||
|
||||
void add_update(uint16 x, uint16 y, uint16 w, uint16 h);
|
||||
inline void fix_position(MousePointer *ptr, int &px, int &py);
|
||||
void save_backing(uint32 px, uint32 py, uint32 w, uint32 h);
|
||||
|
||||
public:
|
||||
Cursor();
|
||||
~Cursor() {
|
||||
unload_all();
|
||||
}
|
||||
bool init(const Configuration *c, Screen *s, nuvie_game_t game_type);
|
||||
uint32 load_all(const Common::Path &filename, nuvie_game_t game_type);
|
||||
void unload_all();
|
||||
bool set_pointer(uint8 ptr_num);
|
||||
|
||||
void reset_position() {
|
||||
cur_x = -1;
|
||||
cur_y = -1;
|
||||
}
|
||||
void move(uint32 px, uint32 py) {
|
||||
cur_x = px;
|
||||
cur_y = py;
|
||||
}
|
||||
void hide() {
|
||||
hidden = true;
|
||||
clear();
|
||||
update();
|
||||
}
|
||||
void show() {
|
||||
hidden = false;
|
||||
}
|
||||
|
||||
void get_hotspot(uint16 &x, uint16 &y) const {
|
||||
x = cursors[cursor_id]->point_x;
|
||||
y = cursors[cursor_id]->point_y;
|
||||
}
|
||||
bool display() {
|
||||
return display(cur_x, cur_y);
|
||||
}
|
||||
bool display(int px, int py);
|
||||
void clear();
|
||||
void update();
|
||||
|
||||
bool is_visible() const {
|
||||
return !hidden;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
31
engines/ultima/nuvie/core/debug.cpp
Normal file
31
engines/ultima/nuvie/core/debug.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
1872
engines/ultima/nuvie/core/effect.cpp
Normal file
1872
engines/ultima/nuvie/core/effect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
703
engines/ultima/nuvie/core/effect.h
Normal file
703
engines/ultima/nuvie/core/effect.h
Normal file
@@ -0,0 +1,703 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_EFFECT_H
|
||||
#define NUVIE_CORE_EFFECT_H
|
||||
|
||||
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/core/anim_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
//class Actor;
|
||||
class EffectManager;
|
||||
class Game;
|
||||
class MapWindow;
|
||||
class NuvieAnim;
|
||||
class Screen;
|
||||
class TimedAdvance;
|
||||
class TimedCallback;
|
||||
class ObjManager;
|
||||
|
||||
// Effects add themselves to EffectManager and most start immediately.
|
||||
|
||||
/* Effects: * = unwritten or untested
|
||||
* Quake - earthquake from cyclops or volcanos
|
||||
* Hit - hit actor anim + sfx
|
||||
* Explosive - explosion caused by powder keg, volcanos, or cannonball hit
|
||||
* ThrowObject - any thrown object or tile
|
||||
* Cannonball (FIX: change to UseCodeThrow)
|
||||
* Missile - throw object to ground or actor; optionally cause damage
|
||||
* *Boomerang - spin Missile and return to sender
|
||||
* Drop - throw obj from inventory to ground
|
||||
* Sleep - pause game & advance time quickly
|
||||
* Fade - fade the mapwindow in or out
|
||||
* GameFadeIn - blocks user-input until Fade is complete
|
||||
* *Palette - do something with the color palette
|
||||
* Vanish - fade from an image of the mapwindow to the real mapwindow
|
||||
* *FadeObject - might not need this since Vanish can be used
|
||||
* U6WhitePotion - will probably make PaletteEffect to do this
|
||||
*/
|
||||
|
||||
|
||||
/* Control animation and sounds in the game world.
|
||||
*/
|
||||
class Effect : public CallBack {
|
||||
protected:
|
||||
Game *game;
|
||||
EffectManager *effect_manager;
|
||||
bool defunct;
|
||||
|
||||
uint32 retain_count;
|
||||
|
||||
public:
|
||||
Effect();
|
||||
~Effect() override;
|
||||
|
||||
void retain() {
|
||||
retain_count++;
|
||||
}
|
||||
void release() {
|
||||
if (retain_count > 0) retain_count--;
|
||||
}
|
||||
bool is_retained() const {
|
||||
return retain_count == 0 ? false : true;
|
||||
}
|
||||
|
||||
void delete_self() {
|
||||
defunct = true;
|
||||
}
|
||||
void add_anim(NuvieAnim *anim);
|
||||
|
||||
bool is_defunct() const {
|
||||
return defunct;
|
||||
}
|
||||
uint16 callback(uint16, CallBack *, void *) override {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* Toss a cannon ball from one actor to another, or from an object towards
|
||||
* a numbered direction.
|
||||
*/
|
||||
class CannonballEffect : public Effect {
|
||||
UseCode *usecode;
|
||||
NuvieAnim *anim;
|
||||
// *sfx;
|
||||
Obj *obj;
|
||||
MapCoord target_loc; // where cannonball will hit
|
||||
|
||||
void start_anim();
|
||||
|
||||
public:
|
||||
CannonballEffect(Obj *src_obj, sint8 direction = -1);
|
||||
// CannonballEffect(Actor *src_actor, Actor *target_actor); from a ship
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
class ProjectileEffect : public Effect {
|
||||
protected:
|
||||
uint16 tile_num;
|
||||
|
||||
MapCoord start_loc; // where explosion will start
|
||||
vector<MapCoord> targets;
|
||||
uint8 anim_speed;
|
||||
bool trail;
|
||||
uint16 initial_tile_rotation;
|
||||
uint16 rotation_amount;
|
||||
uint8 src_tile_y_offset;
|
||||
uint16 finished_tiles;
|
||||
|
||||
vector<MapEntity> hit_entities;
|
||||
|
||||
virtual void start_anim();
|
||||
|
||||
public:
|
||||
ProjectileEffect() : tile_num(0), anim_speed(0), trail(false),
|
||||
initial_tile_rotation(0), rotation_amount(0), src_tile_y_offset(0),
|
||||
finished_tiles(0) {
|
||||
}
|
||||
ProjectileEffect(uint16 tileNum, MapCoord start, MapCoord target, uint8 speed, bool trailFlag, uint16 initialTileRotation, uint16 rotationAmount, uint8 src_y_offset);
|
||||
ProjectileEffect(uint16 tileNum, MapCoord start, const vector<MapCoord> &t, uint8 speed, bool trailFlag, uint16 initialTileRotation);
|
||||
|
||||
void init(uint16 tileNum, MapCoord start, const vector<MapCoord> &t, uint8 speed, bool trailFlag, uint16 initialTileRotation, uint16 rotationAmount, uint8 src_y_offset);
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
|
||||
vector<MapEntity> *get_hit_entities() {
|
||||
return &hit_entities;
|
||||
}
|
||||
};
|
||||
|
||||
class ExpEffect : public ProjectileEffect {
|
||||
//UseCode *usecode;
|
||||
NuvieAnim *anim;
|
||||
|
||||
//Obj *obj;
|
||||
uint16 exp_tile_num;
|
||||
|
||||
protected:
|
||||
void start_anim() override;
|
||||
public:
|
||||
ExpEffect(uint16 tileNum, const MapCoord &location);
|
||||
|
||||
};
|
||||
|
||||
/* Use to add an effect with timed activity. Self-contained timer must be
|
||||
* stopped/started with the included methods.
|
||||
*/
|
||||
class TimedEffect : public Effect {
|
||||
protected:
|
||||
TimedCallback *timer;
|
||||
public:
|
||||
TimedEffect() {
|
||||
timer = nullptr;
|
||||
}
|
||||
TimedEffect(uint32 delay) {
|
||||
timer = nullptr;
|
||||
start_timer(delay);
|
||||
}
|
||||
~TimedEffect() override {
|
||||
stop_timer();
|
||||
}
|
||||
|
||||
void start_timer(uint32 delay);
|
||||
void stop_timer();
|
||||
|
||||
void delete_self() {
|
||||
stop_timer();
|
||||
Effect::delete_self();
|
||||
}
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override {
|
||||
if (msg == MESG_TIMED) delete_self(); //= 0;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Shake the visible play area around.
|
||||
*/
|
||||
class QuakeEffect : public TimedEffect {
|
||||
MapWindow *map_window;
|
||||
static QuakeEffect *current_quake; // do nothing if already active
|
||||
sint32 sx, sy; // last map_window movement amount
|
||||
MapCoord orig; // map_window location at start
|
||||
Actor *orig_actor; // center map_window on actor
|
||||
uint32 stop_time;
|
||||
uint8 strength; // magnitude
|
||||
|
||||
public:
|
||||
QuakeEffect(uint8 magnitude, uint32 duration, Actor *keep_on = nullptr);
|
||||
~QuakeEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
|
||||
void init_directions();
|
||||
void recenter_map();
|
||||
void stop_quake();
|
||||
};
|
||||
|
||||
|
||||
/* Hit target actor.
|
||||
*/
|
||||
class HitEffect : public Effect {
|
||||
public:
|
||||
HitEffect(Actor *target, uint32 duration = 300);
|
||||
HitEffect(const MapCoord &location);
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
/* Print text to MapWindow for a given duration
|
||||
*/
|
||||
class TextEffect : public Effect {
|
||||
|
||||
public:
|
||||
TextEffect(Std::string text);
|
||||
TextEffect(Std::string text, const MapCoord &location);
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
|
||||
/* Create explosion animation and sounds from the source location out to
|
||||
* specified radius. Hit actors and objects for `dmg'.
|
||||
*/
|
||||
class ExplosiveEffect : public Effect {
|
||||
protected:
|
||||
NuvieAnim *anim;
|
||||
// *sfx;
|
||||
MapCoord start_at;
|
||||
uint32 radius;
|
||||
uint16 hit_damage; // hp taken off actors hit by explosion
|
||||
|
||||
void start_anim();
|
||||
|
||||
public:
|
||||
ExplosiveEffect(uint16 x, uint16 y, uint32 size, uint16 dmg = 0);
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
|
||||
// children can override
|
||||
virtual void delete_self() {
|
||||
Effect::delete_self();
|
||||
}
|
||||
virtual bool hit_object(Obj *obj) {
|
||||
return false; // explosion hit something
|
||||
}
|
||||
// true return=end effect
|
||||
};
|
||||
|
||||
|
||||
/* Explosion that sends usecode event to an object on completion.
|
||||
*/
|
||||
class UseCodeExplosiveEffect : public ExplosiveEffect {
|
||||
Obj *obj; // explosion came from this object (can be nullptr)
|
||||
Obj *original_obj; // don't hit this object (chain-reaction avoidance hack)
|
||||
|
||||
public:
|
||||
UseCodeExplosiveEffect(Obj *src_obj, uint16 x, uint16 y, uint32 size, uint16 dmg = 0, Obj *dont_hit_me = nullptr)
|
||||
: ExplosiveEffect(x, y, size, dmg), obj(src_obj), original_obj(dont_hit_me) {
|
||||
}
|
||||
void delete_self() override;
|
||||
bool hit_object(Obj *hit_obj) override; // explosion hit something
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* Toss object tile from one location to another with a TossAnim, and play a
|
||||
* sound effect. The ThrowObjectEffect is constructed with uninitialized
|
||||
* parameters and isn't started until start_anim() is called.
|
||||
*/
|
||||
class ThrowObjectEffect : public Effect {
|
||||
protected:
|
||||
ObjManager *obj_manager;
|
||||
NuvieAnim *anim; // TossAnim
|
||||
// *sfx;
|
||||
MapCoord start_at, stop_at; // start_at -> stop_at
|
||||
Obj *throw_obj; // object being thrown
|
||||
const Tile *throw_tile; // graphic to use (default is object's tile)
|
||||
uint16 throw_speed; // used in animation
|
||||
uint16 degrees; // rotation of tile
|
||||
uint8 stop_flags; // TossAnim blocking flags
|
||||
|
||||
public:
|
||||
ThrowObjectEffect();
|
||||
~ThrowObjectEffect() override { }
|
||||
|
||||
void hit_target(); // stops effect
|
||||
void start_anim();
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override = 0;
|
||||
};
|
||||
|
||||
|
||||
/* Drop an object from an actor's inventory. Object is removed from the actor
|
||||
* after starting the effect, and added to the map when the effect is complete.
|
||||
* Effect speed is gametype-defined.
|
||||
*/
|
||||
class DropEffect : public ThrowObjectEffect {
|
||||
Actor *drop_from_actor;
|
||||
public:
|
||||
DropEffect(Obj *obj, uint16 qty, Actor *actor, MapCoord *drop_loc);
|
||||
|
||||
void hit_target();
|
||||
|
||||
void get_obj(Obj *obj, uint16 qty);
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
#define MISSILE_DEFAULT_SPEED 200
|
||||
#define MISSILE_HIT_TARGET TOSS_TO_BLOCKING
|
||||
#define MISSILE_HIT_OBJECTS (TOSS_TO_BLOCKING|TOSS_TO_OBJECT)
|
||||
#define MISSILE_HIT_ACTORS (TOSS_TO_BLOCKING|TOSS_TO_ACTOR)
|
||||
#define MISSILE_HIT_ALL (TOSS_TO_BLOCKING|TOSS_TO_OBJECT|TOSS_TO_ACTOR)
|
||||
|
||||
/* Throw a missile towards a target location. If the target is an actor or
|
||||
* object, it will be hit for the requested damage. If the target is an empty
|
||||
* map location, the object will be added to the map. The missile always stops
|
||||
* if hitting a blocking tile.
|
||||
*
|
||||
* Decide in the attack logic, before constructing this, whether or not it was
|
||||
* successful, and use the appropriate constructor. You can set the effect to
|
||||
* hit any actors or objects in the way if the attack missed.
|
||||
*/
|
||||
class MissileEffect : public ThrowObjectEffect {
|
||||
ActorManager *actor_manager;
|
||||
|
||||
uint16 hit_damage; // hp taken off actor/object hit by missile
|
||||
Actor *hit_actor;
|
||||
Obj *hit_obj;
|
||||
|
||||
public:
|
||||
MissileEffect(uint16 tile_num, uint16 obj_n, const MapCoord &source,
|
||||
const MapCoord &target, uint8 dmg, uint8 intercept = MISSILE_HIT_TARGET, uint16 speed = MISSILE_DEFAULT_SPEED);
|
||||
|
||||
void init(uint16 tile_num, uint16 obj_n, const MapCoord &source,
|
||||
const MapCoord &target, uint32 dmg, uint8 intercept, uint32 speed);
|
||||
void hit_target();
|
||||
void hit_blocking();
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* Throw an object and bring it back.
|
||||
*/
|
||||
class BoomerangEffect : public ThrowObjectEffect {
|
||||
// I might even add an arc from the center line for a cool effect.
|
||||
};
|
||||
|
||||
|
||||
/* Cycle or modify the game palette in some way.
|
||||
*/
|
||||
class PaletteEffect : public TimedEffect {
|
||||
// palette effects are created from child classes (new BlackPotionEffect();)
|
||||
// ...and these can include SFX like any other effect
|
||||
// but PaletteEffect is not abstract (new PaletteEffect(timing & color params...);)
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* For sleeping at inns. Fade-out, advance time, and fade-in.
|
||||
*/
|
||||
class SleepEffect : public Effect {
|
||||
TimedAdvance *timer; // timed event
|
||||
uint8 stop_hour, stop_minute; // sleep until this time
|
||||
Std::string stop_time;
|
||||
public:
|
||||
SleepEffect(Std::string until);
|
||||
SleepEffect(uint8 to_hour);
|
||||
~SleepEffect() override;
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
void delete_self();
|
||||
};
|
||||
|
||||
|
||||
typedef enum { FADE_PIXELATED, FADE_CIRCLE, FADE_PIXELATED_ONTOP } FadeType;
|
||||
typedef enum { FADE_IN, FADE_OUT } FadeDirection;
|
||||
|
||||
/* Manipulate the MapWindow for two types of fades. One is a stippled-like fade
|
||||
* that draws pixels to random locations on the screen until completely flooded
|
||||
* with a set color. The other changes the ambient light until fully black.
|
||||
*/
|
||||
class FadeEffect : public TimedEffect {
|
||||
protected:
|
||||
static FadeEffect *current_fade; // do nothing if already active
|
||||
|
||||
MapWindow *map_window;
|
||||
Screen *screen; // for PIXELATED, the overlay is blitted to the screen...
|
||||
Common::Rect *viewport; // ...at the MapWindow coordinates set here
|
||||
Graphics::ManagedSurface *overlay; // this is what gets blitted
|
||||
|
||||
FadeType fade_type; // PIXELATED[_ONTOP] or CIRCLE
|
||||
FadeDirection fade_dir; // IN (removing color) or OUT (adding color)
|
||||
uint32 fade_speed; // meaning of this depends on fade_type
|
||||
uint8 pixelated_color; // color from palette that is being faded to/from
|
||||
Graphics::ManagedSurface *fade_from; // image being faded from or to (or nullptr if coloring)
|
||||
uint16 fade_x, fade_y; // start fade from this point (to fade_from size)
|
||||
|
||||
uint32 evtime, prev_evtime; // time of last message to callback()
|
||||
uint32 pixel_count, colored_total; // number of pixels total/colored
|
||||
uint16 fade_iterations; // number of times we've updated the fade effect
|
||||
|
||||
public:
|
||||
FadeEffect(FadeType fade, FadeDirection dir, uint32 color = 0, uint32 speed = 0);
|
||||
FadeEffect(FadeType fade, FadeDirection dir, Graphics::ManagedSurface *capture, uint32 speed = 0);
|
||||
FadeEffect(FadeType fade, FadeDirection dir, Graphics::ManagedSurface *capture, uint16 x, uint16 y, uint32 speed = 0);
|
||||
~FadeEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
|
||||
bool pixelated_fade_out();
|
||||
bool pixelated_fade_in();
|
||||
bool circle_fade_out();
|
||||
bool circle_fade_in();
|
||||
|
||||
void delete_self();
|
||||
|
||||
protected:
|
||||
void init(FadeType fade, FadeDirection dir, uint32 color, Graphics::ManagedSurface *capture, uint16 x, uint16 y, uint32 speed);
|
||||
void init_pixelated_fade();
|
||||
void init_circle_fade();
|
||||
|
||||
inline bool find_free_pixel(uint32 &rnum, uint32 pixel_count);
|
||||
uint32 pixels_to_check();
|
||||
bool pixelated_fade_core(uint32 pixels_to_check, sint16 fade_to);
|
||||
// inline uint32 get_random_pixel(uint16 center_thresh = 0);
|
||||
};
|
||||
|
||||
|
||||
/* Front-end to FadeEffect that fades in, and resumes game.
|
||||
*/
|
||||
class GameFadeInEffect : public FadeEffect {
|
||||
public:
|
||||
GameFadeInEffect(uint32 color);
|
||||
~GameFadeInEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
|
||||
/* Captures an image of the MapWindow without an object, then places the object
|
||||
* on the map and fades to the new image. (or the opposite if FADE_OUT is used)
|
||||
*/
|
||||
class FadeObjectEffect : public Effect {
|
||||
ObjManager *obj_manager;
|
||||
Obj *fade_obj;
|
||||
FadeDirection fade_dir;
|
||||
public:
|
||||
FadeObjectEffect(Obj *obj, FadeDirection dir);
|
||||
~FadeObjectEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
|
||||
/* Do a blocking fade-to (FADE_OUT) from a captured image of the game area, to
|
||||
* the active game area. (transparent) This is used for vanish or morph effects.
|
||||
*/
|
||||
#define VANISH_WAIT true
|
||||
#define VANISH_NOWAIT false
|
||||
class VanishEffect : public Effect {
|
||||
bool input_blocked;
|
||||
public:
|
||||
VanishEffect(bool pause_user = VANISH_NOWAIT);
|
||||
~VanishEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
class TileFadeEffect : public TimedEffect {
|
||||
//TileAnim *anim;
|
||||
//Tile *to_tile;
|
||||
//Tile *anim_tile;
|
||||
Actor *actor;
|
||||
//uint8 color_from, color_to;
|
||||
bool inc_reverse;
|
||||
uint16 spd;
|
||||
|
||||
uint16 num_anim_running;
|
||||
public:
|
||||
TileFadeEffect(const MapCoord &loc, Tile *from, Tile *to, FadeType type, uint16 speed);
|
||||
//TileFadeEffect(MapCoord loc, Tile *from, uint8 color_from, uint8 color_to, bool reverse, uint16 speed);
|
||||
TileFadeEffect(Actor *a, uint16 speed);
|
||||
~TileFadeEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
|
||||
protected:
|
||||
void add_actor_anim();
|
||||
void add_fade_anim(const MapCoord &loc, Tile *tile);
|
||||
void add_tile_anim(const MapCoord &loc, Tile *tile);
|
||||
void add_obj_anim(Obj *obj);
|
||||
};
|
||||
|
||||
class TileBlackFadeEffect : public TimedEffect {
|
||||
Actor *actor;
|
||||
Obj *obj;
|
||||
uint8 color;
|
||||
bool reverse;
|
||||
uint16 fade_speed;
|
||||
|
||||
uint16 num_anim_running;
|
||||
public:
|
||||
TileBlackFadeEffect(Actor *a, uint8 fade_color, uint16 speed);
|
||||
TileBlackFadeEffect(Obj *o, uint8 fade_color, uint16 speed);
|
||||
~TileBlackFadeEffect() override;
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
protected:
|
||||
void init(uint8 fade_color, uint16 speed);
|
||||
void add_actor_anim();
|
||||
void add_obj_anim(Obj *o);
|
||||
void add_tile_anim(const MapCoord &loc, Tile *tile);
|
||||
};
|
||||
|
||||
/* Briefly modify the mapwindow colors, disable map-blacking and player
|
||||
* movement for a few seconds, then enable both.
|
||||
*/
|
||||
class XorEffect : public TimedEffect {
|
||||
MapWindow *map_window;
|
||||
uint32 length;
|
||||
Graphics::ManagedSurface *capture; // this is what gets blitted
|
||||
|
||||
void xor_capture(uint8 mod);
|
||||
void init_effect();
|
||||
|
||||
public:
|
||||
/* eff_ms=length of visual effect */
|
||||
XorEffect(uint32 eff_ms);
|
||||
~XorEffect() override { }
|
||||
|
||||
/* Called by the timer between each effect stage. */
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
/* Briefly modify the mapwindow colors, disable map-blacking and player
|
||||
* movement for a few seconds, then enable both.
|
||||
*/
|
||||
class U6WhitePotionEffect : public TimedEffect {
|
||||
MapWindow *map_window;
|
||||
uint8 state; // 0=start, 1=eff1, 2=eff2, 3=x-ray, 4=complete
|
||||
uint32 start_length, eff1_length, eff2_length, xray_length;
|
||||
Graphics::ManagedSurface *capture; // this is what gets blitted
|
||||
Obj *potion; // allows effect to call usecode and delete object
|
||||
|
||||
void xor_capture(uint8 mod);
|
||||
void init_effect();
|
||||
|
||||
public:
|
||||
/* eff_ms=length of visual effect; delay_ms=length of x-ray effect */
|
||||
U6WhitePotionEffect(uint32 eff_ms, uint32 delay_ms, Obj *callback_obj = nullptr);
|
||||
~U6WhitePotionEffect() override { }
|
||||
|
||||
/* Called by the timer between each effect stage. */
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
|
||||
class XRayEffect : public TimedEffect {
|
||||
uint32 xray_length;
|
||||
void init_effect();
|
||||
|
||||
public:
|
||||
/* eff_ms=length of x-ray effect */
|
||||
XRayEffect(uint32 eff_ms);
|
||||
~XRayEffect() override { }
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
/* Pause the game, create an effect, and wait for user input to continue. */
|
||||
class PauseEffect: public Effect {
|
||||
public:
|
||||
/* Called by the Effect handler when input is available. */
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
virtual void delete_self() {
|
||||
Effect::delete_self();
|
||||
}
|
||||
PauseEffect();
|
||||
~PauseEffect() override { }
|
||||
};
|
||||
|
||||
/* Gather text from scroll input then continue. */
|
||||
class TextInputEffect: public Effect {
|
||||
Std::string input;
|
||||
public:
|
||||
/* Called by the Effect handler when input is available. */
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
TextInputEffect(const char *allowed_chars, bool can_escape);
|
||||
~TextInputEffect() override { }
|
||||
Std::string get_input() {
|
||||
return input;
|
||||
}
|
||||
};
|
||||
|
||||
class WizardEyeEffect: public Effect {
|
||||
public:
|
||||
/* Called by the Effect handler when input is available. */
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
virtual void delete_self() {
|
||||
Effect::delete_self();
|
||||
}
|
||||
WizardEyeEffect(const MapCoord &location, uint16 duration);
|
||||
~WizardEyeEffect() override { }
|
||||
};
|
||||
|
||||
/* colors for PeerEffect */
|
||||
const uint8 peer_tilemap[4] = {
|
||||
0x0A, // GROUND/PASSABLE
|
||||
0x09, // WATER
|
||||
0x07, // WALLS/BLOCKED
|
||||
0x0C // DANGER/DAMAGING
|
||||
};
|
||||
|
||||
#define PEER_TILEW 4
|
||||
const uint8 peer_tile[PEER_TILEW * PEER_TILEW] = {
|
||||
0, 1, 0, 1,
|
||||
1, 0, 1, 0,
|
||||
0, 1, 0, 1,
|
||||
1, 0, 1, 0
|
||||
};
|
||||
|
||||
/* Display an overview of the current area in the MapWindow. Any new actions
|
||||
* cancel the effect and return to the prompt.
|
||||
* (area is 48x48 tiles around the player, regardless of MapWindow size)
|
||||
*/
|
||||
class PeerEffect : public PauseEffect {
|
||||
MapWindow *map_window;
|
||||
Graphics::ManagedSurface *overlay; // this is what gets blitted
|
||||
Obj *gem; // allows effect to call usecode and delete object
|
||||
MapCoord area; // area to display (top-left corner)
|
||||
uint8 tile_trans; // peer_tile transparency mask (0 or 1)
|
||||
uint16 map_pitch;
|
||||
|
||||
inline void blit_tile(uint16 x, uint16 y, uint8 c);
|
||||
inline void blit_actor(Actor *actor);
|
||||
inline uint8 get_tilemap_type(uint16 wx, uint16 wy, uint8 wz);
|
||||
void fill_buffer(uint8 *mapbuffer, uint16 x, uint16 y);
|
||||
void peer();
|
||||
|
||||
public:
|
||||
PeerEffect(uint16 x, uint16 y, uint8 z, Obj *callback_obj = 0);
|
||||
~PeerEffect() override { }
|
||||
void init_effect();
|
||||
void delete_self() override;
|
||||
};
|
||||
|
||||
class WingStrikeEffect : public Effect {
|
||||
protected:
|
||||
|
||||
Actor *actor;
|
||||
|
||||
public:
|
||||
WingStrikeEffect(Actor *target_actor);
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
class HailStormEffect : public Effect {
|
||||
public:
|
||||
HailStormEffect(const MapCoord &target);
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
#define EFFECT_PROCESS_GUI_INPUT true
|
||||
|
||||
/* Run an effect asynchronously and keep updating the world until the effect completes. */
|
||||
class AsyncEffect : public Effect {
|
||||
protected:
|
||||
Effect *effect;
|
||||
bool effect_complete;
|
||||
|
||||
public:
|
||||
AsyncEffect(Effect *e);
|
||||
~AsyncEffect() override;
|
||||
void run(bool process_gui_input = false);
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data) override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
135
engines/ultima/nuvie/core/effect_manager.cpp
Normal file
135
engines/ultima/nuvie/core/effect_manager.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/core/timed_event.h"
|
||||
#include "ultima/nuvie/core/effect.h"
|
||||
#include "ultima/nuvie/core/effect_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
EffectManager::EffectManager() {
|
||||
}
|
||||
|
||||
EffectManager::~EffectManager() {
|
||||
while (!effects.empty())
|
||||
delete_effect(effects.front());
|
||||
}
|
||||
|
||||
/* Delete an effect and remove it from the list.
|
||||
*/
|
||||
void EffectManager::delete_effect(Effect *eff) {
|
||||
EffectIterator ei = effects.begin();
|
||||
while (ei != effects.end()) {
|
||||
if (*ei == eff) {
|
||||
signal_watch(eff);
|
||||
delete eff;
|
||||
effects.erase(ei);
|
||||
return;
|
||||
}
|
||||
++ei;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add an (already existent) effect to the list.
|
||||
*/
|
||||
void EffectManager::add_effect(Effect *eff) {
|
||||
effects.push_back(eff);
|
||||
}
|
||||
|
||||
/* Delete completed effects.
|
||||
*/
|
||||
void EffectManager::update_effects() {
|
||||
EffectIterator ei = effects.begin();
|
||||
while (ei != effects.end()) {
|
||||
if ((*ei)->is_defunct()/* && !has_message(*ei)*/) {
|
||||
signal_watch(*ei);
|
||||
if ((*ei)->is_retained() == false) { //if no longer needed by other objects we can delete.
|
||||
delete(*ei);
|
||||
ei = effects.erase(ei);
|
||||
} else ++ei;
|
||||
} else ++ei;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if there are any effects still active.
|
||||
*/
|
||||
bool EffectManager::has_effects() const {
|
||||
if (!effects.empty()) {
|
||||
ConstEffectIterator i = effects.begin();
|
||||
while (i != effects.end())
|
||||
if (!(*i)->is_defunct()) // effect is still active
|
||||
return true;
|
||||
}
|
||||
return false; // no effects, or all effects are complete
|
||||
}
|
||||
|
||||
/* Add a watched effect. This will send effect completion message to the
|
||||
* target when the effect is deleted.
|
||||
*/
|
||||
void EffectManager::watch_effect(CallBack *callback_target, Effect *watch) {
|
||||
EffectWatch new_watch;
|
||||
new_watch.watcher = callback_target;
|
||||
new_watch.effect = watch;
|
||||
watched.push_back(new_watch);
|
||||
}
|
||||
|
||||
/* Remove a watched effect, or all watched effects for target.
|
||||
*/
|
||||
void EffectManager::unwatch_effect(CallBack *callback_target, Effect *watch) {
|
||||
if (!watched.empty()) {
|
||||
WatchIterator i = watched.begin();
|
||||
while (i != watched.end())
|
||||
if ((*i).watcher == callback_target
|
||||
&& ((*i).effect == watch || watch == nullptr)) {
|
||||
i = watched.erase(i); // resume from next element
|
||||
} else ++i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Signal effect completion if it is being watched, and stop watching it.
|
||||
*/
|
||||
void EffectManager::signal_watch(Effect *effect) {
|
||||
EffectWatch *watch = find_effect_watch(effect);
|
||||
if (watch) {
|
||||
if (watch->watcher)
|
||||
watch->watcher->callback(EFFECT_CB_COMPLETE, nullptr, effect);
|
||||
unwatch_effect(watch->watcher, effect);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns watch for an effect. (or nullptr)
|
||||
*/
|
||||
EffectManager::EffectWatch *EffectManager::find_effect_watch(Effect *effect) {
|
||||
if (!watched.empty()) {
|
||||
WatchIterator i = watched.begin();
|
||||
while (i != watched.end())
|
||||
if ((*i).effect == effect)
|
||||
return (&(*i));
|
||||
else ++i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
68
engines/ultima/nuvie/core/effect_manager.h
Normal file
68
engines/ultima/nuvie/core/effect_manager.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_EFFECT_MANAGER_H
|
||||
#define NUVIE_CORE_EFFECT_MANAGER_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Effect;
|
||||
|
||||
/* This just keeps a list of Effect pointers, and deletes them when requested.
|
||||
*/
|
||||
class EffectManager {
|
||||
friend class Effect;
|
||||
typedef Std::vector<Effect *>::iterator EffectIterator;
|
||||
typedef Std::vector<Effect *>::const_iterator ConstEffectIterator;
|
||||
/* For each EffectWatch, a message will be sent to "watcher" when
|
||||
"effect" is deleted. */
|
||||
typedef struct {
|
||||
CallBack *watcher;
|
||||
Effect *effect;
|
||||
} EffectWatch;
|
||||
typedef Std::vector<EffectWatch>::iterator WatchIterator;
|
||||
|
||||
Std::vector<Effect *> effects; // the simple list
|
||||
Std::vector<EffectWatch> watched;
|
||||
|
||||
void add_effect(Effect *eff); // only effects can add themselves
|
||||
void signal_watch(Effect *effect);
|
||||
EffectWatch *find_effect_watch(Effect *effect);
|
||||
|
||||
public:
|
||||
EffectManager();
|
||||
~EffectManager();
|
||||
|
||||
void delete_effect(Effect *eff); // anyone may delete an effect
|
||||
void update_effects(); // check and delete
|
||||
|
||||
bool has_effects() const;
|
||||
void watch_effect(CallBack *callback_target, Effect *watch);
|
||||
void unwatch_effect(CallBack *callback_target, Effect *watch = nullptr);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
240
engines/ultima/nuvie/core/egg_manager.cpp
Normal file
240
engines/ultima/nuvie/core/egg_manager.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/misc/u6_llist.h"
|
||||
#include "ultima/nuvie/core/egg_manager.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/actors/u6_work_types.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h" //needed for silver serpent exception
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* ALWAYS means the time is unset, is unknown, or day and night are both set */
|
||||
typedef enum {
|
||||
EGG_HATCH_ALWAYS, EGG_HATCH_DAY, EGG_HATCH_NIGHT
|
||||
} egg_hatch_time;
|
||||
|
||||
static const int EGG_DAY_HOUR = 06; /* first hour of the day */
|
||||
static const int EGG_NIGHT_HOUR = 19; /* first hour of night */
|
||||
|
||||
static inline egg_hatch_time get_egg_hatch_time(uint8 EQ) {
|
||||
return (EQ < 10) ? EGG_HATCH_ALWAYS
|
||||
: (EQ < 20) ? EGG_HATCH_DAY
|
||||
: (EQ < 30) ? EGG_HATCH_NIGHT : EGG_HATCH_ALWAYS;
|
||||
}
|
||||
|
||||
EggManager::EggManager(nuvie_game_t type)
|
||||
: gametype(type), actor_manager(nullptr), obj_manager(nullptr),
|
||||
not_spawning_actors(false) {
|
||||
}
|
||||
|
||||
EggManager::~EggManager() {
|
||||
|
||||
}
|
||||
|
||||
void EggManager::clean(bool keep_obj) {
|
||||
Std::list<Egg *>::iterator egg_iter;
|
||||
|
||||
for (egg_iter = egg_list.begin(); egg_iter != egg_list.end();) {
|
||||
//egg = *egg_iter;
|
||||
|
||||
// eggs are always on the map now.
|
||||
// if(keep_obj == false)
|
||||
// delete_obj(egg->obj);
|
||||
|
||||
delete *egg_iter;
|
||||
egg_iter = egg_list.erase(egg_iter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EggManager::add_egg(Obj *egg_obj) {
|
||||
Egg *egg;
|
||||
|
||||
if (egg_obj == nullptr)
|
||||
return;
|
||||
|
||||
egg = new Egg();
|
||||
egg->obj = egg_obj;
|
||||
|
||||
egg_list.push_back(egg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void EggManager::remove_egg(Obj *egg_obj, bool keep_obj) {
|
||||
Std::list<Egg *>::iterator egg_iter;
|
||||
|
||||
for (egg_iter = egg_list.begin(); egg_iter != egg_list.end(); egg_iter++) {
|
||||
if ((*egg_iter)->obj == egg_obj) {
|
||||
//if(keep_obj == false) eggs always on map now.
|
||||
|
||||
//obj_manager->unlink_from_engine((*egg_iter)->obj);
|
||||
//delete_obj((*egg_iter)->obj);
|
||||
|
||||
delete *egg_iter;
|
||||
egg_list.erase(egg_iter);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EggManager::set_egg_visibility(bool show_eggs) {
|
||||
for (Egg *egg : egg_list)
|
||||
egg->obj->set_invisible(!show_eggs);
|
||||
}
|
||||
|
||||
void EggManager::spawn_eggs(uint16 x, uint16 y, uint8 z, bool teleport) {
|
||||
for (Egg *egg : egg_list) {
|
||||
uint8 quality = egg->obj->quality;
|
||||
sint16 dist_x = abs((sint16)egg->obj->x - x);
|
||||
sint16 dist_y = abs((sint16)egg->obj->y - y);
|
||||
|
||||
//Deactivate eggs that are more than 20 tiles from player.
|
||||
if ((egg->obj->status & OBJ_STATUS_EGG_ACTIVE) && (egg->obj->z != z || (dist_x >= 20 || dist_y >= 20))) {
|
||||
egg->obj->status &= (0xff ^ OBJ_STATUS_EGG_ACTIVE);
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Reactivate egg at (%x,%x,%d)\n", egg->obj->x, egg->obj->y, egg->obj->z);
|
||||
}
|
||||
|
||||
if (dist_x < 20 && dist_y < 20 && egg->obj->z == z
|
||||
&& (dist_x > 8 || dist_y > 8 || !Game::get_game()->is_orig_style() || teleport)) {
|
||||
|
||||
if ((egg->obj->status & OBJ_STATUS_EGG_ACTIVE) == 0) {
|
||||
egg->obj->status |= OBJ_STATUS_EGG_ACTIVE;
|
||||
|
||||
uint8 hatch_probability = (NUVIE_RAND() % 100) + 1;
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Checking Egg (%x,%x,%x). Rand: %d Probability: %d%%", egg->obj->x, egg->obj->y, egg->obj->z, hatch_probability, egg->obj->qty);
|
||||
|
||||
DEBUG(1, LEVEL_DEBUGGING, " Align: %s", get_actor_alignment_str(static_cast<ActorAlignment>(quality % 10)));
|
||||
|
||||
if (quality < 10) DEBUG(1, LEVEL_DEBUGGING, " (always)"); // 0-9
|
||||
else if (quality < 20) DEBUG(1, LEVEL_DEBUGGING, " (day)"); // 10-19
|
||||
else if (quality < 30) DEBUG(1, LEVEL_DEBUGGING, " (night)"); // 20-29
|
||||
else if (quality < 40) DEBUG(1, LEVEL_DEBUGGING, " (day+night)"); // 30-39
|
||||
DEBUG(1, LEVEL_DEBUGGING, "\n");
|
||||
spawn_egg(egg->obj, hatch_probability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool EggManager::spawn_egg(Obj *egg, uint8 hatch_probability) {
|
||||
U6Link *link;
|
||||
uint16 i;
|
||||
Obj *obj, *spawned_obj;
|
||||
uint16 qty;
|
||||
uint8 hour = Game::get_game()->get_clock()->get_hour();
|
||||
ActorAlignment alignment = static_cast<ActorAlignment>(egg->quality % 10);
|
||||
|
||||
// check time that the egg will hach
|
||||
egg_hatch_time period = get_egg_hatch_time(egg->quality);
|
||||
if (period == EGG_HATCH_ALWAYS
|
||||
|| (period == EGG_HATCH_DAY && hour >= EGG_DAY_HOUR && hour < EGG_NIGHT_HOUR)
|
||||
|| (period == EGG_HATCH_NIGHT && !(hour >= EGG_DAY_HOUR && hour < EGG_NIGHT_HOUR))) {
|
||||
if (egg->container == nullptr) {
|
||||
DEBUG(1, LEVEL_WARNING, " egg at (%x,%x,%x) does not contain any embryos!", egg->x, egg->y, egg->z);
|
||||
}
|
||||
// check random probability that the egg will hatch
|
||||
if ((egg->qty == 100 || hatch_probability <= egg->qty) && egg->container) { // Hatch the egg.
|
||||
assert(egg->container);
|
||||
for (link = egg->container->start(); link != nullptr; link = link->next) {
|
||||
obj = (Obj *)link->data;
|
||||
qty = obj->qty;
|
||||
|
||||
if (gametype == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_SILVER_SERPENT) //U6 silver serpents only hatch once per egg.
|
||||
qty = 1;
|
||||
else if (egg->qty != 100) // egg qty 100: do not randomize spawn count
|
||||
qty = (NUVIE_RAND() % qty) + 1; // try to spawn 1 to qty entities
|
||||
|
||||
for (i = 0; i < qty; i++) {
|
||||
if ((gametype == NUVIE_GAME_U6 && (obj->obj_n >= OBJ_U6_GIANT_RAT || obj->obj_n == OBJ_U6_CHEST))
|
||||
|| obj->quality != 0) { /* spawn temp actor we know it's an actor if it has a non-zero worktype. */
|
||||
if ((not_spawning_actors && Game::get_game()->are_cheats_enabled())
|
||||
|| Game::get_game()->is_armageddon())
|
||||
break;
|
||||
// group new actors randomly if egg space already occupied
|
||||
Actor *prev_actor = actor_manager->get_actor(egg->x, egg->y, egg->z);
|
||||
Actor *new_actor = nullptr;
|
||||
MapCoord actor_loc = MapCoord(egg->x, egg->y, egg->z);
|
||||
if (prev_actor) {
|
||||
if (prev_actor->get_obj_n() != obj->obj_n
|
||||
|| !actor_manager->toss_actor_get_location(egg->x, egg->y, egg->z, 3, 2, &actor_loc)
|
||||
|| !actor_manager->toss_actor_get_location(egg->x, egg->y, egg->z, 2, 3, &actor_loc))
|
||||
actor_manager->toss_actor_get_location(egg->x, egg->y, egg->z, 4, 4, &actor_loc);
|
||||
}
|
||||
uint8 worktype = get_worktype(obj);
|
||||
actor_manager->create_temp_actor(obj->obj_n, obj->status, actor_loc.x, actor_loc.y, actor_loc.z, alignment, worktype, &new_actor);
|
||||
/*
|
||||
// try to group actors of the same type first (FIXME: maybe this should use alignment/quality)
|
||||
if(prev_actor->get_obj_n() != new_actor->get_obj_n() || !actor_manager->toss_actor(new_actor, 3, 2) || !actor_manager->toss_actor(new_actor, 2, 3))
|
||||
actor_manager->toss_actor(new_actor, 4, 4);
|
||||
*/
|
||||
} else {
|
||||
/* spawn temp object */
|
||||
spawned_obj = new Obj();
|
||||
spawned_obj->obj_n = obj->obj_n;
|
||||
//spawned_obj->x = egg->x+i; // line objects up in a row
|
||||
spawned_obj->x = egg->x; // regeants all grow at the same location
|
||||
spawned_obj->y = egg->y;
|
||||
spawned_obj->z = egg->z;
|
||||
spawned_obj->qty = 1; // (it already spawns qty objects with the loop)
|
||||
spawned_obj->status |= OBJ_STATUS_TEMPORARY | OBJ_STATUS_OK_TO_TAKE;
|
||||
|
||||
obj_manager->add_obj(spawned_obj, true); // addOnTop
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 EggManager::get_worktype(const Obj *embryo) {
|
||||
if (gametype == NUVIE_GAME_U6
|
||||
&& (embryo->obj_n == OBJ_U6_WINGED_GARGOYLE || embryo->obj_n == OBJ_U6_GARGOYLE)
|
||||
&& (Game::get_game()->get_party()->has_obj(OBJ_U6_AMULET_OF_SUBMISSION, 0, false)
|
||||
|| Game::get_game()->get_party()->contains_actor(164))) { // Beh lem
|
||||
return WORKTYPE_U6_ANIMAL_WANDER;
|
||||
}
|
||||
|
||||
return embryo->quality;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
90
engines/ultima/nuvie/core/egg_manager.h
Normal file
90
engines/ultima/nuvie/core/egg_manager.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_EGG_MANAGER_H
|
||||
#define NUVIE_CORE_EGG_MANAGER_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
struct Egg {
|
||||
bool seen_egg;
|
||||
Obj *obj;
|
||||
Egg() {
|
||||
seen_egg = false;
|
||||
obj = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
class Configuration;
|
||||
class ActorManager;
|
||||
class Actor;
|
||||
class Map;
|
||||
|
||||
class EggManager {
|
||||
ActorManager *actor_manager;
|
||||
ObjManager *obj_manager;
|
||||
nuvie_game_t gametype; // what game is being played?
|
||||
|
||||
Std::list<Egg *> egg_list;
|
||||
|
||||
public:
|
||||
|
||||
EggManager(nuvie_game_t type);
|
||||
~EggManager();
|
||||
|
||||
void set_actor_manager(ActorManager *am) {
|
||||
actor_manager = am;
|
||||
}
|
||||
void set_obj_manager(ObjManager *om) {
|
||||
obj_manager = om;
|
||||
}
|
||||
|
||||
void clean(bool keep_obj = true);
|
||||
|
||||
void add_egg(Obj *egg);
|
||||
void remove_egg(Obj *egg, bool keep_egg = true);
|
||||
void set_egg_visibility(bool show_eggs);
|
||||
bool spawn_egg(Obj *egg, uint8 hatch_probability);
|
||||
void spawn_eggs(uint16 x, uint16 y, uint8 z, bool teleport = false);
|
||||
Std::list<Egg *> *get_egg_list() {
|
||||
return &egg_list;
|
||||
};
|
||||
bool is_spawning_actors() const {
|
||||
return !not_spawning_actors;
|
||||
}
|
||||
void set_spawning_actors(bool spawning) {
|
||||
not_spawning_actors = !spawning;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint8 get_worktype(const Obj *embryo);
|
||||
bool not_spawning_actors;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
3862
engines/ultima/nuvie/core/events.cpp
Normal file
3862
engines/ultima/nuvie/core/events.cpp
Normal file
File diff suppressed because it is too large
Load Diff
482
engines/ultima/nuvie/core/events.h
Normal file
482
engines/ultima/nuvie/core/events.h
Normal file
@@ -0,0 +1,482 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_EVENT_H
|
||||
#define NUVIE_CORE_EVENT_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
#include "ultima/nuvie/keybinding/keys_enum.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Actor;
|
||||
class CallbackTarget;
|
||||
class Configuration;
|
||||
class Converse;
|
||||
class Book;
|
||||
class Game;
|
||||
class TimeQueue;
|
||||
class MapWindow;
|
||||
class MsgScroll;
|
||||
class GameClock;
|
||||
class Player;
|
||||
class ViewManager;
|
||||
class UseCode;
|
||||
class GUI;
|
||||
class GUI_Dialog;
|
||||
class Magic;
|
||||
class KeyBinder;
|
||||
class FpsCounter;
|
||||
class ScriptThread;
|
||||
|
||||
#define NUVIE_INTERVAL 50
|
||||
#define PUSH_FROM_PLAYER false
|
||||
#define PUSH_FROM_OBJECT true
|
||||
|
||||
#define BUTTON_MASK(MB) (1 << ((int)(MB) - 1))
|
||||
|
||||
enum EventMode {
|
||||
LOOK_MODE = 0,
|
||||
USE_MODE,
|
||||
GET_MODE,
|
||||
MOVE_MODE,
|
||||
DROP_MODE,
|
||||
TALK_MODE, /* finding an actor to talk to */
|
||||
ATTACK_MODE,
|
||||
PUSH_MODE,
|
||||
REST_MODE, /* modes before this need targets if using the command bar selected action */
|
||||
CAST_MODE,
|
||||
COMBAT_MODE, /* only used to cancel previous actions */
|
||||
SPELL_MODE, //direct spell casting without spell select etc.
|
||||
EQUIP_MODE,
|
||||
WAIT_MODE, /* waiting for something, optionally display prompt when finished */
|
||||
INPUT_MODE,
|
||||
MULTIUSE_MODE,
|
||||
KEYINPUT_MODE,
|
||||
SCRIPT_MODE
|
||||
};
|
||||
|
||||
extern uint32 nuvieGameCounter;
|
||||
|
||||
// type of input that event may collect and send somewhere
|
||||
#define EVENTINPUT_MAPCOORD 0
|
||||
#define EVENTINPUT_KEY 1
|
||||
#define EVENTINPUT_STRING 2
|
||||
#define EVENTINPUT_OBJECT 3
|
||||
#define EVENTINPUT_MAPCOORD_DIR 4
|
||||
#define EVENTINPUT_SPELL_NUM 5
|
||||
|
||||
/**
|
||||
* Joystick actions mapped to dummy unused keycode values
|
||||
*/
|
||||
const Common::KeyCode FIRST_JOY = (Common::KeyCode)400;
|
||||
const Common::KeyCode JOY_UP = FIRST_JOY; // PS d-pad when analog is disabled. left stick when enabled
|
||||
const Common::KeyCode JOY_DOWN = (Common::KeyCode)(FIRST_JOY + 1);
|
||||
const Common::KeyCode JOY_LEFT = (Common::KeyCode)(FIRST_JOY + 2);
|
||||
const Common::KeyCode JOY_RIGHT = (Common::KeyCode)(FIRST_JOY + 3);
|
||||
const Common::KeyCode JOY_RIGHTUP = (Common::KeyCode)(FIRST_JOY + 4);
|
||||
const Common::KeyCode JOY_RIGHTDOWN = (Common::KeyCode)(FIRST_JOY + 5);
|
||||
const Common::KeyCode JOY_LEFTUP = (Common::KeyCode)(FIRST_JOY + 6);
|
||||
const Common::KeyCode JOY_LEFTDOWN = (Common::KeyCode)(FIRST_JOY + 7);
|
||||
const Common::KeyCode JOY_UP2 = (Common::KeyCode)(FIRST_JOY + 8); // PS right stick when analog is enabled
|
||||
const Common::KeyCode JOY_DOWN2 = (Common::KeyCode)(FIRST_JOY + 9);
|
||||
const Common::KeyCode JOY_LEFT2 = (Common::KeyCode)(FIRST_JOY + 10);
|
||||
const Common::KeyCode JOY_RIGHT2 = (Common::KeyCode)(FIRST_JOY + 11);
|
||||
const Common::KeyCode JOY_RIGHTUP2 = (Common::KeyCode)(FIRST_JOY + 12);
|
||||
const Common::KeyCode JOY_RIGHTDOWN2 = (Common::KeyCode)(FIRST_JOY + 13);
|
||||
const Common::KeyCode JOY_LEFTUP2 = (Common::KeyCode)(FIRST_JOY + 14);
|
||||
const Common::KeyCode JOY_LEFTDOWN2 = (Common::KeyCode)(FIRST_JOY + 15);
|
||||
const Common::KeyCode JOY_UP3 = (Common::KeyCode)(FIRST_JOY + 16);
|
||||
const Common::KeyCode JOY_DOWN3 = (Common::KeyCode)(FIRST_JOY + 17);
|
||||
const Common::KeyCode JOY_LEFT3 = (Common::KeyCode)(FIRST_JOY + 18);
|
||||
const Common::KeyCode JOY_RIGHT3 = (Common::KeyCode)(FIRST_JOY + 19);
|
||||
const Common::KeyCode JOY_RIGHTUP3 = (Common::KeyCode)(FIRST_JOY + 20);
|
||||
const Common::KeyCode JOY_RIGHTDOWN3 = (Common::KeyCode)(FIRST_JOY + 21);
|
||||
const Common::KeyCode JOY_LEFTUP3 = (Common::KeyCode)(FIRST_JOY + 22);
|
||||
const Common::KeyCode JOY_LEFTDOWN3 = (Common::KeyCode)(FIRST_JOY + 23);
|
||||
const Common::KeyCode JOY_UP4 = (Common::KeyCode)(FIRST_JOY + 24);
|
||||
const Common::KeyCode JOY_DOWN4 = (Common::KeyCode)(FIRST_JOY + 25);
|
||||
const Common::KeyCode JOY_LEFT4 = (Common::KeyCode)(FIRST_JOY + 26);
|
||||
const Common::KeyCode JOY_RIGHT4 = (Common::KeyCode)(FIRST_JOY + 27);
|
||||
const Common::KeyCode JOY_RIGHTUP4 = (Common::KeyCode)(FIRST_JOY + 28);
|
||||
const Common::KeyCode JOY_RIGHTDOWN4 = (Common::KeyCode)(FIRST_JOY + 29);
|
||||
const Common::KeyCode JOY_LEFTUP4 = (Common::KeyCode)(FIRST_JOY + 30);
|
||||
const Common::KeyCode JOY_LEFTDOWN4 = (Common::KeyCode)(FIRST_JOY + 31);
|
||||
const Common::KeyCode JOY_HAT_UP = (Common::KeyCode)(FIRST_JOY + 32); // PS d-pad when analog is enabled
|
||||
const Common::KeyCode JOY_HAT_DOWN = (Common::KeyCode)(FIRST_JOY + 33);
|
||||
const Common::KeyCode JOY_HAT_LEFT = (Common::KeyCode)(FIRST_JOY + 34);
|
||||
const Common::KeyCode JOY_HAT_RIGHT = (Common::KeyCode)(FIRST_JOY + 35);
|
||||
const Common::KeyCode JOY_HAT_RIGHTUP = (Common::KeyCode)(FIRST_JOY + 36);
|
||||
const Common::KeyCode JOY_HAT_RIGHTDOWN = (Common::KeyCode)(FIRST_JOY + 37);
|
||||
const Common::KeyCode JOY_HAT_LEFTUP = (Common::KeyCode)(FIRST_JOY + 38);
|
||||
const Common::KeyCode JOY_HAT_LEFTDOWN = (Common::KeyCode)(FIRST_JOY + 39);
|
||||
const Common::KeyCode JOY0 = (Common::KeyCode)(FIRST_JOY + 40); // PS triangle
|
||||
const Common::KeyCode JOY1 = (Common::KeyCode)(FIRST_JOY + 41); // PS circle
|
||||
const Common::KeyCode JOY2 = (Common::KeyCode)(FIRST_JOY + 42); // PS x
|
||||
const Common::KeyCode JOY3 = (Common::KeyCode)(FIRST_JOY + 43); // PS square
|
||||
const Common::KeyCode JOY4 = (Common::KeyCode)(FIRST_JOY + 44); // PS L2
|
||||
const Common::KeyCode JOY5 = (Common::KeyCode)(FIRST_JOY + 45); // PS R2
|
||||
const Common::KeyCode JOY6 = (Common::KeyCode)(FIRST_JOY + 46); // PS L1
|
||||
const Common::KeyCode JOY7 = (Common::KeyCode)(FIRST_JOY + 47); // PS R1
|
||||
const Common::KeyCode JOY8 = (Common::KeyCode)(FIRST_JOY + 48); // PS select
|
||||
const Common::KeyCode JOY9 = (Common::KeyCode)(FIRST_JOY + 49); // PS start
|
||||
const Common::KeyCode JOY10 = (Common::KeyCode)(FIRST_JOY + 50); // PS L3 (analog must be enabled)
|
||||
const Common::KeyCode JOY11 = (Common::KeyCode)(FIRST_JOY + 51); // PS R3 (analog must be enabled)
|
||||
const Common::KeyCode JOY12 = (Common::KeyCode)(FIRST_JOY + 52);
|
||||
const Common::KeyCode JOY13 = (Common::KeyCode)(FIRST_JOY + 53);
|
||||
const Common::KeyCode JOY14 = (Common::KeyCode)(FIRST_JOY + 54);
|
||||
const Common::KeyCode JOY15 = (Common::KeyCode)(FIRST_JOY + 55);
|
||||
const Common::KeyCode JOY16 = (Common::KeyCode)(FIRST_JOY + 56);
|
||||
const Common::KeyCode JOY17 = (Common::KeyCode)(FIRST_JOY + 57);
|
||||
const Common::KeyCode JOY18 = (Common::KeyCode)(FIRST_JOY + 58);
|
||||
const Common::KeyCode JOY19 = (Common::KeyCode)(FIRST_JOY + 59);
|
||||
|
||||
|
||||
struct EventInput_s {
|
||||
uint8 type; // 0=loc,1=key,2=str,3=obj,4=actor
|
||||
// union
|
||||
// {
|
||||
Common::KeyCode key; // last key entered, if capturing input
|
||||
ActionKeyType action_key_type; // last ActionKeyType entered if capturing input
|
||||
MapCoord *loc; // target location, or direction if relative ???
|
||||
Std::string *str; // ???
|
||||
// };
|
||||
void set_loc(const MapCoord &c);
|
||||
EventInput_s() : loc(0), str(0), obj(0), actor(0), get_direction(false), get_text(false),
|
||||
target_init(0), select_from_inventory(false), select_range(0), key(Common::KEYCODE_INVALID),
|
||||
action_key_type(ActionKeyType::CANCEL_ACTION_KEY), spell_num(0), type(0) {
|
||||
}
|
||||
~EventInput_s();
|
||||
|
||||
Obj *obj; // top object at loc (or object from inventory)
|
||||
Actor *actor; // actor at loc
|
||||
bool get_direction; // if true, entering directions selects a target
|
||||
bool get_text; // if true, the MsgScroll is polled for text input
|
||||
MapCoord *target_init; // where MapWindow cursor is centered when targeting
|
||||
bool select_from_inventory; // if true, objects from inventory will be selected (and not from the map)
|
||||
uint8 select_range; // limits movement of MapWindow cursor from center
|
||||
sint16 spell_num;
|
||||
};
|
||||
typedef struct EventInput_s EventInput;
|
||||
|
||||
class Events : public CallBack {
|
||||
friend class Magic; // FIXME
|
||||
private:
|
||||
const Configuration *config;
|
||||
GUI *gui;
|
||||
Game *game;
|
||||
ObjManager *obj_manager;
|
||||
MapWindow *map_window;
|
||||
MsgScroll *scroll;
|
||||
GameClock *clock;
|
||||
Player *player;
|
||||
Converse *converse;
|
||||
ViewManager *view_manager;
|
||||
UseCode *usecode;
|
||||
Magic *magic;
|
||||
KeyBinder *keybinder;
|
||||
GUI_Dialog *gamemenu_dialog;
|
||||
GUI_Dialog *assetviewer_dialog;
|
||||
|
||||
Common::Event event;
|
||||
EventMode mode, last_mode;
|
||||
EventInput input; // collected/received input (of any type)
|
||||
// Std::vector<EventMode> mode_stack; // current mode is at the end of the list
|
||||
int ts; //timestamp for TimeLeft() method.
|
||||
int altCodeVal;
|
||||
uint16 active_alt_code; // alt-code that needs more input
|
||||
uint8 alt_code_input_num; // alt-code can get multiple inputs
|
||||
|
||||
TimeQueue *time_queue, *game_time_queue;
|
||||
Obj *drop_obj;
|
||||
uint16 drop_qty;
|
||||
sint32 drop_x, drop_y; // only to allow pre-targeting from MapWindow, feel free to ignore this
|
||||
uint8 rest_time; // How many hours?
|
||||
uint8 rest_guard; // Who will guard?
|
||||
Obj *push_obj;
|
||||
Actor *push_actor;
|
||||
|
||||
bool drop_from_key;
|
||||
bool showingDialog;
|
||||
bool showingQuitDialog;
|
||||
bool ignore_timeleft; // do not wait for NUVIE_INTERVAL
|
||||
bool move_in_inventory;
|
||||
bool in_control_cheat;
|
||||
bool looking_at_spellbook;
|
||||
bool direction_selects_target;
|
||||
bool _keymapperStateBeforeKEYINPUT;
|
||||
|
||||
uint32 fps_timestamp;
|
||||
uint16 fps_counter;
|
||||
FpsCounter *fps_counter_widget;
|
||||
ScriptThread *scriptThread;
|
||||
|
||||
Common::Point _mousePos;
|
||||
uint8 _buttonsDown;
|
||||
|
||||
static Events *g_events;
|
||||
protected:
|
||||
inline uint32 TimeLeft();
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data = nullptr) override;
|
||||
bool handleSDL_KEYDOWN(const Common::Event *event);
|
||||
const char *print_mode(EventMode mode);
|
||||
void try_next_attack();
|
||||
|
||||
public:
|
||||
enum MouseButton {
|
||||
BUTTON_NONE = 0,
|
||||
BUTTON_LEFT = 1,
|
||||
BUTTON_RIGHT = 2,
|
||||
BUTTON_MIDDLE = 3,
|
||||
MOUSE_LAST
|
||||
};
|
||||
|
||||
Events(const Configuration *cfg);
|
||||
~Events() override;
|
||||
|
||||
void clear();
|
||||
|
||||
bool init(ObjManager *om, MapWindow *mw, MsgScroll *ms, Player *p, Magic *mg,
|
||||
GameClock *gc, ViewManager *vm, UseCode *uc, GUI *g, KeyBinder *kb);
|
||||
GUI_Dialog *get_gamemenu_dialog() {
|
||||
return gamemenu_dialog;
|
||||
}
|
||||
TimeQueue *get_time_queue() {
|
||||
return time_queue;
|
||||
}
|
||||
TimeQueue *get_game_time_queue() {
|
||||
return game_time_queue;
|
||||
}
|
||||
EventMode get_mode() const {
|
||||
return mode;
|
||||
}
|
||||
EventMode get_last_mode() const {
|
||||
return last_mode;
|
||||
}
|
||||
void set_mode(EventMode new_mode);
|
||||
|
||||
bool is_direction_selecting_targets() const {
|
||||
return direction_selects_target;
|
||||
}
|
||||
void set_direction_selects_target(bool val) {
|
||||
direction_selects_target = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse position
|
||||
*/
|
||||
Common::Point getMousePos() const {
|
||||
return _mousePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mouse buttons states
|
||||
*/
|
||||
byte getButtonState() const {
|
||||
return _buttonsDown;
|
||||
}
|
||||
|
||||
bool using_pickpocket_cheat;
|
||||
bool cursor_mode;
|
||||
void update_timers();
|
||||
bool update();
|
||||
static MouseButton whichButton(Common::EventType type);
|
||||
bool pollEvent(Common::Event &event);
|
||||
bool handleEvent(const Common::Event *event);
|
||||
void request_input(CallBack *caller, void *user_data = nullptr);
|
||||
void target_spell();
|
||||
void close_spellbook();
|
||||
// Prompt for input.
|
||||
// obsolete:
|
||||
// void useselect_mode(Obj *src, const char *prompt = nullptr); // deprecated
|
||||
// void freeselect_mode(Obj *src, const char *prompt = nullptr); // deprecated
|
||||
void get_scroll_input(const char *allowed = nullptr, bool can_escape = true, bool using_target_cursor = false, bool set_numbers_only_to_true = true);
|
||||
void get_inventory_obj(Actor *actor, bool getting_target = true);
|
||||
void get_spell_num(Actor *caster, Obj *spell_container);
|
||||
// void get_amount();
|
||||
void get_direction(const char *prompt);
|
||||
void get_direction(const MapCoord &from, const char *prompt);
|
||||
void get_target(const char *prompt);
|
||||
void get_target(const MapCoord &init, const char *prompt);
|
||||
// void get_obj_from_inventory(Actor *actor, const char *prompt);
|
||||
void display_portrait(Actor *actor, const char *name = nullptr);
|
||||
// Start a new action, setting a new mode and prompting for input.
|
||||
bool newAction(EventMode new_mode);
|
||||
// void doAction(sint16 rel_x = 0, sint16 rel_y = 0);
|
||||
// void doAction(Obj *obj);
|
||||
void doAction();
|
||||
void cancelAction();
|
||||
void endAction(bool prompt = false);
|
||||
// Send input back to Events, performing an action for the current mode.
|
||||
bool select_obj(Obj *obj, Actor *actor = nullptr);
|
||||
bool select_view_obj(Obj *obj, Actor *actor);
|
||||
bool select_actor(Actor *actor);
|
||||
bool select_direction(sint16 rel_x, sint16 rel_y);
|
||||
bool select_target(uint16 x, uint16 y, uint8 z = 0);
|
||||
bool select_party_member(uint8 num);
|
||||
bool select_spell_num(sint16 spell_num);
|
||||
// bool select_obj(Obj *obj = nullptr, Actor *actor = nullptr);
|
||||
// bool select_obj(sint16 rel_x, sint16 rel_y);
|
||||
// There is no "select_text", as Events polls MsgScroll for new input.
|
||||
// Similarly, a "select_key" is unnecessary. The following method
|
||||
// starts sending all keyboard input to 'caller'. (with the CB_DATA_READY message)
|
||||
void key_redirect(CallBack *caller, void *user_data);
|
||||
void cancel_key_redirect();
|
||||
|
||||
/* These will be replaced in the future with an InputAction class. */
|
||||
bool move(sint16 rel_x, sint16 rel_y);
|
||||
|
||||
bool use_start();
|
||||
bool use(sint16 rel_x, sint16 rel_y);
|
||||
bool use(const MapCoord &coord);
|
||||
bool use(Obj *obj);
|
||||
bool use(Actor *actor, uint16 x, uint16 y);
|
||||
|
||||
bool get_start();
|
||||
bool get(const MapCoord &coord);
|
||||
bool get(sint16 rel_x, sint16 rel_y);
|
||||
bool perform_get(Obj *obj, Obj *container_obj = nullptr, Actor *actor = nullptr);
|
||||
|
||||
bool look_start();
|
||||
bool lookAtCursor(bool delayed = false, uint16 x = 0, uint16 y = 0, uint8 z = 0, Obj *obj = nullptr, Actor *actor = nullptr);
|
||||
bool look(Obj *obj);
|
||||
bool look(Actor *actor);
|
||||
bool search(Obj *obj);
|
||||
|
||||
bool talk_start();
|
||||
bool talk_cursor();
|
||||
bool talk(Actor *actor);
|
||||
bool talk(Obj *obj);
|
||||
bool perform_talk(Actor *actor);
|
||||
|
||||
bool attack();
|
||||
|
||||
bool push_start();
|
||||
bool pushFrom(Obj *obj);
|
||||
bool pushFrom(sint16 rel_x, sint16 rel_y);
|
||||
bool pushFrom(const MapCoord &target);
|
||||
bool pushTo(Obj *obj, Actor *actor);
|
||||
bool pushTo(sint16 rel_x, sint16 rel_y, bool push_from = PUSH_FROM_PLAYER);
|
||||
|
||||
void solo_mode(uint32 actor_num);
|
||||
bool party_mode();
|
||||
bool toggle_combat();
|
||||
|
||||
bool ready(Obj *obj, Actor *actor = nullptr);
|
||||
bool unready(Obj *obj);
|
||||
|
||||
bool drop_start();
|
||||
bool drop_select(Obj *obj, uint16 qty = 0);
|
||||
bool drop_count(uint16 qty);
|
||||
bool perform_drop();
|
||||
void set_drop_from_key(bool closing_gumps) {
|
||||
drop_from_key = closing_gumps;
|
||||
}
|
||||
bool drop(Obj *obj, uint16 qty, uint16 x, uint16 y);
|
||||
bool drop(uint16 x, uint16 y) {
|
||||
return (drop(drop_obj, drop_qty, x, y));
|
||||
}
|
||||
void set_drop_target(uint16 x, uint16 y) {
|
||||
drop_x = sint32(x);
|
||||
drop_y = sint32(y);
|
||||
}
|
||||
bool can_move_obj_between_actors(Obj *obj, Actor *src_actor, Actor *target_actor, bool display_name = false);
|
||||
void display_not_aboard_vehicle(bool show_prompt = true);
|
||||
void display_move_text(Actor *target_actor, Obj *obj);
|
||||
bool can_get_to_actor(const Actor *actor, uint16 x, uint16 y);
|
||||
bool using_control_cheat() const {
|
||||
return in_control_cheat;
|
||||
}
|
||||
void set_control_cheat(bool control_cheat) {
|
||||
in_control_cheat = control_cheat;
|
||||
}
|
||||
bool is_looking_at_spellbook() const {
|
||||
return looking_at_spellbook;
|
||||
}
|
||||
void set_looking_at_spellbook(bool looking) {
|
||||
looking_at_spellbook = looking;
|
||||
}
|
||||
|
||||
bool rest();
|
||||
bool rest_input(uint16 input);
|
||||
|
||||
void cast_spell_directly(uint8 spell_num);
|
||||
bool can_target_icon(); // Target the actor or container tile in inventory and party view
|
||||
|
||||
// these are both for mouse-using convenience
|
||||
void walk_to_mouse_cursor(uint32 mx, uint32 my);
|
||||
void multiuse(uint16 wx, uint16 wy);
|
||||
|
||||
void alt_code(int c);
|
||||
void alt_code_input(const char *in);
|
||||
void clear_alt_code() { altCodeVal = 0; }
|
||||
|
||||
void toggleAltCodeMode(bool enable);
|
||||
void appendAltCode(int code);
|
||||
|
||||
bool alt_code_teleport(const char *location_string);
|
||||
void alt_code_infostring();
|
||||
void alt_code_teleport_menu(uint32 selection);
|
||||
bool alt_code_teleport_to_person(uint32 npc);
|
||||
|
||||
void wait();
|
||||
void set_ignore_timeleft(bool newsetting) {
|
||||
ignore_timeleft = newsetting;
|
||||
}
|
||||
EventInput *get_input() {
|
||||
return &input;
|
||||
}
|
||||
// These cursor methods are use to make sure Events knows where the cursor is
|
||||
// when objects are selected with ENTER. (since MapWindow and InventoryView
|
||||
// may each independently show/hide their own cursors)
|
||||
void moveCursorToMapWindow(bool ToggleCursor = false);
|
||||
void moveCursorToInventory();
|
||||
|
||||
void toggleFpsDisplay();
|
||||
void close_gumps();
|
||||
bool do_not_show_target_cursor;
|
||||
bool dont_show_target_cursor() const;
|
||||
bool input_really_needs_directon() const;
|
||||
void quitDialog();
|
||||
void gameMenuDialog();
|
||||
void assetViewer();
|
||||
bool actor_exists(const Actor *a) const;
|
||||
|
||||
/* FIXME: Some of the above (action) functions can be removed from public, so
|
||||
that we don't need to check for WAIT mode in all of them. */
|
||||
|
||||
/**
|
||||
* Gets a reference to the events manager
|
||||
*/
|
||||
static Events *get() { return g_events; }
|
||||
};
|
||||
|
||||
extern bool shouldQuit();
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
692
engines/ultima/nuvie/core/game.cpp
Normal file
692
engines/ultima/nuvie/core/game.cpp
Normal file
@@ -0,0 +1,692 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
#include "ultima/nuvie/gui/widgets/console.h"
|
||||
#include "ultima/nuvie/screen/dither.h"
|
||||
#include "ultima/nuvie/sound/sound_manager.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/script/script.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/screen/game_palette.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/core/egg_manager.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/core/player.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/core/converse.h"
|
||||
#include "ultima/nuvie/gui/widgets/converse_gump.h"
|
||||
#include "ultima/nuvie/gui/widgets/converse_gump_wou.h"
|
||||
#include "ultima/nuvie/fonts/font_manager.h"
|
||||
#include "ultima/nuvie/views/view_manager.h"
|
||||
#include "ultima/nuvie/core/effect_manager.h"
|
||||
|
||||
#include "ultima/nuvie/core/magic.h"
|
||||
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
||||
#include "ultima/nuvie/gui/widgets/msg_scroll_new_ui.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/core/events.h"
|
||||
#include "ultima/nuvie/portraits/portrait.h"
|
||||
#include "ultima/nuvie/gui/widgets/background.h"
|
||||
#include "ultima/nuvie/gui/widgets/command_bar.h"
|
||||
#include "ultima/nuvie/gui/widgets/command_bar_new_ui.h"
|
||||
#include "ultima/nuvie/views/party_view.h"
|
||||
#include "ultima/nuvie/views/actor_view.h"
|
||||
#include "ultima/nuvie/usecode/usecode.h"
|
||||
#include "ultima/nuvie/usecode/u6_usecode.h"
|
||||
#include "ultima/nuvie/core/cursor.h"
|
||||
#include "ultima/nuvie/core/weather.h"
|
||||
#include "ultima/nuvie/core/book.h"
|
||||
#include "ultima/nuvie/keybinding/keys.h"
|
||||
#include "ultima/nuvie/files/utils.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/nuvie.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Game *Game::game = nullptr;
|
||||
|
||||
Game::Game(Configuration *cfg, Events *evt, Screen *scr, GUI *g, nuvie_game_t type, SoundManager *sm)
|
||||
: config(cfg), event(evt), gui(g), screen(scr), game_type(type),
|
||||
sound_manager(sm), script(nullptr), background(nullptr),
|
||||
cursor(nullptr), dither(nullptr), tile_manager(nullptr),
|
||||
obj_manager(nullptr), palette(nullptr), font_manager(nullptr),
|
||||
scroll(nullptr), game_map(nullptr), map_window(nullptr),
|
||||
actor_manager(nullptr), player(nullptr), converse(nullptr),
|
||||
conv_gump(nullptr), command_bar(nullptr), new_command_bar(nullptr),
|
||||
_clock(nullptr), party(nullptr), portrait(nullptr),
|
||||
view_manager(nullptr), egg_manager(nullptr), usecode(nullptr),
|
||||
effect_manager(nullptr), weather(nullptr), magic(nullptr),
|
||||
book(nullptr), keybinder(nullptr), _playing(true),
|
||||
converse_gump_type(CONVERSE_GUMP_DEFAULT),
|
||||
pause_flags(PAUSE_UNPAUSED), pause_user_count(0),
|
||||
ignore_event_delay(0), unlimited_casting(false),
|
||||
god_mode_enabled(false), armageddon(false), ethereal(false),
|
||||
free_balloon_movement(false), converse_gump_width(0),
|
||||
min_converse_gump_width(0), force_solid_converse_bg(false) {
|
||||
game = this;
|
||||
|
||||
config->value("config/cheats/enabled", cheats_enabled, false);
|
||||
config->value("config/cheats/enable_hackmove", is_using_hackmove, false);
|
||||
config->value("config/input/enabled_dragging", dragging_enabled, true);
|
||||
config->value("config/general/use_text_gumps", using_text_gumps, false);
|
||||
config->value(config_get_game_key(config) + "/roof_mode", roof_mode, false);
|
||||
config->value("config/input/doubleclick_opens_containers", open_containers, false);
|
||||
int value;
|
||||
uint16 screen_width = gui->get_width();
|
||||
uint16 screen_height = gui->get_height();
|
||||
|
||||
init_game_style();
|
||||
if (is_orig_style()) {
|
||||
game_width = 320;
|
||||
game_height = 200;
|
||||
} else {
|
||||
config->value("config/video/game_width", value, 320);
|
||||
game_width = (value < screen_width) ? value : screen_width;
|
||||
config->value("config/video/game_height", value, 200);
|
||||
game_height = (value < screen_height) ? value : screen_height;
|
||||
if (game_width < 320)
|
||||
game_width = 320;
|
||||
if (game_height < 200)
|
||||
game_height = 200;
|
||||
if (is_original_plus_full_map() && screen_height <= 200) // not tall enough to show extra map space
|
||||
game_style = NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP;
|
||||
}
|
||||
|
||||
string game_position;
|
||||
config->value("config/video/game_position", game_position, "center");
|
||||
|
||||
if (game_position == "upper_left")
|
||||
game_x_offset = game_y_offset = 0;
|
||||
else { // center
|
||||
game_x_offset = (screen_width - game_width) / 2;
|
||||
game_y_offset = (screen_height - game_height) / 2;
|
||||
}
|
||||
|
||||
effect_manager = new EffectManager;
|
||||
|
||||
init_cursor();
|
||||
|
||||
keybinder = new KeyBinder(config);
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
// note: don't delete objects that are added to the GUI object via
|
||||
// AddWidget()!
|
||||
if (dither) delete dither;
|
||||
if (tile_manager) delete tile_manager;
|
||||
if (obj_manager) delete obj_manager;
|
||||
if (palette) delete palette;
|
||||
if (font_manager) delete font_manager;
|
||||
//delete scroll;
|
||||
if (game_map) delete game_map;
|
||||
if (actor_manager) delete actor_manager;
|
||||
//delete map_window;
|
||||
// If conversation active, must be deleted before player as it resets
|
||||
// player flags.
|
||||
if (converse) delete converse;
|
||||
if (player) delete player;
|
||||
//delete background;
|
||||
if (_clock) delete _clock;
|
||||
if (party) delete party;
|
||||
if (portrait) delete portrait;
|
||||
if (view_manager) delete view_manager;
|
||||
if (sound_manager) delete sound_manager;
|
||||
if (gui) delete gui;
|
||||
if (usecode) delete usecode;
|
||||
if (effect_manager) delete effect_manager;
|
||||
if (cursor) delete cursor;
|
||||
if (egg_manager) delete egg_manager;
|
||||
if (weather) delete weather;
|
||||
if (magic) delete magic;
|
||||
if (book) delete book;
|
||||
if (keybinder) delete keybinder;
|
||||
}
|
||||
|
||||
bool Game::shouldQuit() const {
|
||||
return !_playing || g_engine->shouldQuit();
|
||||
}
|
||||
|
||||
bool Game::loadGame(Script *s) {
|
||||
dither = new Dither(config);
|
||||
|
||||
script = s;
|
||||
//sound_manager->LoadSongs(nullptr);
|
||||
//sound_manager->LoadObjectSamples(nullptr);
|
||||
|
||||
palette = new GamePalette(screen, config);
|
||||
|
||||
_clock = new GameClock(game_type);
|
||||
|
||||
background = new Background(config);
|
||||
background->init();
|
||||
background->Hide();
|
||||
if (is_original_plus_full_map() == false) // need to render before map window
|
||||
gui->AddWidget(background);
|
||||
|
||||
font_manager = new FontManager(config);
|
||||
font_manager->init(game_type);
|
||||
|
||||
if (!is_new_style()) {
|
||||
scroll = new MsgScroll(config, font_manager->get_font(0));
|
||||
} else {
|
||||
scroll = new MsgScrollNewUI(config, screen);
|
||||
}
|
||||
game_map = new Map(config);
|
||||
|
||||
egg_manager = new EggManager(game_type);
|
||||
|
||||
tile_manager = new TileManager(config);
|
||||
if (tile_manager->loadTiles() == false)
|
||||
return false;
|
||||
|
||||
ConsoleAddInfo("Loading ObjManager()");
|
||||
obj_manager = new ObjManager(config, tile_manager, egg_manager);
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
book = new Book(config);
|
||||
if (book->init() == false)
|
||||
return false;
|
||||
config->value(config_get_game_key(config) + "/free_balloon_movement", free_balloon_movement, false);
|
||||
}
|
||||
|
||||
// Correct usecode class for each game
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
usecode = (UseCode *) new U6UseCode(this, config);
|
||||
break;
|
||||
case NUVIE_GAME_MD :
|
||||
usecode = (UseCode *) new UseCode(this, config);
|
||||
break;
|
||||
case NUVIE_GAME_SE :
|
||||
usecode = (UseCode *) new UseCode(this, config);
|
||||
break;
|
||||
}
|
||||
|
||||
obj_manager->set_usecode(usecode);
|
||||
//obj_manager->loadObjs();
|
||||
|
||||
ConsoleAddInfo("Loading map data.");
|
||||
game_map->loadMap(tile_manager, obj_manager);
|
||||
egg_manager->set_obj_manager(obj_manager);
|
||||
|
||||
ConsoleAddInfo("Loading actor data.");
|
||||
actor_manager = new ActorManager(config, game_map, tile_manager, obj_manager, _clock);
|
||||
|
||||
game_map->set_actor_manager(actor_manager);
|
||||
egg_manager->set_actor_manager(actor_manager);
|
||||
|
||||
map_window = new MapWindow(config, game_map);
|
||||
map_window->init(tile_manager, obj_manager, actor_manager);
|
||||
map_window->Hide();
|
||||
gui->AddWidget(map_window);
|
||||
if (is_original_plus_full_map()) // need to render after map window
|
||||
gui->AddWidget(background);
|
||||
|
||||
weather = new Weather(config, _clock, game_type);
|
||||
|
||||
// if(!is_new_style()) // Everyone always uses original style command bar now.
|
||||
{
|
||||
command_bar = new CommandBar(this);
|
||||
bool using_new_command_bar;
|
||||
config->value("config/input/new_command_bar", using_new_command_bar, false);
|
||||
if (using_new_command_bar) {
|
||||
init_new_command_bar();
|
||||
}
|
||||
}
|
||||
// else
|
||||
// command_bar = new CommandBarNewUI(this);
|
||||
command_bar->Hide();
|
||||
gui->AddWidget(command_bar);
|
||||
|
||||
|
||||
player = new Player(config);
|
||||
party = new Party(config);
|
||||
player->init(obj_manager, actor_manager, map_window, _clock, party);
|
||||
party->init(this, actor_manager);
|
||||
|
||||
portrait = newPortrait(game_type, config);
|
||||
if (portrait->init() == false)
|
||||
return false;
|
||||
|
||||
view_manager = new ViewManager(config);
|
||||
view_manager->init(gui, font_manager->get_font(0), party, player, tile_manager, obj_manager, portrait);
|
||||
scroll->Hide();
|
||||
gui->AddWidget(scroll);
|
||||
|
||||
|
||||
//map_window->set_windowSize(11,11);
|
||||
|
||||
init_converse_gump_settings();
|
||||
init_converse();
|
||||
|
||||
usecode->init(obj_manager, game_map, player, scroll);
|
||||
|
||||
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
magic = new Magic();
|
||||
}
|
||||
|
||||
event->init(obj_manager, map_window, scroll, player, magic, _clock, view_manager, usecode, gui, keybinder);
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
magic->init(event);
|
||||
}
|
||||
|
||||
if (g_engine->journeyOnwards() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ConsoleAddInfo("Polishing Anhk");
|
||||
|
||||
//ConsolePause();
|
||||
ConsoleHide();
|
||||
|
||||
// if(!is_new_style())
|
||||
{
|
||||
if (is_orig_style())
|
||||
command_bar->Show();
|
||||
else {
|
||||
bool show;
|
||||
Std::string show_cb;
|
||||
config->value(config_get_game_key(config) + "/show_orig_style_cb", show_cb, "default");
|
||||
if (show_cb == "default") {
|
||||
if (is_new_style())
|
||||
show = false;
|
||||
else
|
||||
show = true;
|
||||
} else if (show_cb == "no")
|
||||
show = false;
|
||||
else
|
||||
show = true;
|
||||
if (show)
|
||||
command_bar->Show();
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_new_style() || screen->get_width() != get_game_width() || screen->get_height() != get_game_height()) {
|
||||
background->Show();
|
||||
}
|
||||
|
||||
map_window->Show();
|
||||
scroll->Show();
|
||||
view_manager->update();
|
||||
|
||||
if (cursor)
|
||||
cursor->show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Game::init_converse_gump_settings() {
|
||||
if (is_new_style())
|
||||
converse_gump_type = CONVERSE_GUMP_DEFAULT;
|
||||
else {
|
||||
converse_gump_type = get_converse_gump_type_from_config(config);
|
||||
}
|
||||
Std::string width_str;
|
||||
int gump_w = get_game_width();
|
||||
|
||||
if (game_type == NUVIE_GAME_MD)
|
||||
min_converse_gump_width = 298;
|
||||
else if (game_type == NUVIE_GAME_SE)
|
||||
min_converse_gump_width = 301;
|
||||
else // U6
|
||||
min_converse_gump_width = 286;
|
||||
|
||||
config->value(config_get_game_key(config) + "/converse_width", width_str, "default");
|
||||
if (!game->is_orig_style()) {
|
||||
if (width_str == "default") {
|
||||
int map_width = get_game_width();
|
||||
if (is_original_plus())
|
||||
map_width += - background->get_border_width() - 1;
|
||||
if (map_width > min_converse_gump_width * 1.5) // big enough that we probably don't want to take up the whole screen
|
||||
gump_w = min_converse_gump_width;
|
||||
else if (game->is_original_plus() && map_width >= min_converse_gump_width) // big enough to draw without going over the UI
|
||||
gump_w = map_width;
|
||||
} else {
|
||||
config->value(config_get_game_key(config) + "/converse_width", gump_w, gump_w);
|
||||
if (gump_w < min_converse_gump_width)
|
||||
gump_w = min_converse_gump_width;
|
||||
else if (gump_w > get_game_width())
|
||||
gump_w = get_game_width();
|
||||
}
|
||||
}
|
||||
converse_gump_width = (uint16)gump_w;
|
||||
|
||||
if ((is_original_plus_cutoff_map() && get_game_width() - background->get_border_width() < min_converse_gump_width)
|
||||
|| game->is_orig_style())
|
||||
force_solid_converse_bg = true;
|
||||
else
|
||||
force_solid_converse_bg = false;
|
||||
}
|
||||
|
||||
void Game::init_converse() {
|
||||
converse = new Converse();
|
||||
if (using_new_converse_gump()) {
|
||||
conv_gump = new ConverseGump(config, font_manager->get_font(0), screen);
|
||||
conv_gump->Hide();
|
||||
gui->AddWidget(conv_gump);
|
||||
|
||||
converse->init(config, game_type, conv_gump, actor_manager, _clock, player, view_manager, obj_manager);
|
||||
} else if (game_type == NUVIE_GAME_U6 && converse_gump_type == CONVERSE_GUMP_DEFAULT) {
|
||||
converse->init(config, game_type, scroll, actor_manager, _clock, player, view_manager, obj_manager);
|
||||
} else {
|
||||
ConverseGumpWOU *gump = new ConverseGumpWOU(config, font_manager->get_font(0), screen);
|
||||
gump->Hide();
|
||||
gui->AddWidget(gump);
|
||||
converse->init(config, game_type, gump, actor_manager, _clock, player, view_manager, obj_manager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Game::set_converse_gump_type(ConverseGumpType new_type) {
|
||||
if (converse)
|
||||
delete converse;
|
||||
converse_gump_type = new_type;
|
||||
init_converse();
|
||||
}
|
||||
|
||||
bool Game::using_new_converse_gump() {
|
||||
return (is_new_style() || converse_gump_type == CONVERSE_GUMP_U7_STYLE);
|
||||
}
|
||||
|
||||
void Game::delete_new_command_bar() {
|
||||
if (new_command_bar == nullptr)
|
||||
return;
|
||||
new_command_bar->Delete();
|
||||
new_command_bar = nullptr;
|
||||
}
|
||||
|
||||
void Game::init_new_command_bar() {
|
||||
if (new_command_bar != nullptr)
|
||||
return;
|
||||
new_command_bar = new CommandBarNewUI(this);
|
||||
new_command_bar->Hide();
|
||||
gui->AddWidget(new_command_bar);
|
||||
}
|
||||
|
||||
void Game::init_cursor() {
|
||||
if (!cursor)
|
||||
cursor = new Cursor();
|
||||
|
||||
if (cursor->init(config, screen, game_type))
|
||||
g_system->showMouse(false); // won't need the system default
|
||||
else {
|
||||
delete cursor;
|
||||
cursor = nullptr; // no game cursor
|
||||
}
|
||||
}
|
||||
|
||||
void Game::init_game_style() {
|
||||
string game_style_str;
|
||||
config->value("config/video/game_style", game_style_str, "original");
|
||||
if (game_style_str == "new")
|
||||
game_style = NUVIE_STYLE_NEW;
|
||||
else if (game_style_str == "original+")
|
||||
game_style = NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP;
|
||||
else if (game_style_str == "original+_full_map")
|
||||
game_style = NUVIE_STYLE_ORIG_PLUS_FULL_MAP;
|
||||
else
|
||||
game_style = NUVIE_STYLE_ORIG;
|
||||
|
||||
}
|
||||
|
||||
bool Game::doubleclick_opens_containers() {
|
||||
if (open_containers || is_new_style())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Game::using_hackmove() {
|
||||
if (cheats_enabled)
|
||||
return is_using_hackmove;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Game::set_hackmove(bool hackmove) {
|
||||
is_using_hackmove = hackmove;
|
||||
map_window->set_interface();
|
||||
}
|
||||
|
||||
bool Game::set_mouse_pointer(uint8 ptr_num) {
|
||||
return (cursor && cursor->set_pointer(ptr_num));
|
||||
}
|
||||
|
||||
//FIXME pausing inside a script function causes problems with yield/resume logic.
|
||||
void Game::set_pause_flags(GamePauseState state) {
|
||||
pause_flags = state; // set
|
||||
}
|
||||
|
||||
void Game::unpause_all() {
|
||||
// DEBUG(0, LEVEL_DEBUGGING,"Unpause ALL!\n");
|
||||
unpause_user();
|
||||
unpause_anims();
|
||||
unpause_world();
|
||||
}
|
||||
|
||||
void Game::unpause_user() {
|
||||
if (pause_user_count > 0)
|
||||
pause_user_count--;
|
||||
|
||||
if (pause_user_count == 0) {
|
||||
set_pause_flags((GamePauseState)(pause_flags & ~PAUSE_USER));
|
||||
|
||||
|
||||
//if(event->get_mode() == WAIT_MODE)
|
||||
// event->endAction(); // change to MOVE_MODE, hide cursors
|
||||
if (gui->get_block_input())
|
||||
gui->unblock();
|
||||
|
||||
}
|
||||
|
||||
// DEBUG(0, LEVEL_DEBUGGING, "unpause user count=%d!\n", pause_user_count);
|
||||
}
|
||||
|
||||
void Game::unpause_anims() {
|
||||
set_pause_flags((GamePauseState)(pause_flags & ~PAUSE_ANIMS));
|
||||
}
|
||||
|
||||
void Game::unpause_world() {
|
||||
set_pause_flags((GamePauseState)(pause_flags & ~PAUSE_WORLD));
|
||||
|
||||
if (actor_manager->get_update() == false) // ActorMgr is not running
|
||||
game->get_actor_manager()->set_update(true); // resume
|
||||
|
||||
//if(clock->get_active() == false) // start time
|
||||
// clock->set_active(true);
|
||||
}
|
||||
|
||||
void Game::pause_all() {
|
||||
pause_user();
|
||||
pause_anims();
|
||||
pause_world();
|
||||
}
|
||||
|
||||
void Game::pause_user() {
|
||||
set_pause_flags((GamePauseState)(pause_flags | PAUSE_USER));
|
||||
|
||||
if (!gui->get_block_input() && pause_user_count == 0)
|
||||
gui->block();
|
||||
|
||||
pause_user_count++;
|
||||
|
||||
// DEBUG(0, LEVEL_DEBUGGING, "Pause user count=%d!\n", pause_user_count);
|
||||
}
|
||||
|
||||
void Game::pause_anims() {
|
||||
set_pause_flags((GamePauseState)(pause_flags | PAUSE_ANIMS));
|
||||
}
|
||||
|
||||
void Game::pause_world() {
|
||||
set_pause_flags((GamePauseState)(pause_flags | PAUSE_WORLD));
|
||||
|
||||
if (actor_manager->get_update() == true) // ActorMgr is running
|
||||
game->get_actor_manager()->set_update(false); // pause
|
||||
|
||||
//if(clock->get_active() == true) // stop time
|
||||
// clock->set_active(false);
|
||||
}
|
||||
|
||||
|
||||
void Game::dont_wait_for_interval() {
|
||||
if (ignore_event_delay < 255)
|
||||
++ignore_event_delay;
|
||||
event->set_ignore_timeleft(true);
|
||||
}
|
||||
|
||||
|
||||
void Game::wait_for_interval() {
|
||||
if (ignore_event_delay > 0)
|
||||
--ignore_event_delay;
|
||||
if (ignore_event_delay == 0)
|
||||
event->set_ignore_timeleft(false);
|
||||
}
|
||||
|
||||
|
||||
void Game::time_changed() {
|
||||
if (!is_new_style()) {
|
||||
if (game->is_orig_style()) // others constantly update
|
||||
get_command_bar()->update(); // date & wind
|
||||
get_view_manager()->get_party_view()->update(); // sky
|
||||
}
|
||||
get_map_window()->updateAmbience();
|
||||
}
|
||||
|
||||
// FIXME: should this be in ViewManager?
|
||||
void Game::stats_changed() {
|
||||
if (!is_new_style()) {
|
||||
get_view_manager()->get_actor_view()->update();
|
||||
get_view_manager()->get_party_view()->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Game::play() {
|
||||
pause_flags = PAUSE_UNPAUSED;
|
||||
|
||||
//view_manager->set_inventory_mode(1); //FIX
|
||||
|
||||
screen->update();
|
||||
|
||||
//map_window->drawMap();
|
||||
|
||||
map_window->updateBlacking();
|
||||
|
||||
while (!shouldQuit()) {
|
||||
if (cursor) cursor->clear(); // restore cursor area before GUI events
|
||||
|
||||
event->update();
|
||||
if (_clock->get_timer(GAMECLOCK_TIMER_U6_TIME_STOP) == 0) {
|
||||
palette->rotatePalette();
|
||||
tile_manager->update();
|
||||
actor_manager->twitchActors();
|
||||
}
|
||||
actor_manager->moveActors(); // update/move actors for this turn
|
||||
map_window->update();
|
||||
//map_window->drawMap();
|
||||
converse->continue_script();
|
||||
//scroll->updateScroll();
|
||||
effect_manager->update_effects();
|
||||
|
||||
gui->Display();
|
||||
if (cursor) cursor->display();
|
||||
|
||||
screen->performUpdate();
|
||||
sound_manager->update();
|
||||
event->wait();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Game::update_until_converse_finished() {
|
||||
while (converse->running()) {
|
||||
update_once(true, true);
|
||||
update_once_display();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::update_once(bool process_gui_input) {
|
||||
update_once(process_gui_input, false);
|
||||
}
|
||||
|
||||
void Game::update_once(bool process_gui_input, bool run_converse) {
|
||||
if (cursor) cursor->clear(); // restore cursor area before GUI events
|
||||
|
||||
event->update_timers();
|
||||
|
||||
Common::Event evt;
|
||||
while (Events::get()->pollEvent(evt)) {
|
||||
if (process_gui_input)
|
||||
gui->HandleEvent(&evt);
|
||||
}
|
||||
|
||||
if (_clock->get_timer(GAMECLOCK_TIMER_U6_TIME_STOP) == 0) {
|
||||
palette->rotatePalette();
|
||||
tile_manager->update();
|
||||
actor_manager->twitchActors();
|
||||
}
|
||||
map_window->update();
|
||||
if (run_converse) {
|
||||
converse->continue_script();
|
||||
}
|
||||
effect_manager->update_effects();
|
||||
}
|
||||
|
||||
void Game::update_once_display() {
|
||||
gui->Display();
|
||||
if (cursor) cursor->display();
|
||||
|
||||
screen->performUpdate();
|
||||
sound_manager->update();
|
||||
event->wait();
|
||||
}
|
||||
|
||||
/* return the fullpath to the datafile. First look for it in the savegame directory.
|
||||
* Then in the app data directory.
|
||||
*/
|
||||
Common::Path Game::get_data_file_path(const Common::Path &datafile) {
|
||||
Common::Path path("data");
|
||||
path.joinInPlace(datafile);
|
||||
|
||||
if (!file_exists(path)) {
|
||||
path = gui->get_data_dir().joinInPlace(datafile);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
uint getRandom(uint maxVal) {
|
||||
return g_engine->getRandomNumber(maxVal);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
429
engines/ultima/nuvie/core/game.h
Normal file
429
engines/ultima/nuvie/core/game.h
Normal file
@@ -0,0 +1,429 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_GAME_H
|
||||
#define NUVIE_CORE_GAME_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Script;
|
||||
class Screen;
|
||||
class Background;
|
||||
class GamePalette;
|
||||
class FontManager;
|
||||
class Dither;
|
||||
class TileManager;
|
||||
class ObjManager;
|
||||
class ActorManager;
|
||||
class Magic;
|
||||
class Map;
|
||||
class MapWindow;
|
||||
class MsgScroll;
|
||||
class Player;
|
||||
class Party;
|
||||
class Converse;
|
||||
class ConverseGump;
|
||||
class Cursor;
|
||||
class GameClock;
|
||||
class ViewManager;
|
||||
class Portrait;
|
||||
class UseCode;
|
||||
class Events;
|
||||
class GUI;
|
||||
class EffectManager;
|
||||
class SoundManager;
|
||||
class EggManager;
|
||||
class CommandBar;
|
||||
class Weather;
|
||||
class Book;
|
||||
class KeyBinder;
|
||||
|
||||
typedef enum {
|
||||
PAUSE_UNPAUSED = 0x00,
|
||||
PAUSE_USER = 0x01, /* Don't allow user-input */
|
||||
PAUSE_ANIMS = 0x02, /* TileManager & Palette */
|
||||
PAUSE_WORLD = 0x04, /* game time doesn't pass, freeze actors */
|
||||
PAUSE_ALL = 0xFF
|
||||
} GamePauseState;
|
||||
|
||||
class Game {
|
||||
private:
|
||||
nuvie_game_t game_type;
|
||||
uint8 game_style; //new, original, orig_plus_cutoff_map, or orig_plus_full_map
|
||||
static Game *game;
|
||||
Configuration *config;
|
||||
Script *script;
|
||||
Screen *screen;
|
||||
Background *background;
|
||||
GamePalette *palette;
|
||||
Dither *dither;
|
||||
FontManager *font_manager;
|
||||
TileManager *tile_manager;
|
||||
ObjManager *obj_manager;
|
||||
ActorManager *actor_manager;
|
||||
Magic *magic;
|
||||
Map *game_map;
|
||||
MapWindow *map_window;
|
||||
MsgScroll *scroll;
|
||||
Player *player;
|
||||
Party *party;
|
||||
Converse *converse;
|
||||
ConverseGump *conv_gump;
|
||||
CommandBar *command_bar;
|
||||
CommandBar *new_command_bar;
|
||||
|
||||
ViewManager *view_manager;
|
||||
EffectManager *effect_manager;
|
||||
SoundManager *sound_manager;
|
||||
EggManager *egg_manager;
|
||||
|
||||
GameClock *_clock;
|
||||
Portrait *portrait;
|
||||
UseCode *usecode;
|
||||
|
||||
Weather *weather;
|
||||
|
||||
Cursor *cursor;
|
||||
|
||||
Events *event;
|
||||
|
||||
GUI *gui;
|
||||
|
||||
Book *book;
|
||||
KeyBinder *keybinder;
|
||||
|
||||
GamePauseState pause_flags;
|
||||
uint16 game_width;
|
||||
uint16 game_height;
|
||||
uint16 game_x_offset;
|
||||
uint16 game_y_offset;
|
||||
uint16 pause_user_count;
|
||||
uint16 converse_gump_width;
|
||||
uint16 min_converse_gump_width;
|
||||
uint8 ignore_event_delay; // (stack) if non-zero, Events will not periodically wait for NUVIE_INTERVAL
|
||||
bool is_using_hackmove;
|
||||
bool dragging_enabled;
|
||||
bool cheats_enabled;
|
||||
bool unlimited_casting;
|
||||
bool god_mode_enabled;
|
||||
bool armageddon;
|
||||
bool ethereal;
|
||||
bool using_text_gumps;
|
||||
bool open_containers; //doubleclick
|
||||
ConverseGumpType converse_gump_type;
|
||||
bool roof_mode;
|
||||
bool free_balloon_movement;
|
||||
bool force_solid_converse_bg;
|
||||
bool _playing;
|
||||
|
||||
public:
|
||||
Game(Configuration *cfg, Events *evt, Screen *scr, GUI *g, nuvie_game_t type, SoundManager *sm);
|
||||
~Game();
|
||||
|
||||
bool loadGame(Script *s);
|
||||
void init_cursor();
|
||||
void init_game_style();
|
||||
void play();
|
||||
void update_once(bool process_gui_input);
|
||||
|
||||
void update_once_display();
|
||||
void update_until_converse_finished();
|
||||
|
||||
bool isLoaded() const {
|
||||
return script != nullptr;
|
||||
}
|
||||
GamePauseState get_pause_flags() const {
|
||||
return pause_flags;
|
||||
}
|
||||
void set_pause_flags(GamePauseState state);
|
||||
void unpause_all();
|
||||
void unpause_user();
|
||||
void unpause_anims();
|
||||
void unpause_world();
|
||||
void pause_all();
|
||||
void pause_user();
|
||||
void pause_anims();
|
||||
void pause_world();
|
||||
|
||||
bool paused() const {
|
||||
return pause_flags;
|
||||
}
|
||||
bool all_paused() const {
|
||||
return (pause_flags & PAUSE_ALL);
|
||||
}
|
||||
bool user_paused() const {
|
||||
return (pause_flags & PAUSE_USER);
|
||||
}
|
||||
bool anims_paused() const {
|
||||
return (pause_flags & PAUSE_ANIMS);
|
||||
}
|
||||
bool world_paused() const{
|
||||
return (pause_flags & PAUSE_WORLD);
|
||||
}
|
||||
|
||||
void quit() {
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
bool shouldQuit() const;
|
||||
|
||||
bool set_mouse_pointer(uint8 ptr_num);
|
||||
void dont_wait_for_interval();
|
||||
void wait_for_interval();
|
||||
void time_changed();
|
||||
void stats_changed();
|
||||
|
||||
void init_new_command_bar();
|
||||
void delete_new_command_bar();
|
||||
nuvie_game_t get_game_type() const {
|
||||
return game_type;
|
||||
}
|
||||
uint8 get_game_style() const {
|
||||
return game_style;
|
||||
}
|
||||
bool is_original_plus() const {
|
||||
return (game_style == NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP || game_style == NUVIE_STYLE_ORIG_PLUS_FULL_MAP);
|
||||
}
|
||||
bool is_original_plus_cutoff_map() const {
|
||||
return (game_style == NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP);
|
||||
}
|
||||
bool is_original_plus_full_map() const {
|
||||
return (game_style == NUVIE_STYLE_ORIG_PLUS_FULL_MAP);
|
||||
}
|
||||
bool is_new_style() const {
|
||||
return (game_style == NUVIE_STYLE_NEW);
|
||||
}
|
||||
bool is_orig_style() const {
|
||||
return (game_style == NUVIE_STYLE_ORIG);
|
||||
}
|
||||
bool doubleclick_opens_containers();
|
||||
void set_doubleclick_opens_containers(bool val) {
|
||||
open_containers = val;
|
||||
}
|
||||
void set_using_text_gumps(bool val) {
|
||||
using_text_gumps = val;
|
||||
}
|
||||
bool is_using_text_gumps() const {
|
||||
return (using_text_gumps || is_new_style());
|
||||
}
|
||||
bool is_roof_mode() const {
|
||||
return roof_mode;
|
||||
}
|
||||
void set_roof_mode(bool val) {
|
||||
roof_mode = val;
|
||||
}
|
||||
bool using_hackmove();
|
||||
void set_hackmove(bool hackmove);
|
||||
uint8 is_dragging_enabled() {
|
||||
return dragging_enabled;
|
||||
}
|
||||
void set_dragging_enabled(bool drag) {
|
||||
dragging_enabled = drag;
|
||||
}
|
||||
bool is_god_mode_enabled() const {
|
||||
return (god_mode_enabled && cheats_enabled);
|
||||
}
|
||||
bool toggle_god_mode() {
|
||||
return (god_mode_enabled = !god_mode_enabled);
|
||||
}
|
||||
bool are_cheats_enabled() const {
|
||||
return cheats_enabled;
|
||||
}
|
||||
void set_cheats_enabled(bool cheat) {
|
||||
cheats_enabled = cheat;
|
||||
}
|
||||
bool has_unlimited_casting() const {
|
||||
return (unlimited_casting && cheats_enabled);
|
||||
}
|
||||
void set_unlimited_casting(bool unlimited) {
|
||||
unlimited_casting = unlimited;
|
||||
}
|
||||
bool is_armageddon() const {
|
||||
return armageddon;
|
||||
}
|
||||
void set_armageddon(bool val) {
|
||||
armageddon = val;
|
||||
}
|
||||
bool is_ethereal() const {
|
||||
return ethereal;
|
||||
}
|
||||
void set_ethereal(bool val) {
|
||||
ethereal = val;
|
||||
}
|
||||
ConverseGumpType get_converse_gump_type() const {
|
||||
return converse_gump_type;
|
||||
}
|
||||
void set_converse_gump_type(ConverseGumpType new_type);
|
||||
bool using_new_converse_gump();
|
||||
void set_free_balloon_movement(bool val) {
|
||||
free_balloon_movement = val;
|
||||
}
|
||||
bool has_free_balloon_movement() const {
|
||||
return free_balloon_movement;
|
||||
}
|
||||
bool is_forcing_solid_converse_bg() const {
|
||||
return force_solid_converse_bg;
|
||||
}
|
||||
uint16 get_converse_gump_width() const {
|
||||
return converse_gump_width;
|
||||
}
|
||||
uint16 get_min_converse_gump_width() const {
|
||||
return min_converse_gump_width;
|
||||
}
|
||||
uint16 get_game_width() const {
|
||||
return game_width;
|
||||
}
|
||||
uint16 get_game_height() const {
|
||||
return game_height;
|
||||
}
|
||||
uint16 get_game_x_offset() const {
|
||||
return game_x_offset;
|
||||
}
|
||||
uint16 get_game_y_offset() const {
|
||||
return game_y_offset;
|
||||
}
|
||||
Common::Path get_data_file_path(const Common::Path &datafile);
|
||||
|
||||
/* Return instances of Game classes */
|
||||
static Game *get_game() {
|
||||
return game;
|
||||
}
|
||||
Configuration *get_config() {
|
||||
return config;
|
||||
}
|
||||
Script *get_script() {
|
||||
return script;
|
||||
}
|
||||
Screen *get_screen() {
|
||||
return screen;
|
||||
}
|
||||
Background *get_background() {
|
||||
return background;
|
||||
}
|
||||
GamePalette *get_palette() {
|
||||
return palette;
|
||||
}
|
||||
Dither *get_dither() {
|
||||
return dither;
|
||||
}
|
||||
FontManager *get_font_manager() {
|
||||
return font_manager;
|
||||
}
|
||||
TileManager *get_tile_manager() {
|
||||
return tile_manager;
|
||||
}
|
||||
ObjManager *get_obj_manager() {
|
||||
return obj_manager;
|
||||
}
|
||||
ActorManager *get_actor_manager() {
|
||||
return actor_manager;
|
||||
}
|
||||
EggManager *get_egg_manager() {
|
||||
return egg_manager;
|
||||
}
|
||||
Magic *get_magic() {
|
||||
return magic;
|
||||
}
|
||||
Map *get_game_map() {
|
||||
return game_map;
|
||||
}
|
||||
MapWindow *get_map_window() {
|
||||
return map_window;
|
||||
}
|
||||
MsgScroll *get_scroll() {
|
||||
return scroll;
|
||||
}
|
||||
Player *get_player() {
|
||||
return player;
|
||||
}
|
||||
Party *get_party() {
|
||||
return party;
|
||||
}
|
||||
Converse *get_converse() {
|
||||
return converse;
|
||||
}
|
||||
ConverseGump *get_converse_gump() {
|
||||
return conv_gump;
|
||||
}
|
||||
ViewManager *get_view_manager() {
|
||||
return view_manager;
|
||||
}
|
||||
GameClock *get_clock() {
|
||||
return _clock;
|
||||
}
|
||||
Portrait *get_portrait() {
|
||||
return portrait;
|
||||
}
|
||||
UseCode *get_usecode() {
|
||||
return usecode;
|
||||
}
|
||||
Events *get_event() {
|
||||
return event;
|
||||
}
|
||||
GUI *get_gui() {
|
||||
return gui;
|
||||
}
|
||||
SoundManager *get_sound_manager() {
|
||||
return sound_manager;
|
||||
}
|
||||
|
||||
Cursor *get_cursor() {
|
||||
return cursor;
|
||||
}
|
||||
EffectManager *get_effect_manager() {
|
||||
return effect_manager;
|
||||
}
|
||||
CommandBar *get_command_bar() {
|
||||
return command_bar;
|
||||
}
|
||||
CommandBar *get_new_command_bar() {
|
||||
return new_command_bar;
|
||||
}
|
||||
Weather *get_weather() {
|
||||
return weather;
|
||||
}
|
||||
|
||||
Book *get_book() {
|
||||
return book;
|
||||
}
|
||||
KeyBinder *get_keybinder() {
|
||||
return keybinder;
|
||||
}
|
||||
protected:
|
||||
void init_converse();
|
||||
void init_converse_gump_settings();
|
||||
|
||||
private:
|
||||
void update_once(bool process_gui_input, bool run_converse);
|
||||
};
|
||||
|
||||
extern uint getRandom(uint maxVal);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
356
engines/ultima/nuvie/core/game_clock.cpp
Normal file
356
engines/ultima/nuvie/core/game_clock.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/save/obj_list.h"
|
||||
#include "ultima/nuvie/core/weather.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
GameClock::GameClock(nuvie_game_t type) : game_type(type), day_of_week(0) {
|
||||
date_string[10] = '\0';
|
||||
time_string[10] = '\0';
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
GameClock::~GameClock() {
|
||||
|
||||
}
|
||||
|
||||
void GameClock::init() {
|
||||
move_counter = 0;
|
||||
time_counter = 0;
|
||||
// tick_counter = 0;
|
||||
|
||||
minute = 0;
|
||||
hour = 0;
|
||||
day = 0;
|
||||
month = 0;
|
||||
year = 0;
|
||||
rest_counter = 0;
|
||||
//active = true;
|
||||
num_timers = 0;
|
||||
}
|
||||
|
||||
bool GameClock::load(NuvieIO *objlist) {
|
||||
init();
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
objlist->seek(OBJLIST_OFFSET_U6_GAMETIME); // start of time data
|
||||
} else {
|
||||
objlist->seek(OBJLIST_OFFSET_WOU_GAMETIME); // start of time data
|
||||
}
|
||||
minute = objlist->read1();
|
||||
hour = objlist->read1();
|
||||
day = objlist->read1();
|
||||
month = objlist->read1();
|
||||
year = objlist->read2();
|
||||
|
||||
update_day_of_week();
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
load_U6_timers(objlist);
|
||||
} else if (game_type == NUVIE_GAME_MD) {
|
||||
load_MD_timers(objlist);
|
||||
}
|
||||
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "Loaded game clock: %s %s\n", get_date_string(), get_time_string());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameClock::load_U6_timers(NuvieIO *objlist) {
|
||||
num_timers = GAMECLOCK_NUM_TIMERS;
|
||||
timers.reserve(num_timers);
|
||||
timers.clear();
|
||||
objlist->seek(OBJLIST_OFFSET_U6_TIMERS);
|
||||
|
||||
for (uint8 i = 0; i < GAMECLOCK_NUM_TIMERS; i++) {
|
||||
timers.push_back(objlist->read1());
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_REST_COUNTER);
|
||||
rest_counter = objlist->read1();
|
||||
}
|
||||
|
||||
void GameClock::load_MD_timers(NuvieIO *objlist) {
|
||||
num_timers = GAMECLOCK_NUM_TIMERS * 3 + 1; //three berries per party member. 16 slots + 1 for the blue berry counter.
|
||||
timers.reserve(num_timers);
|
||||
timers.clear();
|
||||
objlist->seek(OBJLIST_OFFSET_MD_BERRY_TIMERS);
|
||||
|
||||
for (uint8 i = 0; i < GAMECLOCK_NUM_TIMERS; i++) {
|
||||
uint8 byte = objlist->read1();
|
||||
|
||||
timers.push_back((uint8)(byte & 0xf)); //purple
|
||||
timers.push_back((uint8)(byte >> 4)); //green
|
||||
timers.push_back((uint8)(objlist->read1() & 0xf)); //brown
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_MD_BLUE_BERRY_COUNTER);
|
||||
timers.push_back(objlist->read1()); //blue berry counter
|
||||
}
|
||||
|
||||
bool GameClock::save(NuvieIO *objlist) {
|
||||
objlist->seek(OBJLIST_OFFSET_U6_GAMETIME); // start of time data
|
||||
|
||||
objlist->write1(minute);
|
||||
objlist->write1(hour);
|
||||
objlist->write1(day);
|
||||
objlist->write1(month);
|
||||
objlist->write2(year);
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
save_U6_timers(objlist);
|
||||
} else if (game_type == NUVIE_GAME_MD) {
|
||||
save_MD_timers(objlist);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameClock::save_U6_timers(NuvieIO *objlist) {
|
||||
objlist->seek(OBJLIST_OFFSET_U6_TIMERS);
|
||||
|
||||
for (int i = 0; i < num_timers; i++) {
|
||||
objlist->write1(timers[i]);
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_REST_COUNTER);
|
||||
objlist->write1(rest_counter);
|
||||
}
|
||||
|
||||
void GameClock::save_MD_timers(NuvieIO *objlist) {
|
||||
objlist->seek(OBJLIST_OFFSET_MD_BERRY_TIMERS);
|
||||
|
||||
for (int i = 0; i < num_timers - 1; i += 3) {
|
||||
objlist->write1((uint8)(timers[i + 1] << 4) + timers[i]);
|
||||
objlist->write1(timers[i + 2]);
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_MD_BLUE_BERRY_COUNTER);
|
||||
objlist->write1(timers[num_timers - 1]);
|
||||
}
|
||||
|
||||
void GameClock::inc_move_counter() {
|
||||
move_counter++;
|
||||
|
||||
/* if((move_counter % GAMECLOCK_TICKS_PER_MINUTE) == 0)
|
||||
inc_minute();
|
||||
else
|
||||
tick_counter++;*/ // commented out because time is updated independently
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// move_counter by a minute.
|
||||
|
||||
void GameClock::inc_move_counter_by_a_minute() {
|
||||
move_counter += GAMECLOCK_TICKS_PER_MINUTE;
|
||||
|
||||
/* inc_minute();*/ // commented out because time is updated independently
|
||||
}
|
||||
|
||||
// advance game time to the start of the next hour.
|
||||
|
||||
void GameClock::advance_to_next_hour() {
|
||||
minute = 0;
|
||||
inc_hour();
|
||||
}
|
||||
|
||||
void GameClock::inc_minute(uint16 amount) {
|
||||
minute += amount;
|
||||
|
||||
if (minute >= 60) {
|
||||
for (; minute >= 60; minute -= 60) {
|
||||
inc_hour();
|
||||
}
|
||||
time_counter += minute;
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "%s\n", get_time_string());
|
||||
} else {
|
||||
time_counter += amount;
|
||||
}
|
||||
|
||||
//update_timers(1);
|
||||
return;
|
||||
}
|
||||
|
||||
void GameClock::inc_hour() {
|
||||
if (rest_counter > 0)
|
||||
rest_counter--;
|
||||
|
||||
if (hour == 23) {
|
||||
hour = 0;
|
||||
inc_day();
|
||||
} else {
|
||||
hour++;
|
||||
time_counter += 60;
|
||||
}
|
||||
|
||||
|
||||
if (game_type == NUVIE_GAME_U6)
|
||||
Game::get_game()->get_weather()->update_moongates();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GameClock::inc_day() {
|
||||
if (day == 28) {
|
||||
day = 1;
|
||||
inc_month();
|
||||
} else {
|
||||
day++;
|
||||
time_counter += 1440;
|
||||
}
|
||||
update_day_of_week();
|
||||
|
||||
DEBUG(0, LEVEL_INFORMATIONAL, "%s\n", get_date_string());
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GameClock::inc_month() {
|
||||
if (month == 12) {
|
||||
month = 1;
|
||||
inc_year();
|
||||
} else {
|
||||
month++;
|
||||
time_counter += 40320;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void GameClock::inc_year() {
|
||||
year++;
|
||||
time_counter += 483840;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 GameClock::get_move_count() const {
|
||||
return move_counter;
|
||||
}
|
||||
|
||||
const char *GameClock::get_time_of_day_string() {
|
||||
if (hour < 12)
|
||||
return "morning";
|
||||
|
||||
if (hour >= 12 && hour <= 18)
|
||||
return "afternoon";
|
||||
|
||||
return "evening";
|
||||
}
|
||||
|
||||
uint8 GameClock::get_hour() const {
|
||||
return hour;
|
||||
}
|
||||
|
||||
uint8 GameClock::get_minute() const {
|
||||
return minute;
|
||||
}
|
||||
|
||||
uint8 GameClock::get_day() const {
|
||||
return day;
|
||||
}
|
||||
|
||||
uint8 GameClock::get_month() const {
|
||||
return month;
|
||||
}
|
||||
|
||||
uint16 GameClock::get_year() const {
|
||||
return year;
|
||||
}
|
||||
|
||||
uint8 GameClock::get_day_of_week() const {
|
||||
return day_of_week;
|
||||
}
|
||||
|
||||
const char *GameClock::get_date_string() {
|
||||
|
||||
Common::sprintf_s(date_string, "%2u-%02u-%04u", month, day, year);
|
||||
|
||||
return date_string;
|
||||
}
|
||||
|
||||
const char *GameClock::get_time_string() {
|
||||
char c;
|
||||
uint8 tmp_hour;
|
||||
|
||||
if (hour < 12)
|
||||
c = 'A';
|
||||
else
|
||||
c = 'P';
|
||||
|
||||
if (hour > 12)
|
||||
tmp_hour = hour - 12;
|
||||
else {
|
||||
if (hour == 0)
|
||||
tmp_hour = 12;
|
||||
else
|
||||
tmp_hour = hour;
|
||||
}
|
||||
|
||||
Common::sprintf_s(time_string, "%0u:%02u %c.M.", tmp_hour, minute, c);
|
||||
|
||||
return time_string;
|
||||
}
|
||||
|
||||
uint8 GameClock::get_rest_counter() const {
|
||||
return rest_counter;
|
||||
}
|
||||
|
||||
inline void GameClock::update_day_of_week() {
|
||||
day_of_week = day % 7;
|
||||
if (day_of_week == 0)
|
||||
day_of_week = 7;
|
||||
}
|
||||
|
||||
void GameClock::set_timer(uint8 timer_num, uint8 val) {
|
||||
if (timer_num < num_timers) {
|
||||
timers[timer_num] = val;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 GameClock::get_timer(uint8 timer_num) const {
|
||||
if (timer_num < num_timers) {
|
||||
return timers[timer_num];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameClock::update_timers(uint8 amount) {
|
||||
for (uint8 i = 0; i < num_timers; i++) {
|
||||
if (timers[i] > amount) {
|
||||
timers[i] -= amount;
|
||||
} else
|
||||
timers[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
156
engines/ultima/nuvie/core/game_clock.h
Normal file
156
engines/ultima/nuvie/core/game_clock.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_GAME_CLOCK_H
|
||||
#define NUVIE_CORE_GAME_CLOCK_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::vector;
|
||||
|
||||
#define GAMECLOCK_TICKS_PER_MINUTE 4
|
||||
|
||||
#define GAMECLOCK_NUM_TIMERS 16
|
||||
|
||||
#define GAMECLOCK_TIMER_U6_LIGHT 0
|
||||
#define GAMECLOCK_TIMER_U6_INFRAVISION 1
|
||||
#define GAMECLOCK_TIMER_U6_STORM 13
|
||||
#define GAMECLOCK_TIMER_U6_TIME_STOP 14
|
||||
#define GAMECLOCK_TIMER_U6_ECLIPSE 15
|
||||
|
||||
#define GAMECLOCK_TIMER_MD_BLUE_BERRY 16*3
|
||||
|
||||
class Configuration;
|
||||
class NuvieIO;
|
||||
|
||||
class GameClock {
|
||||
nuvie_game_t game_type;
|
||||
|
||||
uint16 minute;
|
||||
uint8 hour;
|
||||
uint8 day;
|
||||
uint8 month;
|
||||
uint16 year;
|
||||
uint8 day_of_week;
|
||||
|
||||
uint32 move_counter; // player steps taken since start
|
||||
uint32 time_counter; // game minutes
|
||||
// uint32 tick_counter; // moves/turns since last minute
|
||||
|
||||
char date_string[11];
|
||||
char time_string[11];
|
||||
|
||||
//bool active; // clock is active and running (false = paused)
|
||||
|
||||
vector<uint8> timers;
|
||||
uint8 num_timers;
|
||||
|
||||
uint8 rest_counter; //hours until the party will heal again while resting.
|
||||
|
||||
public:
|
||||
|
||||
GameClock(nuvie_game_t type);
|
||||
~GameClock();
|
||||
|
||||
bool load(NuvieIO *objlist);
|
||||
bool save(NuvieIO *objlist);
|
||||
|
||||
//void set_active(bool state) { active = state; }
|
||||
//bool get_active() { return(active); }
|
||||
|
||||
void inc_move_counter();
|
||||
void inc_move_counter_by_a_minute();
|
||||
|
||||
void advance_to_next_hour();
|
||||
|
||||
void inc_minute(uint16 amount = 1);
|
||||
void inc_hour();
|
||||
void inc_day();
|
||||
void inc_month();
|
||||
void inc_year();
|
||||
|
||||
uint32 get_move_count() const;
|
||||
|
||||
const char *get_time_of_day_string();
|
||||
|
||||
uint8 get_hour() const;
|
||||
uint8 get_minute() const;
|
||||
|
||||
uint8 get_day() const;
|
||||
uint8 get_month() const;
|
||||
uint16 get_year() const;
|
||||
uint8 get_day_of_week() const;
|
||||
|
||||
const char *get_date_string();
|
||||
const char *get_time_string();
|
||||
|
||||
uint8 get_rest_counter() const;
|
||||
void set_rest_counter(uint8 value) {
|
||||
rest_counter = value;
|
||||
}
|
||||
|
||||
uint32 get_ticks() const {
|
||||
return SDL_GetTicks(); // milliseconds since start
|
||||
}
|
||||
uint32 get_game_ticks() const {
|
||||
return time_counter/**GAMECLOCK_TICKS_PER_MINUTE+tick_counter*/;
|
||||
}
|
||||
// uint32 get_time() { return(time_counter); } // get_game_ticks() is preferred
|
||||
uint32 get_turn() const {
|
||||
return move_counter;
|
||||
}
|
||||
|
||||
void set_timer(uint8 timer_num, uint8 val);
|
||||
uint8 get_timer(uint8 timer_num) const;
|
||||
void update_timers(uint8 amount);
|
||||
|
||||
//MD berry counters
|
||||
uint8 get_purple_berry_counter(uint8 actor_num) const {
|
||||
return get_timer(actor_num * 3);
|
||||
}
|
||||
uint8 get_green_berry_counter(uint8 actor_num) const {
|
||||
return get_timer(actor_num * 3 + 1);
|
||||
}
|
||||
uint8 get_brown_berry_counter(uint8 actor_num) const {
|
||||
return get_timer(actor_num * 3 + 2);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void init();
|
||||
inline void update_day_of_week();
|
||||
|
||||
private:
|
||||
void load_U6_timers(NuvieIO *objlist);
|
||||
void load_MD_timers(NuvieIO *objlist);
|
||||
void save_U6_timers(NuvieIO *objlist);
|
||||
void save_MD_timers(NuvieIO *objlist);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
179
engines/ultima/nuvie/core/look.cpp
Normal file
179
engines/ultima/nuvie/core/look.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/core/look.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Look::Look(const Configuration *cfg)
|
||||
: look_data(nullptr), desc_buf(nullptr), config(cfg), max_len(0) {
|
||||
look_tbl[2047] = nullptr;
|
||||
}
|
||||
|
||||
Look::~Look() {
|
||||
free(look_data);
|
||||
free(desc_buf);
|
||||
}
|
||||
|
||||
bool Look::init() {
|
||||
Common::Path filename;
|
||||
U6Lzw lzw;
|
||||
uint32 decomp_size;
|
||||
unsigned char *ptr;
|
||||
const char *s;
|
||||
uint16 i, j;
|
||||
unsigned int len;
|
||||
int game_type;
|
||||
NuvieIOFileRead look_file;
|
||||
|
||||
config->value("config/GameType", game_type);
|
||||
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
config_get_path(config, "look.lzd", filename);
|
||||
look_data = lzw.decompress_file(filename, decomp_size);
|
||||
if (look_data == nullptr)
|
||||
return false;
|
||||
break;
|
||||
case NUVIE_GAME_MD :
|
||||
case NUVIE_GAME_SE :
|
||||
U6Lib_n lib_file;
|
||||
config_get_path(config, "look.lzc", filename);
|
||||
if (lib_file.open(filename, 4, game_type) == false)
|
||||
return false;
|
||||
look_data = lib_file.get_item(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = look_data;
|
||||
// i: current string pos, j: last string pos
|
||||
for (i = 0, j = 0; i < 2048;) {
|
||||
// get number of string
|
||||
i = ptr[0] + (ptr[1] << 8);
|
||||
|
||||
if (i >= 2048)
|
||||
break;
|
||||
|
||||
// store pointer to look_data buffer
|
||||
look_tbl[i] = s = reinterpret_cast<char *>(&ptr[2]);
|
||||
|
||||
// update max_len
|
||||
len = strlen(s);
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
|
||||
ptr += len + 3;
|
||||
|
||||
// fill all empty strings between current and last one
|
||||
for (; j <= i; j++)
|
||||
look_tbl[j] = s;
|
||||
}
|
||||
|
||||
// fill remaining strings with "Nothing"
|
||||
for (i = j; i < 2048; i++) {
|
||||
look_tbl[i] = look_tbl[0]; // nothing
|
||||
}
|
||||
|
||||
// allocate space for description buffer
|
||||
desc_buf = (char *)malloc(max_len + 1);
|
||||
if (desc_buf == nullptr)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *Look::get_description(uint16 tile_num, bool *plural) {
|
||||
const char *desc;
|
||||
char c;
|
||||
uint16 i, j;
|
||||
uint16 len;
|
||||
bool has_plural = false;
|
||||
|
||||
if (tile_num >= 2048)
|
||||
return nullptr;
|
||||
|
||||
desc = look_tbl[tile_num];
|
||||
|
||||
len = strlen(desc);
|
||||
|
||||
for (i = 0, j = 0; i < len;) {
|
||||
if (desc[i] == '\\' || desc[i] == '/') {
|
||||
has_plural = true;
|
||||
c = desc[i];
|
||||
for (i++; Common::isAlpha(desc[i]) && i < len; i++) {
|
||||
|
||||
if ((*plural && c == '\\') || (!*plural && c == '/')) {
|
||||
desc_buf[j] = desc[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
desc_buf[j] = desc[i];
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
desc_buf[j] = desc[i];
|
||||
|
||||
*plural = has_plural; //we return if this string contained a plural form.
|
||||
|
||||
return desc_buf;
|
||||
}
|
||||
|
||||
bool Look::has_plural(uint16 tile_num) const {
|
||||
if (tile_num >= 2048)
|
||||
return false;
|
||||
|
||||
const char *desc = look_tbl[tile_num];
|
||||
|
||||
if (desc == nullptr)
|
||||
return false;
|
||||
|
||||
for (; *desc != '\0'; desc++) {
|
||||
if (*desc == '\\')
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 Look::get_max_len() const {
|
||||
return max_len;
|
||||
}
|
||||
|
||||
void Look::print() {
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
DEBUG(0, LEVEL_DEBUGGING, "%04d :: %s\n", i, look_tbl[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
57
engines/ultima/nuvie/core/look.h
Normal file
57
engines/ultima/nuvie/core/look.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_LOOKUP_H
|
||||
#define NUVIE_CORE_LOOKUP_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
|
||||
class Look {
|
||||
const Configuration *config;
|
||||
const char *look_tbl[2048];
|
||||
uint16 max_len;
|
||||
unsigned char *look_data;
|
||||
char *desc_buf;
|
||||
|
||||
public:
|
||||
|
||||
Look(const Configuration *cfg);
|
||||
~Look();
|
||||
|
||||
bool init();
|
||||
|
||||
// if description has a plural form, true is returned in plural
|
||||
const char *get_description(uint16 tile_num, bool *plural);
|
||||
bool has_plural(uint16 tile_num) const;
|
||||
uint16 get_max_len() const;
|
||||
|
||||
void print();
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
502
engines/ultima/nuvie/core/magic.cpp
Normal file
502
engines/ultima/nuvie/core/magic.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/usecode/usecode.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
#include "ultima/nuvie/gui/gui_yes_no_dialog.h"
|
||||
#include "ultima/nuvie/gui/widgets/console.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/views/view_manager.h"
|
||||
#include "ultima/nuvie/views/spell_view.h"
|
||||
#include "ultima/nuvie/sound/sound_manager.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h"
|
||||
#include "ultima/nuvie/core/magic.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/misc/u6_llist.h"
|
||||
#include "ultima/nuvie/core/effect.h"
|
||||
#include "ultima/nuvie/core/weather.h"
|
||||
#include "ultima/nuvie/script/script.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* Syllable Meaning Syllable Meaning
|
||||
* An .......... Negate/Dispel Nox ................ Poison
|
||||
* Bet ................. Small Ort ................. Magic
|
||||
* Corp ................ Death Por ......... Move/Movement
|
||||
* Des ............ Lower/Down Quas ............. Illusion
|
||||
* Ex ................ Freedom Rel ................ Change
|
||||
* Flam ................ Flame Sanct .. Protect/Protection
|
||||
* Grav ......... Energy/Field Tym .................. Time
|
||||
* Hur .................. Wind Uus .............. Raise/Up
|
||||
* In ...... Make/Create/Cause Vas ................. Great
|
||||
* Jux ...... Danger/Trap/Harm Wis ........ Know/Knowledge
|
||||
* Kal ......... Summon/Invoke Xen .............. Creature
|
||||
* Lor ................. Light Ylem ............... Matter
|
||||
* Mani ......... Life/Healing Zu .................. Sleep
|
||||
*/
|
||||
|
||||
const char *const syllable[26] = {"An ", "Bet ", "Corp ", "Des ", "Ex ", "Flam ", "Grav ", "Hur ", "In ", "Jux ", "Kal ", "Lor ", "Mani ", "Nox ", "Ort ", "Por ", "Quas ", "Rel ", "Sanct ", "Tym ", "Uus ", "Vas ", "Wis ", "Xen ", "Ylem ", "Zu "};
|
||||
const char *const reagent[8] = {"mandrake root", "nightshade", "black pearl", "blood moss", "spider silk", "garlic", "ginseng", "sulfurous ash"}; // check names
|
||||
const int obj_n_reagent[8] = {OBJ_U6_MANDRAKE_ROOT, OBJ_U6_NIGHTSHADE, OBJ_U6_BLACK_PEARL, OBJ_U6_BLOOD_MOSS, OBJ_U6_SPIDER_SILK, OBJ_U6_GARLIC, OBJ_U6_GINSENG, OBJ_U6_SULFUROUS_ASH};
|
||||
|
||||
|
||||
Magic::Magic() : event(nullptr), magic_script(nullptr), spellbook_obj(nullptr), state(0) {
|
||||
ARRAYCLEAR(spell);
|
||||
clear_cast_buffer();
|
||||
}
|
||||
|
||||
Magic::~Magic() {
|
||||
for (int index = 0; index < 256; index++)
|
||||
delete(spell[index]);
|
||||
}
|
||||
|
||||
bool Magic::init(Events *evt) {
|
||||
event = evt;
|
||||
return read_spell_list();
|
||||
}
|
||||
|
||||
bool Magic::read_spell_list() {
|
||||
return Game::get_game()->get_script()->call_magic_get_spell_list(spell);
|
||||
}
|
||||
|
||||
Obj *Magic::book_equipped() {
|
||||
// book(s) equipped? Maybe should check all locations?
|
||||
Actor *caster = event->player->get_actor();
|
||||
|
||||
Obj *obj = caster->inventory_get_readied_object(ACTOR_ARM);
|
||||
if (obj && obj->obj_n == OBJ_U6_SPELLBOOK)
|
||||
return obj;
|
||||
|
||||
obj = caster->inventory_get_readied_object(ACTOR_ARM_2);
|
||||
|
||||
if (obj && obj->obj_n == OBJ_U6_SPELLBOOK)
|
||||
return obj;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Magic::start_new_spell() {
|
||||
spellbook_obj = book_equipped();
|
||||
|
||||
if (Game::get_game()->get_clock()->get_timer(GAMECLOCK_TIMER_U6_STORM) > 0 && !Game::get_game()->has_unlimited_casting()) {
|
||||
event->scroll->display_string("No magic at this time!\n\n");
|
||||
} else if (spellbook_obj != nullptr) {
|
||||
state = MAGIC_STATE_SELECT_SPELL;
|
||||
clear_cast_buffer();
|
||||
event->close_gumps();
|
||||
Game::get_game()->get_view_manager()->set_spell_mode(event->player->get_actor(), spellbook_obj);
|
||||
Game::get_game()->get_view_manager()->get_spell_view()->grab_focus();
|
||||
return true;
|
||||
} else
|
||||
event->scroll->display_string("\nNo spellbook is readied.\n\n");
|
||||
|
||||
state = MAGIC_STATE_READY;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Magic::cast() {
|
||||
if (magic_script != nullptr)
|
||||
return false;
|
||||
|
||||
Game::get_game()->get_view_manager()->close_spell_mode();
|
||||
|
||||
|
||||
cast_buffer_str[cast_buffer_len] = '\0';
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Trying to cast '%s'\n", cast_buffer_str);
|
||||
/* decode the invocation */
|
||||
// FIXME? original allows random order of syllables, do we want that?
|
||||
// easy enough to sort invocations, but would somewhat limit custom spells
|
||||
uint16 index;
|
||||
|
||||
if (cast_buffer_len != 0) {
|
||||
for (index = 0; index < 256; index++) {
|
||||
if (spell[index] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(spell[index]->invocation, cast_buffer_str)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sint16 view_spell = Game::get_game()->get_view_manager()->get_spell_view()->get_selected_spell();
|
||||
if (view_spell < 256 && view_spell != -1)
|
||||
index = (uint16)view_spell;
|
||||
else
|
||||
index = 256;
|
||||
}
|
||||
|
||||
if (index >= 256) {
|
||||
DEBUG(0, LEVEL_DEBUGGING, "didn't find spell in spell list\n");
|
||||
event->scroll->display_string("\nThat spell is not in thy spellbook!\n");
|
||||
return false;
|
||||
}
|
||||
//20110701 Pieter Luteijn: add an assert(spell[index]) to be sure it's not nullptr?
|
||||
if (cast_buffer_len != 0) {
|
||||
event->scroll->display_string("\n(");
|
||||
event->scroll->display_string(spell[index]->name);
|
||||
event->scroll->display_string(")\n");
|
||||
} else {
|
||||
event->scroll->display_string(spell[index]->name);
|
||||
event->scroll->display_string("\n\"");
|
||||
display_spell_incantation(index);
|
||||
event->scroll->display_string("\"\n");
|
||||
}
|
||||
|
||||
if (Game::get_game()->has_unlimited_casting()) {
|
||||
cast_spell_directly(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* debug block */
|
||||
DEBUG(0, LEVEL_DEBUGGING, "matched spell #%d\n", index);
|
||||
DEBUG(0, LEVEL_DEBUGGING, "name: %s\n", spell[index]->name);
|
||||
DEBUG(0, LEVEL_DEBUGGING, "reagents: ");
|
||||
const char *comma = "";
|
||||
for (uint8 shift = 0; shift < 8; shift++) {
|
||||
if (1 << shift & spell[index]->reagents) {
|
||||
DEBUG(1, LEVEL_DEBUGGING, "%s%s", comma, reagent[shift]);
|
||||
comma = ", ";
|
||||
}
|
||||
}
|
||||
DEBUG(1, LEVEL_DEBUGGING, "\n");
|
||||
//DEBUG(0,LEVEL_DEBUGGING,"script: %s\n",spell[index]->script);
|
||||
/* end debug block */
|
||||
|
||||
|
||||
if (Game::get_game()->user_paused()) //event->mode == WAIT_MODE)
|
||||
return false;
|
||||
|
||||
// book(s) equipped? Maybe should check all locations?
|
||||
Actor *caster = event->player->get_actor();
|
||||
Obj *right = caster->inventory_get_readied_object(ACTOR_ARM);
|
||||
Obj *left = caster->inventory_get_readied_object(ACTOR_ARM_2);
|
||||
uint8 books = 0;
|
||||
if (right != nullptr && right->obj_n == OBJ_U6_SPELLBOOK) {
|
||||
books += 1;
|
||||
};
|
||||
if (left != nullptr && left->obj_n == OBJ_U6_SPELLBOOK) {
|
||||
books += 2;
|
||||
};
|
||||
|
||||
if (right && right->obj_n != OBJ_U6_SPELLBOOK)
|
||||
right = nullptr;
|
||||
if (left && left->obj_n != OBJ_U6_SPELLBOOK)
|
||||
left = nullptr;
|
||||
|
||||
if (right == nullptr && left == nullptr) {
|
||||
event->scroll->display_string("\nNo spellbook is readied.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// any spells available?
|
||||
uint32 spells = 0;
|
||||
if ((books & 1) && right->container) { // hmm, relying on shortcut logic here.
|
||||
spells = right->container->count();
|
||||
}
|
||||
if ((books & 2) && left->container) {
|
||||
spells += left->container->count();
|
||||
}
|
||||
if (!spells) {
|
||||
event->scroll->display_string("\nNo spells in the spellbook.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// spell (or catch all spell 255) in (one of the) book(s)?
|
||||
if (spellbook_has_spell(right, index) == false && spellbook_has_spell(left, index) == false) {
|
||||
event->scroll->display_string("\nThat spell is not in thy spellbook!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// level of caster sufficient
|
||||
uint8 spell_level = MIN(8, (index / 16) + 1);
|
||||
if (caster->get_level() < spell_level) {
|
||||
event->scroll->display_string("\nYour level is not high enough.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// enough Magic Points available
|
||||
if (caster->get_magic() < spell_level) {
|
||||
event->scroll->display_string("\nNot enough magic points.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// reagents available
|
||||
for (uint8 shift = 0; shift < 8; shift++) {
|
||||
if (1 << shift & spell[index]->reagents) {
|
||||
if (!caster->inventory_has_object(obj_n_reagent[shift], 0, false)) {
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Didn't have %s\n", reagent[shift]);
|
||||
event->scroll->display_string("\nNo Reagents.\n");
|
||||
Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_FAILURE);
|
||||
return false;
|
||||
}
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Ok, has %s\n", reagent[shift]);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO check all pre-requisites before continue */
|
||||
// 'spell failed' because of bad luck
|
||||
// anything else?
|
||||
|
||||
// consume the reagents and magic points; we checked so they must be there.
|
||||
caster->set_magic(caster->get_magic() - spell_level); // add a MAX (0, here?
|
||||
|
||||
for (int shift = 0; shift < 8; shift++) {
|
||||
if (1 << shift & spell[index]->reagents) {
|
||||
// FIXME Although we just checked, maybe something is messed up, so we
|
||||
// should probably check that we're not passing nullptr to delete_obj
|
||||
caster->inventory_del_object(obj_n_reagent[shift], 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
cast_spell_directly(index);
|
||||
|
||||
event->player->subtract_movement_points(spell_level * 3 + 10);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Magic::display_spell_incantation(uint8 index) {
|
||||
string incantation_str;
|
||||
for (int i = 0; spell[index]->invocation[i] != '\0'; i++)
|
||||
incantation_str += syllable[spell[index]->invocation[i] - Common::KEYCODE_a];
|
||||
|
||||
incantation_str.erase(incantation_str.size() - 1); // get rid of extra space at the end
|
||||
event->scroll->display_string(incantation_str);
|
||||
}
|
||||
|
||||
void Magic::show_spell_description(uint8 index) {
|
||||
event->scroll->display_string(spell[index]->name);
|
||||
event->scroll->display_string("-");
|
||||
display_spell_incantation(index);
|
||||
display_ingredients(index);
|
||||
}
|
||||
|
||||
void Magic::display_ingredients(uint8 index) {
|
||||
event->scroll->display_string("\nIngredients:\n");
|
||||
if (spell[index]->reagents == 0) {
|
||||
event->scroll->display_string("None\n\n");
|
||||
return;
|
||||
}
|
||||
string list;
|
||||
for (int shift = 0; shift < 8; shift++) {
|
||||
if (1 << shift & spell[index]->reagents) {
|
||||
list += " ";
|
||||
list += reagent[shift];
|
||||
list += "\n";
|
||||
}
|
||||
}
|
||||
list += "\n";
|
||||
event->scroll->set_discard_whitespace(false);
|
||||
event->scroll->display_string(list);
|
||||
event->scroll->set_discard_whitespace(true);
|
||||
}
|
||||
|
||||
void Magic::cast_spell_directly(uint8 spell_num) {
|
||||
string lua = "run_magic_script(\"";
|
||||
lua += spell[spell_num]->invocation;
|
||||
lua += "\")";
|
||||
|
||||
magic_script = Game::get_game()->get_script()->new_thread_from_string(lua.c_str());
|
||||
|
||||
if (magic_script)
|
||||
process_script_return(magic_script->start());
|
||||
}
|
||||
|
||||
bool Magic::resume(const MapCoord &location) {
|
||||
if (magic_script) {
|
||||
process_script_return(magic_script->resume_with_location(location));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Magic::resume(NuvieDir dir) {
|
||||
if (magic_script) {
|
||||
process_script_return(magic_script->resume_with_direction(dir));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Magic::resume_with_spell_num(uint8 spell_num) {
|
||||
if (magic_script) {
|
||||
process_script_return(magic_script->resume_with_spell_num(spell_num));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Magic::resume(Obj *obj) {
|
||||
if (magic_script) {
|
||||
process_script_return(magic_script->resume_with_obj(obj));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Magic::resume() {
|
||||
if (magic_script) {
|
||||
process_script_return(magic_script->resume_with_nil());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Magic::spellbook_has_spell(Obj *book, uint8 spell_index) {
|
||||
if (!book)
|
||||
return false;
|
||||
|
||||
if (book->find_in_container(OBJ_U6_SPELL, MAGIC_ALL_SPELLS, OBJ_MATCH_QUALITY) ||
|
||||
book->find_in_container(OBJ_U6_SPELL, spell_index, OBJ_MATCH_QUALITY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Magic::process_script_return(uint8 ret) {
|
||||
Game::get_game()->get_view_manager()->close_all_gumps();
|
||||
if (ret == NUVIE_SCRIPT_ERROR) {
|
||||
delete magic_script;
|
||||
magic_script = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 nturns;
|
||||
uint8 *cb_msgid;
|
||||
switch (ret) {
|
||||
case NUVIE_SCRIPT_FINISHED :
|
||||
delete magic_script;
|
||||
magic_script = nullptr;
|
||||
state = MAGIC_STATE_READY;
|
||||
Game::get_game()->get_actor_manager()->startActors(); // end player turn
|
||||
break;
|
||||
case NUVIE_SCRIPT_GET_TARGET :
|
||||
state = MAGIC_STATE_ACQUIRE_TARGET;
|
||||
break;
|
||||
case NUVIE_SCRIPT_GET_DIRECTION :
|
||||
state = MAGIC_STATE_ACQUIRE_DIRECTION;
|
||||
break;
|
||||
case NUVIE_SCRIPT_GET_INV_OBJ :
|
||||
state = MAGIC_STATE_ACQUIRE_INV_OBJ;
|
||||
break;
|
||||
case NUVIE_SCRIPT_GET_OBJ :
|
||||
state = MAGIC_STATE_ACQUIRE_OBJ;
|
||||
break;
|
||||
case NUVIE_SCRIPT_GET_SPELL :
|
||||
state = MAGIC_STATE_ACQUIRE_SPELL;
|
||||
break;
|
||||
|
||||
case NUVIE_SCRIPT_ADVANCE_GAME_TIME :
|
||||
nturns = magic_script->get_data();
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Magic: Advance %d turns\n", nturns);
|
||||
cb_msgid = new uint8;
|
||||
*cb_msgid = NUVIE_SCRIPT_CB_ADV_GAME_TIME;
|
||||
new GameTimedCallback((CallBack *)this, cb_msgid, nturns);
|
||||
break;
|
||||
|
||||
case NUVIE_SCRIPT_TALK_TO_ACTOR :
|
||||
state = MAGIC_STATE_TALK_TO_ACTOR;
|
||||
break;
|
||||
|
||||
default :
|
||||
DEBUG(0, LEVEL_WARNING, "Unknown ScriptThread return code!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Actor *Magic::get_actor_from_script() {
|
||||
if (magic_script && (state == MAGIC_STATE_ACQUIRE_INV_OBJ || state == MAGIC_STATE_TALK_TO_ACTOR))
|
||||
return Game::get_game()->get_actor_manager()->get_actor((uint8)magic_script->get_data());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16 Magic::callback(uint16 msg, CallBack *caller, void *data) {
|
||||
if (msg == CB_DATA_READY) {
|
||||
if (event->input.type != EVENTINPUT_KEY)
|
||||
return 0;
|
||||
Common::KeyCode sym = event->input.key;
|
||||
|
||||
if (state == MAGIC_STATE_SELECT_SPELL) {
|
||||
if (sym >= Common::KEYCODE_a && sym <= Common::KEYCODE_z) {
|
||||
if (cast_buffer_len < 4) {
|
||||
cast_buffer_str[cast_buffer_len++] = sym;
|
||||
event->scroll->display_string(syllable[sym - Common::KEYCODE_a]);
|
||||
return 1; // handled the event
|
||||
}
|
||||
return 1; // handled the event
|
||||
} else if (sym == Common::KEYCODE_BACKSPACE) {
|
||||
if (cast_buffer_len > 0) {
|
||||
cast_buffer_len--; // back up a syllable FIXME, doesn't handle automatically inserted newlines, so we need to keep track more. (THAT SHOULD BE DONE BY MSGSCROLL)
|
||||
size_t len = strlen(syllable[cast_buffer_str[cast_buffer_len] - Common::KEYCODE_a]);
|
||||
while (len--) event->scroll->remove_char();
|
||||
event->scroll->Display(true);
|
||||
return 1; // handled the event
|
||||
}
|
||||
return 1; // handled the event
|
||||
}
|
||||
} // MAGIC_STATE_SELECT_SPELL
|
||||
if (state == MAGIC_STATE_ACQUIRE_TARGET) {
|
||||
if (sym >= Common::KEYCODE_1 && sym <= Common::KEYCODE_9) {
|
||||
cast();//event->player->get_party()->get_actor(sym - 48-1));
|
||||
event->cancel_key_redirect();
|
||||
return 1; // handled the event
|
||||
}
|
||||
}
|
||||
|
||||
// We must handle all keys even those we may not want or else
|
||||
// we'll lose input focus, except for these three which end
|
||||
// Casting. (besides, not handling all keys means they go back
|
||||
// to global which could start another action)
|
||||
if (event->input.action_key_type != DO_ACTION_KEY && event->input.action_key_type != CANCEL_ACTION_KEY)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
} else if (magic_script) {
|
||||
switch (msg) {
|
||||
case NUVIE_SCRIPT_GET_TARGET :
|
||||
process_script_return(magic_script->resume_with_location(MapCoord(200, 200, 0))); //FIXME need to get real loc.
|
||||
break;
|
||||
case NUVIE_SCRIPT_GET_DIRECTION :
|
||||
process_script_return(magic_script->resume_with_direction(NUVIE_DIR_N)); //FIXME need to get real dir.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
167
engines/ultima/nuvie/core/magic.h
Normal file
167
engines/ultima/nuvie/core/magic.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_MAGIC_H
|
||||
#define NUVIE_CORE_MAGIC_H
|
||||
|
||||
#include "ultima/nuvie/core/events.h"
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define REAGENT_U6_MANDRAKE_ROOT 0x01
|
||||
#define REAGENT_U6_NIGHTSHADE 0x02
|
||||
#define REAGENT_U6_BLACK_PEARL 0x04
|
||||
#define REAGENT_U6_BLOOD_MOSS 0x08
|
||||
#define REAGENT_U6_SPIDER_SILK 0x10
|
||||
#define REAGENT_U6_GARLIC 0x20
|
||||
#define REAGENT_U6_GINSENG 0x40
|
||||
#define REAGENT_U6_SULFUROUS_ASH 0x80
|
||||
|
||||
#define MAX_SCRIPT_LENGTH 65000
|
||||
#define MAX_TOKEN_LENGTH 255
|
||||
#define MAX_STACK_DEPTH 255
|
||||
|
||||
#define MAGIC_STATE_READY 0x00
|
||||
#define MAGIC_STATE_SELECT_SPELL 0x01
|
||||
#define MAGIC_STATE_PROCESS_SCRIPT 0x02
|
||||
#define MAGIC_STATE_ACQUIRE_TARGET 0x03
|
||||
#define MAGIC_STATE_ACQUIRE_INPUT 0x04
|
||||
#define MAGIC_STATE_ACQUIRE_DIRECTION 0x05
|
||||
#define MAGIC_STATE_ACQUIRE_INV_OBJ 0x06
|
||||
#define MAGIC_STATE_TALK_TO_ACTOR 0x07
|
||||
#define MAGIC_STATE_ACQUIRE_SPELL 0x08
|
||||
#define MAGIC_STATE_ACQUIRE_OBJ 0x09
|
||||
|
||||
#define MAGIC_ALL_SPELLS 255
|
||||
|
||||
class ScriptThread;
|
||||
class NuvieIOFileRead;
|
||||
|
||||
class Spell {
|
||||
public: /* saves using dumb get / set functions */
|
||||
uint8 num;
|
||||
char *name; // eg. "Heal"
|
||||
char *invocation; // eg. "im"
|
||||
uint8 reagents; // reagents used
|
||||
|
||||
Spell(uint8 new_num, const char *new_name = "undefined name", const char *new_invocation = "kawo", uint8 new_reagents = 255) {
|
||||
num = new_num;
|
||||
name = scumm_strdup(new_name);
|
||||
invocation = scumm_strdup(new_invocation);
|
||||
reagents = new_reagents;
|
||||
};
|
||||
~Spell() {
|
||||
free(name);
|
||||
free(invocation);
|
||||
}
|
||||
};
|
||||
|
||||
class Magic : public CallBack {
|
||||
private:
|
||||
Spell *spell[256]; // spell list;
|
||||
char cast_buffer_str[26]; // buffer for spell syllables typed.
|
||||
uint8 cast_buffer_len; // how many characters typed in the spell buffer.
|
||||
Events *event;
|
||||
uint8 state;
|
||||
|
||||
ScriptThread *magic_script;
|
||||
Obj *spellbook_obj;
|
||||
/* TODO
|
||||
* add a register array, or a pointer to a list of variables?
|
||||
*/
|
||||
public:
|
||||
Magic();
|
||||
~Magic() override;
|
||||
bool init(Events *evt);
|
||||
|
||||
bool read_spell_list();
|
||||
void clear_cast_buffer() {
|
||||
cast_buffer_str[0] = '\0';
|
||||
cast_buffer_len = 0;
|
||||
}
|
||||
bool start_new_spell();
|
||||
Obj *book_equipped();
|
||||
bool cast();
|
||||
void cast_spell_directly(uint8 spell_num);
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data = nullptr) override;
|
||||
bool process_script_return(uint8 ret);
|
||||
bool resume(const MapCoord &location);
|
||||
bool resume(NuvieDir dir);
|
||||
bool resume_with_spell_num(uint8 spell_num);
|
||||
bool resume(Obj *obj);
|
||||
bool resume();
|
||||
bool is_waiting_for_location() const {
|
||||
if (magic_script && state == MAGIC_STATE_ACQUIRE_TARGET) return true;
|
||||
else return false;
|
||||
}
|
||||
bool is_waiting_for_direction() const {
|
||||
if (magic_script && state == MAGIC_STATE_ACQUIRE_DIRECTION) return true;
|
||||
else return false;
|
||||
}
|
||||
bool is_waiting_for_inventory_obj() const {
|
||||
if (magic_script && state == MAGIC_STATE_ACQUIRE_INV_OBJ) return true;
|
||||
else return false;
|
||||
}
|
||||
bool is_waiting_for_obj() const {
|
||||
if (magic_script && state == MAGIC_STATE_ACQUIRE_OBJ) return true;
|
||||
else return false;
|
||||
}
|
||||
bool is_waiting_to_talk() const {
|
||||
if (state == MAGIC_STATE_TALK_TO_ACTOR) return true;
|
||||
else return false;
|
||||
}
|
||||
bool is_waiting_for_spell() const {
|
||||
if (magic_script && state == MAGIC_STATE_ACQUIRE_SPELL) return true;
|
||||
else return false;
|
||||
}
|
||||
bool is_selecting_spell() const {
|
||||
if (magic_script && state == MAGIC_STATE_SELECT_SPELL) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool is_waiting_to_resume() const {
|
||||
if (magic_script) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
Spell *get_spell(uint8 spell_num) {
|
||||
return spell[spell_num];
|
||||
}
|
||||
Obj *get_spellbook_obj() {
|
||||
return spellbook_obj;
|
||||
}
|
||||
|
||||
Actor *get_actor_from_script();
|
||||
void show_spell_description(uint8 index);
|
||||
private:
|
||||
bool spellbook_has_spell(Obj *book, uint8 spell_index);
|
||||
void display_ingredients(uint8 index);
|
||||
void display_spell_incantation(uint8 index);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
823
engines/ultima/nuvie/core/map.cpp
Normal file
823
engines/ultima/nuvie/core/map.cpp
Normal file
@@ -0,0 +1,823 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Map::Map(const Configuration *cfg) : config(cfg), tile_manager(nullptr),
|
||||
obj_manager(nullptr), actor_manager(nullptr), surface(nullptr),
|
||||
roof_surface(nullptr) {
|
||||
ARRAYCLEAR(dungeons);
|
||||
|
||||
config->value(config_get_game_key(config) + "/roof_mode", roof_mode, false);
|
||||
}
|
||||
|
||||
Map::~Map() {
|
||||
if (surface == nullptr)
|
||||
return;
|
||||
|
||||
free(surface);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
free(dungeons[i]);
|
||||
|
||||
if (roof_surface)
|
||||
free(roof_surface);
|
||||
}
|
||||
|
||||
|
||||
byte *Map::get_map_data(uint8 level) {
|
||||
if (level == 0)
|
||||
return surface;
|
||||
|
||||
if (level > 5)
|
||||
return nullptr;
|
||||
|
||||
return dungeons[level - 1];
|
||||
}
|
||||
|
||||
uint16 *Map::get_roof_data(uint8 level) {
|
||||
if (level == 0)
|
||||
return roof_surface;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Tile *Map::get_tile(uint16 x, uint16 y, uint8 level, bool original_tile) {
|
||||
if (level > 5)
|
||||
return nullptr;
|
||||
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile;
|
||||
if (original_tile)
|
||||
map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
else
|
||||
map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
return map_tile;
|
||||
}
|
||||
|
||||
uint16 Map::get_width(uint8 level) const {
|
||||
if (level == 0)
|
||||
return 1024; // surface
|
||||
|
||||
return 256; // dungeon
|
||||
}
|
||||
|
||||
bool Map::is_passable(uint16 x, uint16 y, uint8 level) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 obj_status = obj_manager->is_passable(x, y, level);
|
||||
if (obj_status == OBJ_NOT_PASSABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//special case for bridges, hacked doors and dungeon entrances etc.
|
||||
if (obj_status != OBJ_NO_OBJ && obj_manager->is_forced_passable(x, y, level))
|
||||
return true;
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
return map_tile->passable;
|
||||
}
|
||||
|
||||
/***
|
||||
* Can we enter this map location by traveling in a given direction?
|
||||
* Used by MD
|
||||
*/
|
||||
bool Map::is_passable(uint16 x, uint16 y, uint8 level, NuvieDir dir) {
|
||||
if (is_passable_from_dir(x, y, level, get_reverse_direction(dir))) {
|
||||
sint16 rel_x, rel_y;
|
||||
uint16 tx, ty;
|
||||
get_relative_dir(get_reverse_direction(dir), &rel_x, &rel_y);
|
||||
tx = wrap_signed_coord((sint16)x + rel_x, level);
|
||||
ty = wrap_signed_coord((sint16)y + rel_y, level);
|
||||
return is_passable_from_dir(tx, ty, level, dir);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_passable_from_dir(uint16 x, uint16 y, uint8 level, NuvieDir dir) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 obj_status = obj_manager->is_passable(x, y, level);
|
||||
if (obj_status == OBJ_NOT_PASSABLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//special case for bridges, hacked doors and dungeon entrances etc.
|
||||
if (obj_status != OBJ_NO_OBJ && obj_manager->is_forced_passable(x, y, level))
|
||||
return true;
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (!map_tile->passable && !(map_tile->flags1 & TILEFLAG_WALL)) {
|
||||
switch (dir) {
|
||||
case NUVIE_DIR_W :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_WEST);
|
||||
case NUVIE_DIR_S :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_SOUTH);
|
||||
case NUVIE_DIR_E :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_EAST);
|
||||
case NUVIE_DIR_N :
|
||||
return (map_tile->flags1 & TILEFLAG_WALL_NORTH);
|
||||
case NUVIE_DIR_NE :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_NORTH) || !(map_tile->flags1 & TILEFLAG_WALL_EAST));
|
||||
case NUVIE_DIR_NW :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_NORTH) || !(map_tile->flags1 & TILEFLAG_WALL_WEST));
|
||||
case NUVIE_DIR_SE :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_SOUTH) || !(map_tile->flags1 & TILEFLAG_WALL_EAST));
|
||||
case NUVIE_DIR_SW :
|
||||
return !(!(map_tile->flags1 & TILEFLAG_WALL_SOUTH) || !(map_tile->flags1 & TILEFLAG_WALL_WEST));
|
||||
default:
|
||||
error("Invalid direction in Map::is_passable_from_dir");
|
||||
}
|
||||
}
|
||||
|
||||
return map_tile->passable;
|
||||
}
|
||||
|
||||
/* Returns true if an entire area is_passable(). */
|
||||
bool Map::is_passable(uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint8 level) {
|
||||
for (int x = x1; x <= x2; x++)
|
||||
for (int y = y1; y <= y2; y++)
|
||||
if (!is_passable((uint16)x, (uint16)y, level))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::is_boundary(uint16 x, uint16 y, uint8 level) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 *ptr = get_map_data(level);
|
||||
Tile *map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (map_tile->boundary && obj_manager->is_forced_passable(x, y, level) == false)
|
||||
return true;
|
||||
|
||||
if (obj_manager->is_boundary(x, y, level))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_missile_boundary(uint16 x, uint16 y, uint8 level, Obj *excluded_obj) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
uint8 *ptr = get_map_data(level);
|
||||
Tile *map_tile = tile_manager->get_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if ((map_tile->flags2 & TILEFLAG_MISSILE_BOUNDARY) != 0 && obj_manager->is_forced_passable(x, y, level) == false)
|
||||
return true;
|
||||
|
||||
if (obj_manager->is_boundary(x, y, level, TILEFLAG_MISSILE_BOUNDARY, excluded_obj))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_water(uint16 x, uint16 y, uint16 level, bool ignore_objects) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
if (!ignore_objects) {
|
||||
const Obj *obj = obj_manager->get_obj(x, y, level);
|
||||
if (obj != nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (map_tile->water)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::is_damaging(uint16 x, uint16 y, uint8 level, bool ignore_objects) {
|
||||
const uint8 *ptr = get_map_data(level);
|
||||
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
|
||||
if (map_tile->damages)
|
||||
return true;
|
||||
|
||||
if (!ignore_objects) {
|
||||
if (obj_manager->is_damaging(x, y, level))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::can_put_obj(uint16 x, uint16 y, uint8 level) {
|
||||
LineTestResult lt;
|
||||
|
||||
if (lineTest(x, y, x, y, level, LT_HitActors | LT_HitUnpassable, lt)) {
|
||||
if (lt.hitObj) {
|
||||
// We can place an object on a bench or table. Or on any other object if
|
||||
// the object is passable and not on a boundary.
|
||||
|
||||
Tile *obj_tile = obj_manager->get_obj_tile(lt.hitObj->obj_n, lt.hitObj->frame_n);
|
||||
if ((obj_tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP) ||
|
||||
(obj_tile->passable && !is_boundary(lt.hit_x, lt.hit_y, lt.hit_level))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_missile_boundary(x, y, level))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 Map::get_impedance(uint16 x, uint16 y, uint8 level, bool ignore_objects) {
|
||||
uint8 *ptr = get_map_data(level);
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
const Tile *map_tile = tile_manager->get_original_tile(ptr[y * get_width(level) + x]);
|
||||
uint8 impedance = 0;
|
||||
|
||||
if (!ignore_objects) {
|
||||
const U6LList *obj_list = obj_manager->get_obj_list(x, y, level);
|
||||
if (obj_list) {
|
||||
for (const U6Link *link = obj_list->start(); link != nullptr; link = link->next) {
|
||||
const Obj *obj = (Obj *)link->data;
|
||||
if (obj != nullptr) {
|
||||
uint8 tile_flag = obj_manager->get_obj_tile(obj->obj_n, obj->frame_n)->flags1;
|
||||
if ((tile_flag & TILEFLAG_BLOCKING) == 0) {
|
||||
impedance += (tile_flag & TILEFLAG_IMPEDANCE) >> TILEFLAG_IMPEDANCE_SHIFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((map_tile->flags1 & TILEFLAG_BLOCKING) == 0)
|
||||
impedance += (map_tile->flags1 & TILEFLAG_IMPEDANCE) >> TILEFLAG_IMPEDANCE_SHIFT;
|
||||
|
||||
return impedance;
|
||||
}
|
||||
|
||||
const Tile *Map::get_dmg_tile(uint16 x, uint16 y, uint8 level) {
|
||||
const Tile *tile = get_tile(x, y, level);
|
||||
|
||||
if (tile->damages)
|
||||
return tile;
|
||||
|
||||
return obj_manager->get_obj_dmg_tile(x, y, level);
|
||||
}
|
||||
|
||||
bool Map::actor_at_location(uint16 x, uint16 y, uint8 level, bool inc_surrounding_objs) {
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
//check for blocking Actor at location.
|
||||
if (actor_manager->get_actor(x, y, level, inc_surrounding_objs) != nullptr)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return pointer to actor standing at map coordinates.
|
||||
*/
|
||||
Actor *Map::get_actor(uint16 x, uint16 y, uint8 z, bool inc_surrounding_objs) {
|
||||
WRAP_COORD(x, z);
|
||||
WRAP_COORD(y, z);
|
||||
return (actor_manager->get_actor(x, y, z, inc_surrounding_objs));
|
||||
}
|
||||
|
||||
|
||||
const char *Map::look(uint16 x, uint16 y, uint8 level) {
|
||||
unsigned char *ptr;
|
||||
uint16 qty = 0;
|
||||
|
||||
if (level == 0) {
|
||||
ptr = surface;
|
||||
} else
|
||||
ptr = dungeons[level - 1];
|
||||
|
||||
WRAP_COORD(x, level);
|
||||
WRAP_COORD(y, level);
|
||||
Obj *obj = obj_manager->get_obj(x, y, level);
|
||||
if (obj != nullptr && !(obj->status & OBJ_STATUS_INVISIBLE) //only show visible objects.
|
||||
&& !Game::get_game()->get_map_window()->tile_is_black(obj->x, obj->y, obj)) {
|
||||
// tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n)+obj->frame_n);
|
||||
// tile_num = tile->tile_num;
|
||||
// qty = obj->qty;
|
||||
return obj_manager->look_obj(obj);
|
||||
}
|
||||
uint16 tile_num = ptr[y * get_width(level) + x];
|
||||
return tile_manager->lookAtTile(tile_num, qty, true);
|
||||
}
|
||||
|
||||
|
||||
bool Map::loadMap(TileManager *tm, ObjManager *om) {
|
||||
Common::Path filename;
|
||||
NuvieIOFileRead map_file;
|
||||
NuvieIOFileRead chunks_file;
|
||||
|
||||
uint8 i;
|
||||
|
||||
tile_manager = tm;
|
||||
obj_manager = om;
|
||||
|
||||
config_get_path(config, "map", filename);
|
||||
if (map_file.open(filename) == false)
|
||||
return false;
|
||||
|
||||
config_get_path(config, "chunks", filename);
|
||||
if (chunks_file.open(filename) == false)
|
||||
return false;
|
||||
|
||||
unsigned char *map_data = map_file.readAll();
|
||||
if (map_data == nullptr)
|
||||
return false;
|
||||
|
||||
unsigned char *chunk_data = chunks_file.readAll();
|
||||
if (chunk_data == nullptr)
|
||||
return false;
|
||||
|
||||
unsigned char *map_ptr = map_data;
|
||||
|
||||
surface = (unsigned char *)malloc(1024 * 1024);
|
||||
if (surface == nullptr)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
insertSurfaceSuperChunk(map_ptr, chunk_data, i);
|
||||
map_ptr += 384;
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
dungeons[i] = (unsigned char *)malloc(256 * 256);
|
||||
if (dungeons[i] == nullptr)
|
||||
return false;
|
||||
|
||||
insertDungeonSuperChunk(map_ptr, chunk_data, i);
|
||||
map_ptr += 1536;
|
||||
}
|
||||
|
||||
free(map_data);
|
||||
free(chunk_data);
|
||||
|
||||
if (roof_mode)
|
||||
loadRoofData();
|
||||
|
||||
/* ERIC Useful for testing map wrapping
|
||||
I plan to add a map patch function
|
||||
to allow custom map changes to be
|
||||
loaded easily into nuvie.
|
||||
|
||||
uint16 mx,my;
|
||||
for(my=100;my<130;my++)
|
||||
for(mx=0;mx<30;mx++)
|
||||
surface[my * 1024 + mx] = 1;
|
||||
|
||||
for(my=100;my<130;my++)
|
||||
for(mx=1000;mx<1024;mx++)
|
||||
surface[my * 1024 + mx] = 111;
|
||||
*/
|
||||
/*
|
||||
printf("\n\n\n\n\n\n\n");
|
||||
|
||||
uint16 mx,my;
|
||||
for(my=0;my<1024;my++)
|
||||
{
|
||||
for(mx=0;mx<1024;mx++)
|
||||
{
|
||||
printf("%3d,", surface[my * 1024 + mx]+1);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::has_roof(uint16 x, uint16 y, uint8 level) const {
|
||||
if (!roof_mode || level != 0)
|
||||
return false;
|
||||
|
||||
if (roof_surface[y * 1024 + x] != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Path Map::getRoofDataFilename() const {
|
||||
Std::string game_type, tmp;
|
||||
Common::Path datadir, path, mapfile;
|
||||
|
||||
config->value("config/datadir", tmp, "");
|
||||
config->value("config/GameID", game_type);
|
||||
|
||||
datadir = Common::Path(tmp);
|
||||
build_path(datadir, "maps", path);
|
||||
datadir = path;
|
||||
build_path(datadir, game_type, path);
|
||||
datadir = path;
|
||||
build_path(datadir, "roof_map_00.dat", mapfile);
|
||||
|
||||
return mapfile;
|
||||
}
|
||||
|
||||
Common::Path Map::getRoofTilesetFilename() const {
|
||||
Std::string tmp;
|
||||
Common::Path datadir;
|
||||
Common::Path imagefile;
|
||||
Common::Path path;
|
||||
|
||||
config->value("config/datadir", tmp, "");
|
||||
|
||||
datadir = Common::Path(tmp);
|
||||
build_path(datadir, "images", path);
|
||||
datadir = path;
|
||||
build_path(datadir, "roof_tiles.bmp", imagefile);
|
||||
return imagefile;
|
||||
}
|
||||
|
||||
void Map::set_roof_mode(bool roofs) {
|
||||
roof_mode = roofs;
|
||||
if (roof_mode) {
|
||||
if (roof_surface)
|
||||
return;
|
||||
else
|
||||
loadRoofData();
|
||||
} else {
|
||||
if (roof_surface) {
|
||||
free(roof_surface);
|
||||
roof_surface = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::loadRoofData() {
|
||||
NuvieIOFileRead file;
|
||||
roof_surface = (uint16 *)malloc(1024 * 1024 * 2);
|
||||
|
||||
if (file.open(getRoofDataFilename())) {
|
||||
memset(roof_surface, 0, 1024 * 1024 * 2);
|
||||
uint16 *ptr = roof_surface;
|
||||
while (!file.is_eof()) {
|
||||
uint16 offset = file.read2();
|
||||
ptr += offset;
|
||||
uint8 run_len = file.read1();
|
||||
for (uint8 i = 0; i < run_len; i++) {
|
||||
*ptr = file.read2();
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (roof_surface) {
|
||||
free(roof_surface);
|
||||
roof_surface = nullptr;
|
||||
}
|
||||
roof_mode = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Map::saveRoofData() {
|
||||
NuvieIOFileWrite file;
|
||||
uint32 prev_offset = 0;
|
||||
uint32 cur_offset = 0;
|
||||
uint16 run_length = 0;
|
||||
|
||||
if (roof_surface && file.open(getRoofDataFilename())) {
|
||||
for (; cur_offset < 1048576;) {
|
||||
for (; cur_offset < prev_offset + 65535 && cur_offset < 1048576;) {
|
||||
if (roof_surface[cur_offset] != 0) {
|
||||
file.write2((uint16)(cur_offset - prev_offset));
|
||||
for (run_length = 0; run_length < 256; run_length++) {
|
||||
if (roof_surface[cur_offset + run_length] == 0)
|
||||
break;
|
||||
}
|
||||
if (run_length == 256)
|
||||
run_length--;
|
||||
|
||||
file.write1((uint8)run_length);
|
||||
for (uint8 i = 0; i < run_length; i++) {
|
||||
file.write2(roof_surface[cur_offset + i]);
|
||||
}
|
||||
cur_offset += run_length;
|
||||
break;
|
||||
}
|
||||
|
||||
cur_offset++;
|
||||
if (cur_offset == prev_offset + 65535) {
|
||||
//write blank.
|
||||
file.write2(65535);
|
||||
file.write1(0);
|
||||
}
|
||||
}
|
||||
|
||||
prev_offset = cur_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::insertSurfaceSuperChunk(const unsigned char *schunk, const unsigned char *chunk_data, uint8 schunk_num) {
|
||||
uint16 world_x, world_y;
|
||||
uint16 c1, c2;
|
||||
uint8 i, j;
|
||||
|
||||
world_x = schunk_num % 8;
|
||||
world_y = (schunk_num - world_x) / 8;
|
||||
|
||||
world_x *= 128;
|
||||
world_y *= 128;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
for (j = 0; j < 16; j += 2) {
|
||||
c1 = ((schunk[1] & 0xf) << 8) | schunk[0];
|
||||
c2 = (schunk[2] << 4) | (schunk[1] >> 4);
|
||||
|
||||
insertSurfaceChunk(&chunk_data[c1 * 64], world_x + j * 8, world_y + i * 8);
|
||||
insertSurfaceChunk(&chunk_data[c2 * 64], world_x + (j + 1) * 8, world_y + i * 8);
|
||||
|
||||
schunk += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::insertSurfaceChunk(const unsigned char *chunk, uint16 x, uint16 y) {
|
||||
unsigned char *map_ptr;
|
||||
|
||||
map_ptr = &surface[y * 1024 + x];
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
memcpy(map_ptr, chunk, 8);
|
||||
map_ptr += 1024;
|
||||
chunk += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Map::insertDungeonSuperChunk(const unsigned char *schunk, const unsigned char *chunk_data, uint8 level) {
|
||||
uint16 c1, c2;
|
||||
uint8 i, j;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
for (j = 0; j < 32; j += 2) {
|
||||
c1 = ((schunk[1] & 0xf) << 8) | schunk[0];
|
||||
c2 = (schunk[2] << 4) | (schunk[1] >> 4);
|
||||
|
||||
insertDungeonChunk(&chunk_data[c1 * 64], j * 8, i * 8, level);
|
||||
insertDungeonChunk(&chunk_data[c2 * 64], (j + 1) * 8, i * 8, level);
|
||||
|
||||
schunk += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::insertDungeonChunk(const unsigned char *chunk, uint16 x, uint16 y, uint8 level) {
|
||||
unsigned char *map_ptr;
|
||||
|
||||
map_ptr = &dungeons[level][y * 256 + x];
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
memcpy(map_ptr, chunk, 8);
|
||||
map_ptr += 256;
|
||||
chunk += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Get absolute coordinates for relative destination from MapCoord.
|
||||
*/
|
||||
MapCoord MapCoord::abs_coords(sint16 dx, sint16 dy) const {
|
||||
// uint16 pitch = Map::get_width(z); cannot call function without object
|
||||
uint16 pitch = (z == 0) ? 1024 : 256;
|
||||
dx += x;
|
||||
dy += y;
|
||||
// wrap on map boundary for MD
|
||||
if (dx < 0)
|
||||
dx = pitch + dx;
|
||||
else if (dx >= pitch)
|
||||
dx = pitch - dx;
|
||||
if (dy < 0)
|
||||
dy = 0;
|
||||
else if (dy >= pitch)
|
||||
dy = pitch - 1;
|
||||
return (MapCoord(dx, dy, z));
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if this map coordinate is visible in the game window.
|
||||
*/
|
||||
bool MapCoord::is_visible() const {
|
||||
return (Game::get_game()->get_map_window()->in_window(x, y, z));
|
||||
}
|
||||
|
||||
|
||||
bool Map::testIntersection(int x, int y, uint8 level, uint8 flags, LineTestResult &Result, Obj *excluded_obj) {
|
||||
/* more checks added, may need more testing (SB-X) */
|
||||
#if 0
|
||||
if (flags & LT_HitUnpassable) {
|
||||
if (!is_passable(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitForcedPassable) {
|
||||
if (obj_manager->is_forced_passable(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitActors) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
if (flags & LT_HitUnpassable) {
|
||||
if (!is_passable(x, y, level)) {
|
||||
Obj *obj_hit = obj_manager->get_obj(x, y, level);
|
||||
if (!obj_hit || !excluded_obj || obj_hit != excluded_obj) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitMissileBoundary) {
|
||||
if (is_missile_boundary(x, y, level, excluded_obj)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitForcedPassable) {
|
||||
if (obj_manager->is_forced_passable(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitActors) {
|
||||
if (actor_manager->get_actor(x, y, level)) {
|
||||
Result.init(x, y, level, actor_manager->get_actor(x, y, level), nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & LT_HitLocation) && Result.loc_to_hit) {
|
||||
if (x == Result.loc_to_hit->x && y == Result.loc_to_hit->y) {
|
||||
Result.init(x, y, level, nullptr, nullptr);
|
||||
Result.loc_to_hit->z = level;
|
||||
Result.hitLoc = Result.loc_to_hit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & LT_HitObjects) {
|
||||
if (obj_manager->get_obj(x, y, level)) {
|
||||
Result.init(x, y, level, nullptr, obj_manager->get_obj(x, y, level, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if a line hits something travelling from (start_x, start_y) to
|
||||
// (end_x, end_y). If a hit occurs Result is filled in with the relevant info.
|
||||
// If want_screen_space is true input tile coordinates are multiplied by 16 for
|
||||
// line calculation and scaled back down before testing for collisions. The
|
||||
// original game does this for projectiles.
|
||||
bool Map::lineTest(int start_x, int start_y, int end_x, int end_y, uint8 level,
|
||||
uint8 flags, LineTestResult &Result, uint32 skip, Obj *excluded_obj,
|
||||
bool want_screen_space) {
|
||||
// standard Bresenham's algorithm.
|
||||
uint8 scale_factor_log2 = 0;
|
||||
if (want_screen_space)
|
||||
scale_factor_log2 = 4; // set scale factor to 16
|
||||
int deltax = abs(end_x - start_x) << scale_factor_log2;
|
||||
int deltay = abs(end_y - start_y) << scale_factor_log2;
|
||||
int x = (start_x << scale_factor_log2);
|
||||
int y = (start_y << scale_factor_log2);
|
||||
x += ((1 << scale_factor_log2) >> 1); // start at the center of the tile
|
||||
y += ((1 << scale_factor_log2) >> 1); // when in screen space
|
||||
int d;
|
||||
int xinc1, xinc2;
|
||||
int yinc1, yinc2;
|
||||
int dinc1, dinc2;
|
||||
uint32 count;
|
||||
int xtile = start_x;
|
||||
int ytile = start_y;
|
||||
int xtile_prev = xtile;
|
||||
int ytile_prev = ytile;
|
||||
|
||||
|
||||
if (deltax >= deltay) {
|
||||
d = (deltay << 1) - deltax;
|
||||
|
||||
count = deltax + 1;
|
||||
dinc1 = deltay << 1;
|
||||
dinc2 = (deltay - deltax) << 1;
|
||||
xinc1 = 1;
|
||||
xinc2 = 1;
|
||||
yinc1 = 0;
|
||||
yinc2 = 1;
|
||||
} else {
|
||||
d = (deltax << 1) - deltay;
|
||||
|
||||
count = deltay + 1;
|
||||
dinc1 = deltax << 1;
|
||||
dinc2 = (deltax - deltay) << 1;
|
||||
xinc1 = 0;
|
||||
xinc2 = 1;
|
||||
yinc1 = 1;
|
||||
yinc2 = 1;
|
||||
}
|
||||
|
||||
if (start_x > end_x) {
|
||||
xinc1 = -xinc1;
|
||||
xinc2 = -xinc2;
|
||||
}
|
||||
if (start_y > end_y) {
|
||||
yinc1 = -yinc1;
|
||||
yinc2 = -yinc2;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
// only test for collision if tile coordinates have changed
|
||||
if ((scale_factor_log2 == 0 || x >> scale_factor_log2 != xtile || y >> scale_factor_log2 != ytile)) {
|
||||
xtile_prev = xtile;
|
||||
ytile_prev = ytile;
|
||||
xtile = x >> scale_factor_log2; // scale back down to tile
|
||||
ytile = y >> scale_factor_log2; // space if necessary
|
||||
// test the current location
|
||||
if ((i >= skip) && (testIntersection(xtile, ytile, level, flags, Result, excluded_obj) == true)) {
|
||||
Result.pre_hit_x = xtile_prev;
|
||||
Result.pre_hit_y = ytile_prev;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < 0) {
|
||||
d += dinc1;
|
||||
x += xinc1;
|
||||
y += yinc1;
|
||||
} else {
|
||||
d += dinc2;
|
||||
x += xinc2;
|
||||
y += yinc2;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
215
engines/ultima/nuvie/core/map.h
Normal file
215
engines/ultima/nuvie/core/map.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_MAP_H
|
||||
#define NUVIE_CORE_MAP_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class U6LList;
|
||||
class Actor;
|
||||
class ActorManager;
|
||||
class MapCoord;
|
||||
class TileManager;
|
||||
class Screen;
|
||||
|
||||
#define MAP_ORIGINAL_TILE true
|
||||
|
||||
enum LineTestFlags {
|
||||
LT_HitActors = (1 << 0),
|
||||
LT_HitUnpassable = (1 << 1),
|
||||
LT_HitForcedPassable = (1 << 2),
|
||||
LT_HitLocation = (1 << 3), /* hit location in Result */
|
||||
LT_HitObjects = (1 << 4), /* hit any object */
|
||||
LT_HitMissileBoundary = (1 << 5)
|
||||
};
|
||||
|
||||
class LineTestResult {
|
||||
public:
|
||||
LineTestResult() { // clears properties not set by init() (SB-X)
|
||||
hit_x = 0;
|
||||
hit_y = 0;
|
||||
hit_level = 0;
|
||||
hitActor = nullptr;
|
||||
hitObj = nullptr;
|
||||
hitLoc = nullptr;
|
||||
loc_to_hit = nullptr;
|
||||
}
|
||||
void init(int x, int y, uint8 level, Actor *actorHit, Obj *objHit) {
|
||||
hit_x = x;
|
||||
hit_y = y;
|
||||
hit_level = level;
|
||||
hitActor = actorHit;
|
||||
hitObj = objHit;
|
||||
}
|
||||
|
||||
int hit_x; // x coord where object / actor was hit
|
||||
int hit_y; // y coord where object / actor was hit
|
||||
int pre_hit_x;
|
||||
int pre_hit_y;
|
||||
uint8 hit_level; // map level where object / actor was hit
|
||||
Actor *hitActor;
|
||||
Obj *hitObj;
|
||||
MapCoord *hitLoc;
|
||||
MapCoord *loc_to_hit; // set hitLoc if hit x,y (z changes)
|
||||
};
|
||||
|
||||
//typedef (*LineTestFilter)(int x, int y, int level, LineTestResult &Result);
|
||||
|
||||
/* Map Location with 2D X,Y coordinates and plane (map number)
|
||||
*/
|
||||
class MapCoord {
|
||||
public:
|
||||
union {
|
||||
uint16 x;
|
||||
sint16 sx;
|
||||
};
|
||||
union {
|
||||
uint16 y;
|
||||
sint16 sy;
|
||||
};
|
||||
uint8 z; // plane
|
||||
|
||||
MapCoord(uint16 nx, uint16 ny, uint16 nz = 0) {
|
||||
x = nx;
|
||||
y = ny;
|
||||
z = nz;
|
||||
}
|
||||
MapCoord(Obj *obj) {
|
||||
x = obj->x;
|
||||
y = obj->y;
|
||||
z = obj->z;
|
||||
}
|
||||
MapCoord() : x(0), y(0), z(0) { }
|
||||
|
||||
uint32 xdistance(const MapCoord &c2) const {
|
||||
uint32 dist = abs(c2.x - x);
|
||||
if (dist > 512)
|
||||
dist = 1024 - dist;
|
||||
|
||||
return dist;
|
||||
}
|
||||
uint32 ydistance(const MapCoord &c2) const {
|
||||
return abs(c2.y - y);
|
||||
}
|
||||
// greatest 2D distance X or Y (estimate of shortest)
|
||||
uint32 distance(const MapCoord &c2) const {
|
||||
uint16 dx = xdistance(c2), dy = ydistance(c2);
|
||||
return (dx >= dy ? dx : dy);
|
||||
}
|
||||
// get absolute coordinates for relative destination (dx,dy)
|
||||
MapCoord abs_coords(sint16 dx, sint16 dy) const;
|
||||
// location is on screen?
|
||||
bool is_visible() const;
|
||||
void print_d(DebugLevelType level) const {
|
||||
DEBUG(1, level, "%d, %d, %d", x, y, z);
|
||||
}
|
||||
void print_h(DebugLevelType level) const {
|
||||
DEBUG(1, level, "%x, %x, %x", x, y, z);
|
||||
}
|
||||
void print_s(DebugLevelType level) const {
|
||||
DEBUG(1, level, "%d, %d", sx, sy);
|
||||
}
|
||||
|
||||
bool operator==(const MapCoord &c2) const {
|
||||
return (x == c2.x && y == c2.y && z == c2.z);
|
||||
}
|
||||
bool operator!=(const MapCoord &c2) const {
|
||||
return (!(*this == c2));
|
||||
}
|
||||
// MapCoord operator+(MapCoord &c2) { return(abs_coords(c2)); }
|
||||
};
|
||||
|
||||
|
||||
class Map {
|
||||
const Configuration *config;
|
||||
TileManager *tile_manager;
|
||||
ObjManager *obj_manager;
|
||||
ActorManager *actor_manager;
|
||||
|
||||
uint8 *surface;
|
||||
uint8 *dungeons[5];
|
||||
|
||||
bool roof_mode;
|
||||
uint16 *roof_surface;
|
||||
|
||||
public:
|
||||
|
||||
Map(const Configuration *cfg);
|
||||
~Map();
|
||||
|
||||
void set_actor_manager(ActorManager *am) {
|
||||
actor_manager = am;
|
||||
}
|
||||
Actor *get_actor(uint16 x, uint16 y, uint8 z, bool inc_surrounding_objs = true);
|
||||
|
||||
bool loadMap(TileManager *tm, ObjManager *om);
|
||||
byte *get_map_data(uint8 level);
|
||||
uint16 *get_roof_data(uint8 level);
|
||||
const Tile *get_tile(uint16 x, uint16 y, uint8 level, bool original_tile = false);
|
||||
uint16 get_width(uint8 level) const;
|
||||
bool is_passable(uint16 x, uint16 y, uint8 level);
|
||||
bool is_water(uint16 x, uint16 y, uint16 level, bool ignore_objects = false);
|
||||
bool is_boundary(uint16 x, uint16 y, uint8 level);
|
||||
bool is_missile_boundary(uint16 x, uint16 y, uint8 level, Obj *excluded_obj = nullptr);
|
||||
bool is_damaging(uint16 x, uint16 y, uint8 level, bool ignore_objects = false);
|
||||
bool can_put_obj(uint16 x, uint16 y, uint8 level);
|
||||
bool actor_at_location(uint16 x, uint16 y, uint8 level, bool inc_surrounding_objs = true);
|
||||
uint8 get_impedance(uint16 x, uint16 y, uint8 level, bool ignore_objects = false);
|
||||
const Tile *get_dmg_tile(uint16 x, uint16 y, uint8 level);
|
||||
bool is_passable(uint16 x, uint16 y, uint8 level, NuvieDir dir);
|
||||
bool is_passable(uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint8 level);
|
||||
bool is_passable_from_dir(uint16 x, uint16 y, uint8 level, NuvieDir dir);
|
||||
bool has_roof(uint16 x, uint16 y, uint8 level) const;
|
||||
void set_roof_mode(bool roofs);
|
||||
|
||||
const char *look(uint16 x, uint16 y, uint8 level);
|
||||
|
||||
bool lineTest(int start_x, int start_y, int end_x, int end_y, uint8 level,
|
||||
uint8 flags, LineTestResult &Result, uint32 skip = 0, Obj *excluded_obj = nullptr, bool want_screen_space = false); // excluded_obj only works for LT_HitUnpassable
|
||||
|
||||
bool testIntersection(int x, int y, uint8 level, uint8 flags, LineTestResult &Result, Obj *excluded_obj = nullptr); // excluded_obj only works for LT_HitUnpassable
|
||||
|
||||
void saveRoofData();
|
||||
Common::Path getRoofTilesetFilename() const;
|
||||
|
||||
protected:
|
||||
Common::Path getRoofDataFilename() const;
|
||||
void insertSurfaceSuperChunk(const unsigned char *schunk_ptr, const unsigned char *chunk_data, uint8 schunk_num);
|
||||
void insertSurfaceChunk(const unsigned char *chunk, uint16 x, uint16 y);
|
||||
|
||||
void insertDungeonSuperChunk(const unsigned char *schunk_ptr, const unsigned char *chunk_data, uint8 level);
|
||||
void insertDungeonChunk(const unsigned char *chunk, uint16 x, uint16 y, uint8 level);
|
||||
|
||||
|
||||
void loadRoofData();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
41
engines/ultima/nuvie/core/nuvie_defs.cpp
Normal file
41
engines/ultima/nuvie/core/nuvie_defs.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#ifndef WITHOUT_DEBUG
|
||||
|
||||
void u6debug(bool no_header, const DebugLevelType level, const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Common::String output = Common::String::vformat(format, va);
|
||||
va_end(va);
|
||||
|
||||
::debugN(level, "%s", output.c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
139
engines/ultima/nuvie/core/nuvie_defs.h
Normal file
139
engines/ultima/nuvie/core/nuvie_defs.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_NUVIE_DEFS_H
|
||||
#define NUVIE_CORE_NUVIE_DEFS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "ultima/nuvie/misc/sdl_compat.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
typedef int8 sint8;
|
||||
typedef int16 sint16;
|
||||
typedef int32 sint32;
|
||||
|
||||
#define USE_BUTTON Events::BUTTON_LEFT
|
||||
#define WALK_BUTTON Events::BUTTON_RIGHT
|
||||
#define ACTION_BUTTON Events::BUTTON_RIGHT
|
||||
#define DRAG_BUTTON Events::BUTTON_LEFT
|
||||
|
||||
typedef uint8 nuvie_game_t; // Game type (1=u6,2=md,4=se)
|
||||
|
||||
#define NUVIE_GAME_NONE 0
|
||||
#define NUVIE_GAME_U6 1
|
||||
#define NUVIE_GAME_MD 2
|
||||
#define NUVIE_GAME_SE 4
|
||||
|
||||
#define NUVIE_CONFIG_NAME_U6 "ultima6"
|
||||
#define NUVIE_CONFIG_NAME_MD "martian"
|
||||
#define NUVIE_CONFIG_NAME_SE "savage"
|
||||
|
||||
#define NUVIE_STYLE_ORIG 0
|
||||
#define NUVIE_STYLE_NEW 1
|
||||
#define NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP 2
|
||||
#define NUVIE_STYLE_ORIG_PLUS_FULL_MAP 3
|
||||
|
||||
#define clamp_min(v, c) (((v) < (c)) ? (c) : (v))
|
||||
#define clamp_max(v, c) (((v) > (c)) ? (c) : (v))
|
||||
#define clamp(v, c1, c2) ( ((v) < (c1)) ? (c1) : (((v) > (c2)) ? (c2) : (v)) )
|
||||
|
||||
#ifndef INT_MAX
|
||||
#define INT_MAX 0x7fffffff
|
||||
#endif
|
||||
#ifndef UCHAR_MAX
|
||||
#define UCHAR_MAX 0xff
|
||||
#endif
|
||||
#ifndef SHRT_MAX
|
||||
#define SHRT_MAX 0x7fff
|
||||
#endif
|
||||
|
||||
//FIXME fix for future maps which will probably be 1024 wide starting at level 6..
|
||||
#define WRAPPED_COORD(c,level) ((c)&((level)?255:1023))
|
||||
#define WRAP_COORD(c,level) ((c)&=((level)?255:1023))
|
||||
|
||||
#define MAP_SIDE_LENGTH(map_level) ((map_level > 0 && map_level < 6) ? 256 : 1024)
|
||||
|
||||
/*
|
||||
* on all levels, except level 0 (conveniently 'false') the map pitch is 256.
|
||||
* to properly wrap, mask the coordinate with the relevant bit-mask.
|
||||
* Another way to write this would be:
|
||||
|
||||
const uint16 map_pitch[2] = { 1024, 256 }; // width of 0:surface plane, and 1:all other planes
|
||||
#define WRAPPED_COORD(c,level) ((c)&(map_pitch[(level==0)?0:1]-1)) // mask high bit, wrap C to map_pitch
|
||||
#define WRAP_COORD(c,level) ((c)&=(map_pitch[(level==0)?0:1]-1)) // modifies C
|
||||
*/
|
||||
|
||||
enum NuvieDir {
|
||||
NUVIE_DIR_N = 0,
|
||||
NUVIE_DIR_E = 1,
|
||||
NUVIE_DIR_S = 2,
|
||||
NUVIE_DIR_W = 3,
|
||||
|
||||
NUVIE_DIR_NE = 4,
|
||||
NUVIE_DIR_SE = 5,
|
||||
NUVIE_DIR_SW = 6,
|
||||
NUVIE_DIR_NW = 7,
|
||||
|
||||
NUVIE_DIR_NONE = 8,
|
||||
};
|
||||
|
||||
#define TRAMMEL_PHASE 1.75
|
||||
#define FELUCCA_PHASE 1.1666666666666667
|
||||
|
||||
enum DebugLevelType {
|
||||
LEVEL_EMERGENCY = 0,
|
||||
LEVEL_ALERT,
|
||||
LEVEL_CRITICAL,
|
||||
LEVEL_ERROR,
|
||||
LEVEL_WARNING,
|
||||
LEVEL_NOTIFICATION,
|
||||
LEVEL_INFORMATIONAL,
|
||||
LEVEL_DEBUGGING
|
||||
};
|
||||
|
||||
enum ConverseGumpType {
|
||||
CONVERSE_GUMP_DEFAULT = 0,
|
||||
CONVERSE_GUMP_U7_STYLE = 1,
|
||||
CONVERSE_GUMP_WOU_STYLE = 2,
|
||||
};
|
||||
|
||||
#ifdef WITHOUT_DEBUG
|
||||
inline void u6debug(bool no_header, const DebugLevelType level, const char *format, ...) {}
|
||||
#else
|
||||
extern void u6debug(bool no_header, const DebugLevelType level, const char *format, ...);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
#define DEBUG u6debug
|
||||
|
||||
#define U6PATH_DELIMITER '/'
|
||||
|
||||
#define NUVIE_RAND_MAX 0x7fffffff // POSIX: 2^(31)-1
|
||||
#define NUVIE_RAND() getRandom(NUVIE_RAND_MAX)
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
326
engines/ultima/nuvie/core/obj.cpp
Normal file
326
engines/ultima/nuvie/core/obj.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Obj::Obj() : obj_n(0), status(0), nuvie_status(0), frame_n(0), qty(0),
|
||||
quality(0), parent(nullptr), container(nullptr), x(0), y(0), z(0) {
|
||||
}
|
||||
|
||||
Obj::Obj(Obj *sobj) {
|
||||
memcpy(this, sobj, sizeof(Obj));
|
||||
|
||||
parent = nullptr;
|
||||
container = nullptr;
|
||||
}
|
||||
|
||||
void Obj::make_container() {
|
||||
if (container == nullptr)
|
||||
container = new U6LList();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Obj *Obj::get_container_obj(bool recursive) {
|
||||
Obj *obj = (is_in_container() ? (Obj *)parent : nullptr);
|
||||
|
||||
if (recursive) {
|
||||
while (obj && obj->is_in_container())
|
||||
obj = (Obj *)obj->parent;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Obj::set_on_map(U6LList *map_list) {
|
||||
parent = map_list;
|
||||
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
|
||||
nuvie_status |= OBJ_LOC_MAP;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_in_container(Obj *container_obj) {
|
||||
parent = (void *)container_obj;
|
||||
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
|
||||
nuvie_status |= OBJ_LOC_CONT;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_invisible(bool flag) {
|
||||
if (flag)
|
||||
status |= OBJ_STATUS_INVISIBLE;
|
||||
else if (is_invisible())
|
||||
status ^= OBJ_STATUS_INVISIBLE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_temporary(bool flag) {
|
||||
if (flag)
|
||||
status |= OBJ_STATUS_TEMPORARY;
|
||||
else if (is_temporary())
|
||||
status ^= OBJ_STATUS_TEMPORARY;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_ok_to_take(bool flag, bool recursive) {
|
||||
if (flag)
|
||||
status |= OBJ_STATUS_OK_TO_TAKE;
|
||||
else if (is_ok_to_take())
|
||||
status ^= OBJ_STATUS_OK_TO_TAKE;
|
||||
|
||||
if (recursive && container) {
|
||||
for (U6Link *link = container->start(); link != nullptr; link = link->next) {
|
||||
Obj *obj = (Obj *)link->data;
|
||||
obj->set_ok_to_take(flag, recursive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Obj::set_in_inventory() {
|
||||
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
|
||||
nuvie_status |= OBJ_LOC_INV;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::readied() { //set_readied() ??
|
||||
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
|
||||
nuvie_status |= OBJ_LOC_READIED;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_noloc() {
|
||||
parent = nullptr;
|
||||
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET; //clear location bits 0 = no loc
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_in_script(bool flag) {
|
||||
if (flag)
|
||||
nuvie_status |= NUVIE_OBJ_STATUS_SCRIPTING;
|
||||
else if (is_script_obj())
|
||||
nuvie_status ^= NUVIE_OBJ_STATUS_SCRIPTING;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::set_actor_obj(bool flag) {
|
||||
if (flag)
|
||||
nuvie_status |= NUVIE_OBJ_STATUS_ACTOR_OBJ;
|
||||
else if (is_actor_obj())
|
||||
nuvie_status ^= NUVIE_OBJ_STATUS_ACTOR_OBJ;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Returns true if an object is in an actor inventory, including containers and readied items. */
|
||||
|
||||
bool Obj::is_in_inventory(bool check_parent) const {
|
||||
switch (get_engine_loc()) {
|
||||
case OBJ_LOC_INV :
|
||||
case OBJ_LOC_READIED :
|
||||
return true;
|
||||
case OBJ_LOC_CONT :
|
||||
if (check_parent)
|
||||
return ((Obj *)parent)->is_in_inventory(check_parent);
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 Obj::get_engine_loc() const {
|
||||
return (nuvie_status & NUVIE_OBJ_STATUS_LOC_MASK_GET);
|
||||
}
|
||||
|
||||
Actor *Obj::get_actor_holding_obj() {
|
||||
switch (get_engine_loc()) {
|
||||
case OBJ_LOC_INV :
|
||||
case OBJ_LOC_READIED :
|
||||
return (Actor *)this->parent;
|
||||
|
||||
case OBJ_LOC_CONT :
|
||||
return ((Obj *)parent)->get_actor_holding_obj();
|
||||
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//Add child object into container, stacking if required
|
||||
void Obj::add(Obj *obj, bool stack, bool addAtTail) {
|
||||
if (container == nullptr)
|
||||
make_container();
|
||||
|
||||
if (stack && Game::get_game()->get_obj_manager()->is_stackable(obj))
|
||||
add_and_stack(obj, addAtTail);
|
||||
else
|
||||
if (!addAtTail)
|
||||
container->addAtPos(0, obj);
|
||||
else
|
||||
container->add(obj);
|
||||
|
||||
obj->set_in_container(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Obj::add_and_stack(Obj *obj, bool addAtTail) {
|
||||
U6Link *link;
|
||||
Obj *cont_obj;
|
||||
|
||||
//should we recurse through nested containers?
|
||||
for (link = container->start(); link != nullptr;) {
|
||||
cont_obj = (Obj *)link->data;
|
||||
link = link->next;
|
||||
//match on obj_n, frame_n and quality.
|
||||
if (obj->obj_n == cont_obj->obj_n && obj->frame_n == cont_obj->frame_n && obj->quality == cont_obj->quality) {
|
||||
obj->qty += cont_obj->qty;
|
||||
container->replace(cont_obj, obj); //replace cont_obj with obj in container list. should we do this to link->data directly?
|
||||
delete_obj(cont_obj);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!addAtTail)
|
||||
container->addAtPos(0, obj); // add the object as we couldn't find another object to stack with.
|
||||
else
|
||||
container->add(obj);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//Remove child object from container.
|
||||
bool Obj::remove(Obj *obj) {
|
||||
if (container == nullptr)
|
||||
return false;
|
||||
|
||||
if (container->remove(obj) == false)
|
||||
return false;
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_SE) {
|
||||
if (obj_n == OBJ_SE_JAR)
|
||||
frame_n = 0; // empty jar frame
|
||||
}
|
||||
obj->x = 0;
|
||||
obj->y = 0;
|
||||
obj->z = 0;
|
||||
|
||||
obj->set_noloc();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Obj *Obj::find_in_container(uint16 objN, uint8 quality_, bool match_quality, uint8 frameN, bool match_frame_n, Obj **prev_obj) const {
|
||||
U6Link *link;
|
||||
Obj *obj;
|
||||
|
||||
if (container == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (link = container->start(); link != nullptr; link = link->next) {
|
||||
obj = (Obj *)link->data;
|
||||
if (obj) {
|
||||
if (obj->obj_n == objN && (match_quality == false || obj->quality == quality_) && (match_frame_n == false || obj->frame_n == frameN)) {
|
||||
if (prev_obj != nullptr && obj == *prev_obj)
|
||||
prev_obj = nullptr;
|
||||
else {
|
||||
if (prev_obj == nullptr || *prev_obj == nullptr)
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->container) {
|
||||
obj = obj->find_in_container(objN, quality_, match_quality, frameN, match_frame_n, prev_obj);
|
||||
if (obj)
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 Obj::get_total_qty(uint16 match_obj_n) {
|
||||
U6Link *link;
|
||||
Obj *obj;
|
||||
uint16 total_qty = 0;
|
||||
|
||||
if (obj_n == match_obj_n) {
|
||||
if (qty == 0)
|
||||
total_qty += 1;
|
||||
else
|
||||
total_qty += qty;
|
||||
}
|
||||
|
||||
if (container != nullptr) {
|
||||
for (link = container->start(); link != nullptr; link = link->next) {
|
||||
obj = (Obj *)link->data;
|
||||
if (obj) {
|
||||
if (obj->container)
|
||||
total_qty += obj->get_total_qty(match_obj_n);
|
||||
else if (obj->obj_n == match_obj_n) {
|
||||
if (obj->qty == 0)
|
||||
total_qty += 1;
|
||||
else
|
||||
total_qty += obj->qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_qty;
|
||||
}
|
||||
|
||||
uint32 Obj::container_count_objects() const {
|
||||
uint32 count = 0;
|
||||
U6Link *link;
|
||||
|
||||
if (container != nullptr) {
|
||||
for (link = container->start(); link != nullptr; link = link->next) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool Obj::is_ok_to_take() const {
|
||||
return ((status & OBJ_STATUS_OK_TO_TAKE) || Game::get_game()->using_hackmove());
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
193
engines/ultima/nuvie/core/obj.h
Normal file
193
engines/ultima/nuvie/core/obj.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_OBJ_H
|
||||
#define NUVIE_CORE_OBJ_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/misc/u6_llist.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Actor;
|
||||
|
||||
#define NO_OBJ_STATUS 0
|
||||
|
||||
// obj status bit flags
|
||||
#define OBJ_STATUS_OK_TO_TAKE 0x1
|
||||
//#define OBJ_STATUS_SEEN_EGG 0x2 // something to do with eggs <- not sure about this one.
|
||||
#define OBJ_STATUS_INVISIBLE 0x2 // I think this is correct
|
||||
#define OBJ_STATUS_CHARMED 0x4 // objlist.txt says 'charmed'
|
||||
|
||||
|
||||
// position: A 2 bit field, so can't use plain | to check / |= to set these.
|
||||
// FIXME: check to make sure we don't do this anywhere anymore
|
||||
#define OBJ_STATUS_ON_MAP 0x0
|
||||
#define OBJ_STATUS_IN_CONTAINER 0x8
|
||||
#define OBJ_STATUS_IN_INVENTORY 0x10
|
||||
#define OBJ_STATUS_READIED 0x18
|
||||
#define OBJ_STATUS_MASK_GET 0x18
|
||||
#define OBJ_STATUS_MASK_SET 0xE7
|
||||
|
||||
#define OBJ_STATUS_TEMPORARY 0x20
|
||||
#define OBJ_STATUS_EGG_ACTIVE 0x40 // something to do with eggs
|
||||
#define OBJ_STATUS_BROKEN 0x40
|
||||
#define OBJ_STATUS_MUTANT 0x40
|
||||
#define OBJ_STATUS_CURSED 0x40
|
||||
#define OBJ_STATUS_LIT 0x80
|
||||
|
||||
|
||||
//first 3 bits of nuvie_status code object location
|
||||
//in the nuvie engine.
|
||||
|
||||
//Nuvie engine obj locations
|
||||
#define OBJ_LOC_NONE 0
|
||||
#define OBJ_LOC_INV 1
|
||||
#define OBJ_LOC_MAP 2
|
||||
#define OBJ_LOC_READIED 3
|
||||
#define OBJ_LOC_CONT 4
|
||||
|
||||
#define NUVIE_OBJ_STATUS_LOC_MASK_GET 0x7
|
||||
#define NUVIE_OBJ_STATUS_LOC_MASK_SET 0xf8
|
||||
|
||||
#define NUVIE_OBJ_STATUS_SCRIPTING 0x8
|
||||
#define NUVIE_OBJ_STATUS_ACTOR_OBJ 0x10
|
||||
|
||||
#define OBJ_MATCH_QUALITY true
|
||||
#define OBJ_NOMATCH_QUALITY false
|
||||
|
||||
#define OBJ_MATCH_FRAME_N true
|
||||
#define OBJ_NOMATCH_FRAME_N false
|
||||
|
||||
//We use this in Obj::is_in_inventory()
|
||||
#define OBJ_DONT_CHECK_PARENT false
|
||||
|
||||
class Obj {
|
||||
uint8 nuvie_status;
|
||||
|
||||
public:
|
||||
//uint16 objblk_n;
|
||||
|
||||
uint16 obj_n;
|
||||
uint8 frame_n;
|
||||
uint8 status;
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint8 z;
|
||||
|
||||
uint16 qty;
|
||||
uint8 quality;
|
||||
void *parent; //either an Obj pointer or an Actor pointer depending on engine_loc.
|
||||
U6LList *container;
|
||||
|
||||
public:
|
||||
Obj();
|
||||
Obj(Obj *sobj);
|
||||
|
||||
bool is_script_obj() const {
|
||||
return (nuvie_status & NUVIE_OBJ_STATUS_SCRIPTING);
|
||||
}
|
||||
bool is_actor_obj() const {
|
||||
return (nuvie_status & NUVIE_OBJ_STATUS_ACTOR_OBJ);
|
||||
}
|
||||
|
||||
bool is_ok_to_take() const;
|
||||
bool is_invisible() const {
|
||||
return (status & OBJ_STATUS_INVISIBLE);
|
||||
}
|
||||
bool is_charmed() const {
|
||||
return (status & OBJ_STATUS_CHARMED);
|
||||
}
|
||||
bool is_temporary() const {
|
||||
return (status & OBJ_STATUS_TEMPORARY);
|
||||
}
|
||||
bool is_egg_active() const {
|
||||
return (status & OBJ_STATUS_EGG_ACTIVE);
|
||||
}
|
||||
bool is_broken() const {
|
||||
return (status & OBJ_STATUS_BROKEN);
|
||||
}
|
||||
bool is_mutant() const {
|
||||
return (status & OBJ_STATUS_MUTANT);
|
||||
}
|
||||
bool is_cursed() const {
|
||||
return (status & OBJ_STATUS_CURSED);
|
||||
}
|
||||
bool is_lit() const {
|
||||
return (status & OBJ_STATUS_LIT);
|
||||
}
|
||||
|
||||
bool is_on_map() const {
|
||||
return ((nuvie_status & NUVIE_OBJ_STATUS_LOC_MASK_GET) == OBJ_LOC_MAP);
|
||||
}
|
||||
bool is_in_container() const {
|
||||
return ((nuvie_status & NUVIE_OBJ_STATUS_LOC_MASK_GET) == OBJ_LOC_CONT);
|
||||
}
|
||||
bool is_in_inventory(bool check_parent = true) const;
|
||||
|
||||
bool is_readied() const {
|
||||
return ((nuvie_status & NUVIE_OBJ_STATUS_LOC_MASK_GET) == OBJ_LOC_READIED);
|
||||
}
|
||||
|
||||
bool has_container() const {
|
||||
return (container != nullptr);
|
||||
}
|
||||
void make_container();
|
||||
Obj *get_container_obj(bool recursive = false);
|
||||
uint32 container_count_objects() const;
|
||||
|
||||
uint8 get_engine_loc() const;
|
||||
Actor *get_actor_holding_obj();
|
||||
|
||||
void set_on_map(U6LList *map_list);
|
||||
void set_in_container(Obj *container_obj);
|
||||
void set_in_inventory();
|
||||
|
||||
void set_invisible(bool flag);
|
||||
|
||||
void set_temporary(bool flag = true);
|
||||
|
||||
void set_ok_to_take(bool flag, bool recursive = false);
|
||||
|
||||
void readied();
|
||||
void set_noloc();
|
||||
void set_in_script(bool flag);
|
||||
void set_actor_obj(bool flag);
|
||||
|
||||
void add(Obj *obj, bool stack = false, bool addAtTail = false);
|
||||
|
||||
bool remove(Obj *obj);
|
||||
|
||||
Obj *find_in_container(uint16 obj_n, uint8 quality, bool match_quality = OBJ_MATCH_QUALITY, uint8 frame_n = 0, bool match_frame_n = OBJ_NOMATCH_FRAME_N, Obj **prev_obj = nullptr) const;
|
||||
|
||||
uint32 get_total_qty(uint16 match_obj_n);
|
||||
|
||||
protected:
|
||||
|
||||
void add_and_stack(Obj *obj, bool addAtTail = false);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
2075
engines/ultima/nuvie/core/obj_manager.cpp
Normal file
2075
engines/ultima/nuvie/core/obj_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
251
engines/ultima/nuvie/core/obj_manager.h
Normal file
251
engines/ultima/nuvie/core/obj_manager.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_OBJ_MANAGER_H
|
||||
#define NUVIE_CORE_OBJ_MANAGER_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/nuvie/misc/iavl_tree.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
#include "ultima/nuvie/misc/u6_llist.h"
|
||||
#include "ultima/nuvie/core/obj.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
//class U6LList;
|
||||
class Configuration;
|
||||
class EggManager;
|
||||
class UseCode;
|
||||
class NuvieIO;
|
||||
class MapCoord;
|
||||
class Actor;
|
||||
|
||||
//is_passable return codes
|
||||
#define OBJ_NO_OBJ 0
|
||||
#define OBJ_NOT_PASSABLE 1
|
||||
#define OBJ_PASSABLE 2
|
||||
|
||||
#define OBJ_WEIGHT_INCLUDE_CONTAINER_ITEMS true
|
||||
#define OBJ_WEIGHT_EXCLUDE_CONTAINER_ITEMS false
|
||||
|
||||
#define OBJ_WEIGHT_DO_SCALE true
|
||||
#define OBJ_WEIGHT_DONT_SCALE false
|
||||
|
||||
#define OBJ_WEIGHT_EXCLUDE_QTY false
|
||||
|
||||
#define OBJ_ADD_TOP true
|
||||
|
||||
#define OBJ_SHOW_PREFIX true
|
||||
|
||||
#define OBJ_TEMP_INIT 255 // this is used to stop temporary objects from being cleaned upon startup.
|
||||
|
||||
#define OBJ_SEARCH_TOP true
|
||||
#define OBJ_INCLUDE_IGNORED true
|
||||
#define OBJ_EXCLUDE_IGNORED false
|
||||
|
||||
struct ObjTreeNode {
|
||||
iAVLKey key;
|
||||
U6LList *obj_list;
|
||||
};
|
||||
|
||||
Obj *new_obj(uint16 obj_n, uint8 frame_n, uint16 x, uint16 y, uint16 z);
|
||||
void delete_obj(Obj *obj);
|
||||
|
||||
void clean_obj_tree_node(void *node);
|
||||
|
||||
class ObjManager {
|
||||
const Configuration *config;
|
||||
int game_type;
|
||||
EggManager *egg_manager;
|
||||
TileManager *tile_manager;
|
||||
//chunk object trees.
|
||||
iAVLTree *surface[64];
|
||||
iAVLTree *dungeon[5];
|
||||
|
||||
uint16 obj_to_tile[1024]; //maps object number (index) to tile number.
|
||||
uint8 obj_weight[1024];
|
||||
uint8 obj_stackable[1024];
|
||||
U6LList *actor_inventories[256];
|
||||
|
||||
bool show_eggs;
|
||||
uint16 egg_tile_num;
|
||||
|
||||
UseCode *usecode;
|
||||
|
||||
Std::list<Obj *> temp_obj_list;
|
||||
Std::list<Obj *> tile_obj_list; // SE single instance 'map tile' objects
|
||||
uint16 last_obj_blk_x, last_obj_blk_y;
|
||||
uint8 last_obj_blk_z;
|
||||
|
||||
uint16 obj_save_count;
|
||||
|
||||
bool custom_actor_tiles;
|
||||
|
||||
public:
|
||||
|
||||
ObjManager(const Configuration *cfg, TileManager *tm, EggManager *em);
|
||||
~ObjManager();
|
||||
|
||||
bool use_custom_actor_tiles() {
|
||||
return custom_actor_tiles;
|
||||
}
|
||||
bool is_showing_eggs() {
|
||||
return show_eggs;
|
||||
}
|
||||
void set_show_eggs(bool value) {
|
||||
show_eggs = value;
|
||||
}
|
||||
|
||||
bool loadObjs();
|
||||
bool load_super_chunk(NuvieIO *chunk_buf, uint8 level, uint8 chunk_offset);
|
||||
void startObjs();
|
||||
void clean();
|
||||
void clean_actor_inventories();
|
||||
|
||||
bool save_super_chunk(NuvieIO *save_buf, uint8 level, uint8 chunk_offset);
|
||||
bool save_eggs(NuvieIO *save_buf);
|
||||
bool save_inventories(NuvieIO *save_buf);
|
||||
bool save_obj(NuvieIO *save_buf, Obj *obj, uint16 parent_objblk_n);
|
||||
|
||||
void set_usecode(UseCode *uc) {
|
||||
usecode = uc;
|
||||
}
|
||||
UseCode *get_usecode() {
|
||||
return usecode;
|
||||
}
|
||||
EggManager *get_egg_manager() {
|
||||
return egg_manager;
|
||||
}
|
||||
|
||||
//U6LList *get_obj_superchunk(uint16 x, uint16 y, uint8 level);
|
||||
bool is_boundary(uint16 x, uint16 y, uint8 level, uint8 boundary_type = TILEFLAG_BOUNDARY, Obj *excluded_obj = nullptr);
|
||||
//bool is_door(Obj * obj);
|
||||
bool is_damaging(uint16 x, uint16 y, uint8 level);
|
||||
uint8 is_passable(uint16 x, uint16 y, uint8 level);
|
||||
bool is_forced_passable(uint16 x, uint16 y, uint8 level);
|
||||
bool is_stackable(const Obj *obj) const;
|
||||
bool is_breakable(const Obj *obj);
|
||||
bool can_store_obj(const Obj *target, Obj *src) const; // Bag, open chest, spellbook.
|
||||
bool can_get_obj(Obj *obj) const;
|
||||
bool has_reduced_weight(uint16 obj_n) const;
|
||||
bool has_reduced_weight(const Obj *obj) const {
|
||||
return has_reduced_weight(obj->obj_n);
|
||||
}
|
||||
bool has_toptile(const Obj *obj) const;
|
||||
bool obj_is_damaging(const Obj *obj, Actor *actor = nullptr); // if actor, it will damage and display text
|
||||
bool is_door(uint16 x, uint16 y, uint8 level);
|
||||
|
||||
U6LList *get_obj_list(uint16 x, uint16 y, uint8 level) const;
|
||||
|
||||
Tile *get_obj_tile(uint16 obj_n, uint8 frame_n);
|
||||
const Tile *get_obj_tile(uint16 x, uint16 y, uint8 level, bool top_obj = true);
|
||||
const Tile *get_obj_dmg_tile(uint16 x, uint16 y, uint8 level);
|
||||
Obj *get_obj(uint16 x, uint16 y, uint8 level, bool top_obj = OBJ_SEARCH_TOP, bool include_ignored_objects = OBJ_EXCLUDE_IGNORED, Obj *excluded_obj = nullptr);
|
||||
Obj *get_obj_of_type_from_location_inc_multi_tile(uint16 obj_n, uint16 x, uint16 y, uint8 z);
|
||||
Obj *get_obj_of_type_from_location_inc_multi_tile(uint16 obj_n, sint16 quality, sint32 qty, uint16 x, uint16 y, uint8 z);
|
||||
Obj *get_obj_of_type_from_location(uint16 obj_n, uint16 x, uint16 y, uint8 z);
|
||||
Obj *get_obj_of_type_from_location(uint16 obj_n, sint16 quality, sint32 qty, uint16 x, uint16 y, uint8 z);
|
||||
Obj *get_objBasedAt(uint16 x, uint16 y, uint8 level, bool top_obj, bool include_ignored_objects = true, Obj *excluded_obj = nullptr);
|
||||
Obj *get_tile_obj(uint16 obj_n);
|
||||
|
||||
uint16 get_obj_tile_num(uint16 obj_num) const;
|
||||
inline bool is_corpse(const Obj *obj) const;
|
||||
uint16 get_obj_tile_num(const Obj *obj) const;
|
||||
void set_obj_tile_num(uint16 obj_num, uint16 tile_num);
|
||||
|
||||
U6LList *get_actor_inventory(uint16 actor_num);
|
||||
bool actor_has_inventory(uint16 actor_num);
|
||||
|
||||
Obj *find_next_obj(uint8 level, Obj *prev_obj, bool match_frame_n = OBJ_NOMATCH_FRAME_N, bool match_quality = OBJ_MATCH_QUALITY);
|
||||
Obj *find_obj(uint8 level, uint16 obj_n, uint8 quality, bool match_quality = OBJ_MATCH_QUALITY, uint16 frame_n = 0, bool match_frame_n = OBJ_NOMATCH_FRAME_N, Obj **prev_obj = nullptr);
|
||||
|
||||
bool move(Obj *obj, uint16 x, uint16 y, uint8 level);
|
||||
bool add_obj(Obj *obj, bool addOnTop = false);
|
||||
bool remove_obj_from_map(Obj *obj);
|
||||
bool remove_obj_type_from_location(uint16 obj_n, uint16 x, uint16 y, uint8 z);
|
||||
|
||||
|
||||
Obj *copy_obj(const Obj *obj);
|
||||
const char *look_obj(Obj *obj, bool show_prefix = false);
|
||||
Obj *get_obj_from_stack(Obj *obj, uint32 count);
|
||||
|
||||
bool list_add_obj(U6LList *list, Obj *obj, bool stack_objects = true, uint32 pos = 0);
|
||||
|
||||
const char *get_obj_name(Obj *obj);
|
||||
const char *get_obj_name(uint16 obj_n);
|
||||
const char *get_obj_name(uint16 obj_n, uint8 frame_n);
|
||||
|
||||
float get_obj_weight(const Obj *obj, bool include_container_items = OBJ_WEIGHT_INCLUDE_CONTAINER_ITEMS, bool scale = true, bool include_qty = true) const;
|
||||
uint8 get_obj_weight_unscaled(uint16 obj_n) const {
|
||||
return obj_weight[obj_n];
|
||||
}
|
||||
float get_obj_weight(uint16 obj_n) const;
|
||||
|
||||
void animate_forwards(Obj *obj, uint32 loop_count = 1);
|
||||
void animate_backwards(Obj *obj, uint32 loop_count = 1);
|
||||
|
||||
void update(uint16 x, uint16 y, uint8 z, bool teleport = false);
|
||||
|
||||
bool unlink_from_engine(Obj *obj, bool run_usecode = true);
|
||||
|
||||
bool moveto_map(Obj *obj, MapCoord location);
|
||||
bool moveto_inventory(Obj *obj, uint16 actor_num);
|
||||
bool moveto_inventory(Obj *obj, Actor *actor);
|
||||
bool moveto_container(Obj *obj, Obj *container_obj, bool stack = true);
|
||||
|
||||
protected:
|
||||
|
||||
void remove_obj(Obj *obj);
|
||||
|
||||
bool load_basetile();
|
||||
bool load_weight_table();
|
||||
|
||||
|
||||
bool addObjToContainer(U6LList *list, Obj *obj);
|
||||
Obj *loadObj(NuvieIO *buf);
|
||||
iAVLTree *get_obj_tree(uint16 x, uint16 y, uint8 level) const;
|
||||
|
||||
iAVLKey get_obj_tree_key(Obj *obj) const;
|
||||
iAVLKey get_obj_tree_key(uint16 x, uint16 y, uint8 level) const;
|
||||
//inline U6LList *ObjManager::get_schunk_list(uint16 x, uint16 y, uint8 level);
|
||||
|
||||
bool temp_obj_list_add(Obj *obj);
|
||||
bool temp_obj_list_remove(Obj *obj);
|
||||
void temp_obj_list_clean_level(uint8 z);
|
||||
void temp_obj_list_clean_area(uint16 x, uint16 y);
|
||||
|
||||
void remove_temp_obj(Obj *tmp_obj);
|
||||
|
||||
inline Obj *find_obj_in_tree(uint16 obj_n, uint8 quality, bool match_quality, uint8 frame_n, bool match_frame_n, Obj **prev_obj, iAVLTree *obj_tree);
|
||||
inline void start_obj_usecode(iAVLTree *obj_tree);
|
||||
inline void print_egg_tree(iAVLTree *obj_tree);
|
||||
|
||||
public:
|
||||
void print_object_list();
|
||||
void print_egg_list();
|
||||
void print_obj(const Obj *obj, bool in_container, uint8 indent = 0);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
999
engines/ultima/nuvie/core/party.cpp
Normal file
999
engines/ultima/nuvie/core/party.cpp
Normal file
@@ -0,0 +1,999 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/converse.h"
|
||||
#include "ultima/nuvie/core/timed_event.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/sound/sound_manager.h"
|
||||
#include "ultima/nuvie/views/view_manager.h"
|
||||
#include "ultima/nuvie/core/player.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/usecode/u6_usecode.h"
|
||||
#include "ultima/nuvie/gui/widgets/command_bar.h"
|
||||
#include "ultima/nuvie/pathfinder/party_path_finder.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/views/view.h"
|
||||
#include "ultima/nuvie/save/obj_list.h"
|
||||
#include "ultima/nuvie/core/events.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Party::Party(const Configuration *cfg) : config(cfg), game(nullptr),
|
||||
actor_manager(nullptr), map(nullptr), pathfinder(nullptr),
|
||||
rest_campfire(nullptr), formation(PARTY_FORM_STANDARD),
|
||||
num_in_party(0), prev_leader_x(0), prev_leader_y(0),
|
||||
defer_removing_dead_members(false), autowalk(false),
|
||||
in_vehicle(false), in_combat_mode(false), lightsources(0),
|
||||
combat_changes_music(false), vehicles_change_music(false) {
|
||||
memset(&member, 0, sizeof member);
|
||||
}
|
||||
|
||||
Party::~Party() {
|
||||
if (pathfinder)
|
||||
delete pathfinder;
|
||||
}
|
||||
|
||||
bool Party::init(Game *g, ActorManager *am) {
|
||||
Std::string formation_string;
|
||||
|
||||
game = g;
|
||||
actor_manager = am;
|
||||
map = g->get_game_map();
|
||||
if (!pathfinder)
|
||||
pathfinder = new PartyPathFinder(this);
|
||||
|
||||
autowalk = false;
|
||||
in_vehicle = false;
|
||||
|
||||
config->value("config/general/party_formation", formation_string, "");
|
||||
if (formation_string == "row")
|
||||
formation = PARTY_FORM_ROW;
|
||||
else if (formation_string == "column")
|
||||
formation = PARTY_FORM_COLUMN;
|
||||
else if (formation_string == "delta")
|
||||
formation = PARTY_FORM_DELTA;
|
||||
else
|
||||
formation = PARTY_FORM_STANDARD;
|
||||
|
||||
config->value("config/audio/combat_changes_music", combat_changes_music, true);
|
||||
config->value("config/audio/vehicles_change_music", vehicles_change_music, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Party::load(NuvieIO *objlist) {
|
||||
uint8 actor_num;
|
||||
uint16 i;
|
||||
|
||||
autowalk = false;
|
||||
in_vehicle = false;
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_NUM_IN_PARTY);
|
||||
num_in_party = objlist->read1();
|
||||
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_PARTY_NAMES);
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
objlist->readToBuf((unsigned char *)member[i].name, PARTY_NAME_MAX_LENGTH + 1); // read in Player name.
|
||||
}
|
||||
objlist->seek(OBJLIST_OFFSET_PARTY_ROSTER);
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
actor_num = objlist->read1();
|
||||
member[i].actor = actor_manager->get_actor(actor_num);
|
||||
member[i].actor->set_in_party(true);
|
||||
//member[i].inactive = false; // false unless actor is asleep, or paralyzed (is_immobile)
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_COMBAT_MODE); // combat mode flag. NOTE! this offset is probably U6 specifix FIXME
|
||||
in_combat_mode = (bool)objlist->read1();
|
||||
|
||||
MapCoord leader_loc = get_leader_location(); // previous leader location
|
||||
prev_leader_x = leader_loc.x;
|
||||
prev_leader_y = leader_loc.y;
|
||||
|
||||
reform_party();
|
||||
|
||||
autowalk = false;
|
||||
|
||||
if (actor_manager->get_actor(ACTOR_VEHICLE_ID_N)->get_worktype() == ACTOR_WT_PLAYER) { // WT_U6_PLAYER
|
||||
set_in_vehicle(true);
|
||||
hide();
|
||||
}
|
||||
|
||||
for (i = 0; i < PARTY_MAX_MEMBERS; i++) {
|
||||
clear_combat_target(i);
|
||||
}
|
||||
|
||||
update_light_sources();
|
||||
update_music();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Party::save(NuvieIO *objlist) {
|
||||
uint16 i;
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_NUM_IN_PARTY);
|
||||
objlist->write1(num_in_party);
|
||||
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_PARTY_NAMES);
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
objlist->writeBuf((unsigned char *)member[i].name, PARTY_NAME_MAX_LENGTH + 1);
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_PARTY_ROSTER);
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
objlist->write1(member[i].actor->get_actor_num());
|
||||
}
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_COMBAT_MODE); // combat mode flag. NOTE! this offset is probably U6 specifix FIXME
|
||||
objlist->write1((uint8)in_combat_mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Party::add_actor(Actor *actor) {
|
||||
Converse *converse = game->get_converse();
|
||||
|
||||
if (num_in_party < PARTY_MAX_MEMBERS) {
|
||||
actor->set_in_party(true);
|
||||
member[num_in_party].actor = actor;
|
||||
//member[num_in_party].inactive = false;
|
||||
strncpy(member[num_in_party].name, converse->npc_name(actor->id_n), PARTY_NAME_MAX_LENGTH + 1);
|
||||
member[num_in_party].name[PARTY_NAME_MAX_LENGTH] = '\0'; // make sure name is terminated
|
||||
member[num_in_party].combat_position = 0;
|
||||
// member[num_in_party].leader_x = member[0].actor->get_location().x;
|
||||
// member[num_in_party].leader_y = member[0].actor->get_location().y;
|
||||
|
||||
num_in_party++;
|
||||
reform_party();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// remove actor from member array shuffle remaining actors down if required.
|
||||
bool Party::remove_actor(Actor *actor, bool keep_party_flag) {
|
||||
if (defer_removing_dead_members) //we don't want to remove member while inside the Party::follow() method.
|
||||
return true;
|
||||
Game::get_game()->get_event()->set_control_cheat(false);
|
||||
|
||||
for (int i = 0; i < num_in_party; i++) {
|
||||
if (member[i].actor->id_n == actor->id_n) {
|
||||
if (keep_party_flag == false) {
|
||||
for (int j = 0; j < member[i].actor->get_num_light_sources(); j++)
|
||||
subtract_light_source();
|
||||
member[i].actor->set_in_party(false);
|
||||
}
|
||||
if (i != (num_in_party - 1)) {
|
||||
for (; i + 1 < num_in_party; i++)
|
||||
member[i] = member[i + 1];
|
||||
}
|
||||
num_in_party--;
|
||||
|
||||
reform_party();
|
||||
if (game->is_new_style()) {
|
||||
Game::get_game()->get_event()->close_gumps();
|
||||
return true;
|
||||
}
|
||||
//FIXME this is a bit hacky we need a better way to refresh views when things change
|
||||
//maybe using callbacks.
|
||||
//If the last actor dies and was being displayed in a view then we need to set the
|
||||
//view to the new last party member.
|
||||
View *cur_view = Game::get_game()->get_view_manager()->get_current_view();
|
||||
if (cur_view && cur_view->get_party_member_num() >= num_in_party)
|
||||
cur_view->set_party_member(num_in_party - 1);
|
||||
else if (cur_view)
|
||||
cur_view->set_party_member(cur_view->get_party_member_num()); // needed if a middle party member dies
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Party::remove_dead_actor(Actor *actor) {
|
||||
return remove_actor(actor, PARTY_KEEP_PARTY_FLAG);
|
||||
}
|
||||
|
||||
bool Party::resurrect_dead_members() {
|
||||
uint16 i;
|
||||
Actor *actor;
|
||||
MapCoord new_pos = get_leader_location();
|
||||
if (Game::get_game()->get_event()->using_control_cheat()) {
|
||||
Game::get_game()->get_event()->set_control_cheat(false);
|
||||
if (!Game::get_game()->is_new_style()) {
|
||||
Game::get_game()->get_view_manager()->set_inventory_mode();
|
||||
Game::get_game()->get_view_manager()->get_current_view()->set_party_member(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ACTORMANAGER_MAX_ACTORS; i++) {
|
||||
actor = actor_manager->get_actor(i);
|
||||
if (actor->is_in_party() && actor->is_alive() == false)
|
||||
actor->resurrect(new_pos);
|
||||
}
|
||||
update_light_sources(); // should only be needed for control cheat but it won't hurt to recheck
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Party::split_gold() {
|
||||
}
|
||||
|
||||
void Party::gather_gold() {
|
||||
}
|
||||
|
||||
uint8 Party::get_party_size() {
|
||||
return num_in_party;
|
||||
}
|
||||
|
||||
Actor *Party::get_leader_actor() const {
|
||||
sint8 leader = get_leader();
|
||||
if (leader < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return get_actor(leader);
|
||||
}
|
||||
|
||||
Actor *Party::get_actor(uint8 member_num) const {
|
||||
if (num_in_party <= member_num)
|
||||
return nullptr;
|
||||
|
||||
return member[member_num].actor;
|
||||
}
|
||||
|
||||
const char *Party::get_actor_name(uint8 member_num) const {
|
||||
if (num_in_party <= member_num)
|
||||
return nullptr;
|
||||
|
||||
return member[member_num].name;
|
||||
}
|
||||
|
||||
|
||||
/* Returns position of actor in party or -1.
|
||||
*/
|
||||
sint8 Party::get_member_num(const Actor *actor) const {
|
||||
for (int i = 0; i < num_in_party; i++) {
|
||||
if (member[i].actor->id_n == actor->id_n)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
sint8 Party::get_member_num(uint8 a) const {
|
||||
return (get_member_num(actor_manager->get_actor(a)));
|
||||
}
|
||||
|
||||
uint8 Party::get_actor_num(uint8 member_num) const {
|
||||
if (num_in_party <= member_num)
|
||||
return 0; // hmm how should we handle this error.
|
||||
|
||||
return member[member_num].actor->id_n;
|
||||
}
|
||||
|
||||
/* Rearrange member slot positions based on the number of members and the
|
||||
* selected formation. Used only when adding or removing actors.
|
||||
*/
|
||||
void Party::reform_party() {
|
||||
uint32 n;
|
||||
sint32 x, y, max_x;
|
||||
bool even_row;
|
||||
sint8 leader = get_leader();
|
||||
if (leader < 0 || num_in_party == 1)
|
||||
return;
|
||||
member[leader].form_x = 0;
|
||||
member[leader].form_y = 0;
|
||||
switch (formation) {
|
||||
case PARTY_FORM_COLUMN: // line up behind Avatar
|
||||
x = 0;
|
||||
y = 1;
|
||||
for (n = leader + 1; n < num_in_party; n++) {
|
||||
member[n].form_x = x;
|
||||
member[n].form_y = y++;
|
||||
if (y == 5) {
|
||||
x += 1;
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PARTY_FORM_ROW: // line up left of Avatar
|
||||
x = -1;
|
||||
y = 0;
|
||||
for (n = leader + 1; n < num_in_party; n++) {
|
||||
member[n].form_x = x--;
|
||||
member[n].form_y = y;
|
||||
if (x == -5) {
|
||||
y += 1;
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PARTY_FORM_DELTA: // open triangle formation with Avatar at front
|
||||
x = -1;
|
||||
y = 1;
|
||||
for (n = leader + 1; n < num_in_party; n++) {
|
||||
member[n].form_x = x;
|
||||
member[n].form_y = y;
|
||||
// alternate X once, then move down
|
||||
x = -x;
|
||||
if (x == 0 || x < 0) {
|
||||
x -= 1;
|
||||
++y;
|
||||
}
|
||||
if (y == 5) { // start at top of triangle
|
||||
y -= ((-x) - 1);
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// case PARTY_FORM_COMBAT: // positions determined by COMBAT mode
|
||||
// break;
|
||||
case PARTY_FORM_REST: // special formation used while Resting
|
||||
member[1].form_x = 0;
|
||||
member[1].form_y = -2;
|
||||
member[2].form_x = 1;
|
||||
member[2].form_y = -1;
|
||||
member[3].form_x = -1;
|
||||
member[3].form_y = -1;
|
||||
member[4].form_x = 1;
|
||||
member[4].form_y = 0;
|
||||
member[5].form_x = -1;
|
||||
member[5].form_y = -2;
|
||||
member[6].form_x = 1;
|
||||
member[6].form_y = -2;
|
||||
member[7].form_x = -1;
|
||||
member[7].form_y = 0;
|
||||
break;
|
||||
case PARTY_FORM_STANDARD: // U6
|
||||
default:
|
||||
// put first follower behind or behind and to the left of Avatar
|
||||
member[leader + 1].form_x = (num_in_party >= 3) ? -1 : 0;
|
||||
member[leader + 1].form_y = 1;
|
||||
x = y = 1;
|
||||
even_row = false;
|
||||
for (n = leader + 2, max_x = 1; n < num_in_party; n++) {
|
||||
member[n].form_x = x;
|
||||
member[n].form_y = y;
|
||||
// alternate & move X left/right by 2
|
||||
x = (x == 0) ? x - 2 : (x < 0) ? -x : -x - 2;
|
||||
if (x > max_x || (x < 0 && -x > max_x)) { // reached row max.
|
||||
++y;
|
||||
even_row = !even_row; // next row
|
||||
++max_x; // increase row max.
|
||||
x = even_row ? 0 : -1; // next guy starts at center or left by 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns number of person leading the party (the first active member), or -1
|
||||
* if the party has no leader and can't move. */
|
||||
sint8 Party::get_leader() const {
|
||||
for (int m = 0; m < num_in_party; m++)
|
||||
if (member[m].actor->is_immobile() == false && member[m].actor->is_charmed() == false)
|
||||
return m;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get map location of a party member. */
|
||||
MapCoord Party::get_location(uint8 m) const {
|
||||
return (member[m].actor->get_location());
|
||||
}
|
||||
|
||||
/* Get map location of the first active party member. */
|
||||
MapCoord Party::get_leader_location() const {
|
||||
sint8 m = get_leader();
|
||||
MapCoord loc;
|
||||
if (m >= 0)
|
||||
loc = member[m].actor->get_location();
|
||||
return loc;
|
||||
}
|
||||
|
||||
/* Returns absolute location where party member `m' SHOULD be (based on party
|
||||
* formation and leader location.
|
||||
*/
|
||||
MapCoord Party::get_formation_coords(uint8 m) const {
|
||||
MapCoord a = get_location(m); // my location
|
||||
MapCoord l = get_leader_location(); // leader location
|
||||
sint8 leader = get_leader();
|
||||
if (leader < 0)
|
||||
return a;
|
||||
uint8 ldir = member[leader].actor->get_direction(); // leader direction
|
||||
// intended location
|
||||
uint16 x = (ldir == NUVIE_DIR_N) ? l.x + member[m].form_x : // X
|
||||
(ldir == NUVIE_DIR_E) ? l.x - member[m].form_y :
|
||||
(ldir == NUVIE_DIR_S) ? l.x - member[m].form_x :
|
||||
(ldir == NUVIE_DIR_W) ? l.x + member[m].form_y : a.x;
|
||||
uint16 y = (ldir == NUVIE_DIR_N) ? l.y + member[m].form_y : // Y
|
||||
(ldir == NUVIE_DIR_E) ? l.y + member[m].form_x :
|
||||
(ldir == NUVIE_DIR_S) ? l.y - member[m].form_y :
|
||||
(ldir == NUVIE_DIR_W) ? l.y - member[m].form_x : a.y;
|
||||
return (MapCoord(WRAPPED_COORD(x, a.z),
|
||||
WRAPPED_COORD(y, a.z),
|
||||
a.z // Z
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/* Update the actual locations of the party actors on the map, so that they are
|
||||
* in the proper formation. */
|
||||
void Party::follow(sint8 rel_x, sint8 rel_y) {
|
||||
Common::Array<bool> try_again;
|
||||
try_again.resize(get_party_max());
|
||||
|
||||
sint8 leader = get_leader();
|
||||
if (leader <= -1)
|
||||
return;
|
||||
|
||||
if (is_in_combat_mode()) { // just update everyone's combat mode
|
||||
for (int p = 0; p < get_party_size(); p++)
|
||||
get_actor(p)->set_worktype(get_actor(p)->get_combat_mode());
|
||||
return;
|
||||
}
|
||||
|
||||
defer_removing_dead_members = true;
|
||||
|
||||
// set previous leader location first, just in case the leader changed
|
||||
prev_leader_x = WRAPPED_COORD(member[leader].actor->x - rel_x, member[leader].actor->z);
|
||||
prev_leader_y = member[leader].actor->y - rel_y;
|
||||
// PASS 1: Keep actors chained together.
|
||||
for (uint32 p = (leader + 1); p < num_in_party; p++) {
|
||||
if (member[p].actor->is_immobile()) continue;
|
||||
|
||||
try_again[p] = false;
|
||||
if (!pathfinder->follow_passA(p))
|
||||
try_again[p] = true;
|
||||
}
|
||||
// PASS 2: Catch up to party.
|
||||
for (uint32 p = (leader + 1); p < num_in_party; p++) {
|
||||
if (member[p].actor->is_immobile()) continue;
|
||||
|
||||
if (try_again[p])
|
||||
pathfinder->follow_passA(p);
|
||||
pathfinder->follow_passB(p);
|
||||
if (!pathfinder->is_contiguous(p)) {
|
||||
sint8 l = get_leader();
|
||||
if (l >= 0) {
|
||||
DEBUG(0, LEVEL_DEBUGGING, "%s is looking for %s.\n", get_actor_name(p), get_actor_name(l));
|
||||
}
|
||||
pathfinder->seek_leader(p); // enter/update seek mode
|
||||
} else if (member[p].actor->get_pathfinder())
|
||||
pathfinder->end_seek(p);
|
||||
|
||||
get_actor(p)->set_moves_left(get_actor(p)->get_moves_left() - 10);
|
||||
get_actor(p)->set_worktype(0x01); // revert to normal worktype
|
||||
}
|
||||
|
||||
defer_removing_dead_members = false;
|
||||
|
||||
//remove party members that died during follow routine.
|
||||
for (int p = get_party_size() - 1; p >= 0; p--) {
|
||||
Actor *a = get_actor(p);
|
||||
if (a->is_alive() == false)
|
||||
remove_actor(a, PARTY_KEEP_PARTY_FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if anyone in the party has a matching object.
|
||||
bool Party::has_obj(uint16 obj_n, uint8 quality, bool match_zero_qual) const {
|
||||
uint16 i;
|
||||
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
if (member[i].actor->inventory_get_object(obj_n, quality, match_zero_qual) != nullptr) // we got a match
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes the first occurrence of an object in the party.
|
||||
bool Party::remove_obj(uint16 obj_n, uint8 quality) {
|
||||
uint16 i;
|
||||
Obj *obj;
|
||||
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
obj = member[i].actor->inventory_get_object(obj_n, quality);
|
||||
if (obj != nullptr) {
|
||||
if (member[i].actor->inventory_remove_obj(obj)) {
|
||||
delete_obj(obj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the actor id of the first person in the party to have a matching object.
|
||||
Actor *Party::who_has_obj(uint16 obj_n, uint8 quality, bool match_qual_zero) {
|
||||
uint16 i;
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
if (member[i].actor->inventory_get_object(obj_n, quality, match_qual_zero) != nullptr)
|
||||
return member[i].actor;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Obj *Party::get_obj(uint16 obj_n, uint8 quality, bool match_qual_zero, uint8 frame_n, bool match_frame_n) {
|
||||
Obj *obj;
|
||||
for (uint16 i = 0; i < num_in_party; i++) {
|
||||
obj = member[i].actor->inventory_get_object(obj_n, quality, match_qual_zero, frame_n, match_frame_n);
|
||||
if (obj)
|
||||
return obj;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Is EVERYONE in the party at or near the coordinates?
|
||||
*/
|
||||
bool Party::is_at(uint16 x, uint16 y, uint8 z, uint32 threshold) const {
|
||||
for (uint32 p = 0; p < num_in_party; p++) {
|
||||
MapCoord loc(x, y, z);
|
||||
if (!member[p].actor->is_nearby(loc, threshold))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Party::is_at(const MapCoord &xyz, uint32 threshold) const {
|
||||
return is_at(xyz.x, xyz.y, xyz.z, threshold);
|
||||
}
|
||||
|
||||
/* Is ANYONE in the party at or near the coordinates? */
|
||||
bool Party::is_anyone_at(uint16 x, uint16 y, uint8 z, uint32 threshold) const {
|
||||
for (uint32 p = 0; p < num_in_party; p++) {
|
||||
MapCoord loc(x, y, z);
|
||||
if (member[p].actor->is_nearby(loc, threshold))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Party::is_anyone_at(const MapCoord &xyz, uint32 threshold) const {
|
||||
return is_anyone_at(xyz.x, xyz.y, xyz.z, threshold);
|
||||
}
|
||||
|
||||
bool Party::contains_actor(const Actor *actor) const {
|
||||
if (get_member_num(actor) >= 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Party::contains_actor(uint8 actor_num) const {
|
||||
return (contains_actor(actor_manager->get_actor(actor_num)));
|
||||
}
|
||||
|
||||
void Party::set_in_combat_mode(bool value) {
|
||||
in_combat_mode = value;
|
||||
actor_manager->set_combat_movement(value);
|
||||
|
||||
if (in_combat_mode) {
|
||||
for (int p = 0; p < get_party_size(); p++)
|
||||
get_actor(p)->set_worktype(get_actor(p)->get_combat_mode()); //set combat worktype
|
||||
} else {
|
||||
for (int p = 0; p < get_party_size(); p++)
|
||||
get_actor(p)->set_worktype(ACTOR_WT_FOLLOW); //set back to follow party leader.
|
||||
}
|
||||
// if(combat_changes_music)
|
||||
update_music();
|
||||
if (game->get_command_bar() != nullptr) {
|
||||
game->get_command_bar()->set_combat_mode(in_combat_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void Party::update_music() {
|
||||
SoundManager *s = Game::get_game()->get_sound_manager();
|
||||
MapCoord pos;
|
||||
|
||||
if (in_vehicle && vehicles_change_music) {
|
||||
s->musicPlayFrom("boat");
|
||||
return;
|
||||
} else if (in_combat_mode && combat_changes_music) {
|
||||
s->musicPlayFrom("combat");
|
||||
return;
|
||||
}
|
||||
|
||||
pos = get_leader_location();
|
||||
|
||||
switch (pos.z) {
|
||||
case 0 :
|
||||
s->musicPlayFrom("random");
|
||||
break;
|
||||
case 5 :
|
||||
s->musicPlayFrom("gargoyle");
|
||||
break;
|
||||
default :
|
||||
s->musicPlayFrom("dungeon");
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Party::heal() {
|
||||
uint16 i;
|
||||
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
member[i].actor->heal();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void Party::cure() {
|
||||
for (uint16 i = 0; i < num_in_party; i++) {
|
||||
member[i].actor->cure();
|
||||
}
|
||||
}
|
||||
|
||||
void Party::set_ethereal(bool ethereal) {
|
||||
for (uint16 i = 0; i < num_in_party; i++) {
|
||||
member[i].actor->set_ethereal(ethereal);
|
||||
}
|
||||
}
|
||||
|
||||
void Party::show() {
|
||||
uint16 i;
|
||||
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
member[i].actor->show();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Party::hide() {
|
||||
uint16 i;
|
||||
|
||||
for (i = 0; i < num_in_party; i++) {
|
||||
member[i].actor->hide();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move and center everyone in the party to one location.
|
||||
*/
|
||||
bool Party::move(uint16 dx, uint16 dy, uint8 dz) {
|
||||
for (sint32 m = 0; m < num_in_party; m++)
|
||||
if (!member[m].actor->move(dx, dy, dz, ACTOR_FORCE_MOVE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Automatically walk (timed) to a destination, and then teleport to new
|
||||
* location (optional). Used to enter/exit dungeons.
|
||||
* (step_delay 0 = default speed)
|
||||
*/
|
||||
void Party::walk(MapCoord *walkto, MapCoord *teleport, uint32 step_delay) {
|
||||
if (step_delay)
|
||||
new TimedPartyMove(walkto, teleport, step_delay);
|
||||
else
|
||||
new TimedPartyMove(walkto, teleport);
|
||||
|
||||
game->pause_world(); // other actors won't move
|
||||
game->pause_user(); // don't allow input
|
||||
// view will snap back to player after everyone has moved
|
||||
game->get_player()->set_mapwindow_centered(false);
|
||||
autowalk = true;
|
||||
}
|
||||
|
||||
|
||||
/* Enter a moongate and teleport to a new location.
|
||||
* (step_delay 0 = default speed)
|
||||
*/
|
||||
void Party::walk(Obj *moongate, MapCoord *teleport, uint32 step_delay) {
|
||||
MapCoord walkto(moongate->x, moongate->y, moongate->z);
|
||||
if (step_delay)
|
||||
new TimedPartyMove(&walkto, teleport, moongate, step_delay);
|
||||
else
|
||||
new TimedPartyMove(&walkto, teleport, moongate);
|
||||
|
||||
game->pause_world(); // other actors won't move
|
||||
game->pause_user(); // don't allow input
|
||||
// view will snap back to player after everyone has moved
|
||||
game->get_player()->set_mapwindow_centered(false);
|
||||
autowalk = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Automatically walk (timed) to vehicle. (step_delay 0 = default speed)
|
||||
*/
|
||||
void Party::enter_vehicle(Obj *ship_obj, uint32 step_delay) {
|
||||
MapCoord walkto(ship_obj->x, ship_obj->y, ship_obj->z);
|
||||
|
||||
dismount_from_horses();
|
||||
|
||||
if (step_delay)
|
||||
new TimedPartyMoveToVehicle(&walkto, ship_obj, step_delay);
|
||||
else
|
||||
new TimedPartyMoveToVehicle(&walkto, ship_obj);
|
||||
|
||||
game->pause_world(); // other actors won't move
|
||||
game->pause_user(); // don't allow input
|
||||
// view will snap back to player after everyone has moved
|
||||
game->get_player()->set_mapwindow_centered(false);
|
||||
autowalk = true;
|
||||
}
|
||||
|
||||
void Party::exit_vehicle(uint16 x, uint16 y, uint16 z) {
|
||||
if (is_in_vehicle() == false)
|
||||
return;
|
||||
|
||||
Actor *vehicle_actor = actor_manager->get_actor(0);
|
||||
|
||||
show();
|
||||
vehicle_actor->unlink_surrounding_objects();
|
||||
vehicle_actor->hide();
|
||||
vehicle_actor->set_worktype(0);
|
||||
|
||||
Player *player = game->get_player();
|
||||
|
||||
player->set_actor(get_actor(0));
|
||||
player->move(x, y, z, false);
|
||||
vehicle_actor->obj_n = 0;//OBJ_U6_NO_VEHICLE;
|
||||
vehicle_actor->frame_n = 0;
|
||||
vehicle_actor->init();
|
||||
vehicle_actor->move(0, 0, 0, ACTOR_FORCE_MOVE);
|
||||
|
||||
set_in_vehicle(false);
|
||||
}
|
||||
|
||||
void Party::set_in_vehicle(bool value) {
|
||||
in_vehicle = value;
|
||||
if (vehicles_change_music)
|
||||
update_music();
|
||||
if (value) {
|
||||
if (in_combat_mode == true)
|
||||
set_in_combat_mode(false); // break off combat when boarding a vehicle
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Done automatically walking, return view to player character.
|
||||
*/
|
||||
void Party::stop_walking(bool force_music_change) {
|
||||
game->get_player()->set_mapwindow_centered(true);
|
||||
game->unpause_world(); // allow user input, unfreeze actors
|
||||
game->unpause_user();
|
||||
autowalk = false;
|
||||
if (force_music_change || vehicles_change_music)
|
||||
update_music();
|
||||
}
|
||||
|
||||
void Party::dismount_from_horses() {
|
||||
UseCode *usecode = Game::get_game()->get_usecode();
|
||||
|
||||
for (uint32 m = 0; m < num_in_party; m++) {
|
||||
if (member[m].actor->obj_n == OBJ_U6_HORSE_WITH_RIDER) {
|
||||
Obj *my_obj = member[m].actor->make_obj();
|
||||
usecode->use_obj(my_obj, member[m].actor);
|
||||
delete_obj(my_obj);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Actor *Party::get_slowest_actor() {
|
||||
Actor *actor = nullptr;
|
||||
sint8 begin = get_leader();
|
||||
if (begin >= 0) {
|
||||
actor = member[begin].actor;
|
||||
sint8 moves = actor->get_moves_left();
|
||||
for (uint32 m = begin + 1; m < num_in_party; m++) {
|
||||
sint8 select_moves = member[m].actor->get_moves_left();
|
||||
if (member[m].actor->is_immobile() == false && (select_moves < moves)) {
|
||||
moves = select_moves;
|
||||
actor = member[m].actor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return actor;
|
||||
}
|
||||
|
||||
/* Gather everyone around a campfire to Rest. */
|
||||
void Party::rest_gather() {
|
||||
Actor *player_actor = get_leader_actor();
|
||||
if (player_actor) {
|
||||
MapCoord player_loc = player_actor->get_location();
|
||||
rest_campfire = new_obj(OBJ_U6_CAMPFIRE, 1, player_loc.x, player_loc.y, player_loc.z);
|
||||
rest_campfire->set_temporary();
|
||||
rest_campfire->qty = 1; //this is set so the campfire may be destroyed by being attacked.
|
||||
game->get_obj_manager()->add_obj(rest_campfire, true); // addOnTop
|
||||
|
||||
game->get_player()->set_mapwindow_centered(false);
|
||||
game->pause_user();
|
||||
new TimedRestGather(player_loc.x, player_loc.y);
|
||||
}
|
||||
}
|
||||
|
||||
/* Start Resting for the specified number of hours, optionally with a party
|
||||
* member standing guard. */
|
||||
void Party::rest_sleep(uint8 hours, sint16 guard) {
|
||||
// FIXME: change music to Stones when asking "How many hours?", change to
|
||||
// a random song when finished camping (or if cancelled)
|
||||
new TimedRest(hours, guard >= 0 ? member[guard].actor : 0, rest_campfire);
|
||||
}
|
||||
|
||||
bool Party::can_rest(Std::string &err_str) {
|
||||
Map *map_ = game->get_game_map();
|
||||
Player *player = game->get_player();
|
||||
Actor *pActor = player->get_actor();
|
||||
MapCoord loc = pActor->get_location();
|
||||
|
||||
ActorList *enemies = nullptr;
|
||||
ActorList *all_actors = nullptr;
|
||||
|
||||
if (is_in_combat_mode()) {
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_SE)
|
||||
err_str = "\nNot while in Combat mode!";
|
||||
else if (Game::get_game()->get_game_type() == NUVIE_GAME_MD)
|
||||
err_str = "- Not while in Combat!";
|
||||
else
|
||||
err_str = "-Not while in Combat!";
|
||||
} else if (is_in_vehicle()
|
||||
&& pActor->get_obj_n() != OBJ_U6_SHIP) // player is a vehicle
|
||||
err_str = "-Can not be repaired!";
|
||||
else if (Game::get_game()->get_game_type() == NUVIE_GAME_U6
|
||||
&& game->get_map_window()->in_town())
|
||||
err_str = "-Only in the wilderness!";
|
||||
else if ((enemies = pActor->find_enemies())) {
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_MD)
|
||||
err_str = "\nNot while foes are near!";
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_SE)
|
||||
err_str = "- Not while foes are near!";
|
||||
else
|
||||
err_str = "-Not while foes are near!";
|
||||
} else if ((all_actors = actor_manager->filter_party(actor_manager->filter_distance(actor_manager->get_actor_list(),
|
||||
loc.x, loc.y, loc.z, 5)))
|
||||
&& !all_actors->empty() && !is_in_vehicle()) {
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_U6)
|
||||
err_str = "-Not while others are near!";
|
||||
else
|
||||
err_str = "\nIt's too noisy to sleep here!";
|
||||
delete all_actors;
|
||||
} else if (!player->in_party_mode())
|
||||
err_str = "-Not in solo mode!";
|
||||
else if (!is_in_vehicle() && !map_->is_passable(loc.x - 1, loc.y - 1, loc.x + 1, loc.y + 1, loc.z)
|
||||
&& Game::get_game()->get_game_type() != NUVIE_GAME_SE)
|
||||
err_str = "-Not enough room!"; // FIXME: for ships the original checks all squares around the ship. Do we really need this?
|
||||
else if (is_horsed())
|
||||
err_str = "-Dismount first!";
|
||||
else
|
||||
return true;
|
||||
delete enemies;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Party::is_horsed() const {
|
||||
for (int p = 0; p < num_in_party; p++)
|
||||
if (member[p].actor->get_obj_n() == OBJ_U6_HORSE_WITH_RIDER)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Party::is_everyone_horsed() const {
|
||||
for (int p = 0; p < num_in_party; p++)
|
||||
if (member[p].actor->get_obj_n() != OBJ_U6_HORSE_WITH_RIDER)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Obj *Party::get_food() {
|
||||
for (int p = 0; p < num_in_party; p++) {
|
||||
Obj *food = member[p].actor->inventory_get_food();
|
||||
if (food)
|
||||
return food;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Party::set_combat_target(uint8 member_num, Actor *target) {
|
||||
if (num_in_party <= member_num)
|
||||
return;
|
||||
|
||||
member[member_num].target.type = TARGET_ACTOR;
|
||||
member[member_num].target.actor_num = target->get_actor_num();
|
||||
}
|
||||
|
||||
void Party::set_combat_target(uint8 member_num, MapCoord target) {
|
||||
if (num_in_party <= member_num)
|
||||
return;
|
||||
|
||||
member[member_num].target.type = TARGET_LOCATION;
|
||||
member[member_num].target.loc = target;
|
||||
}
|
||||
|
||||
void Party::clear_combat_target(uint8 member_num) {
|
||||
if (member_num >= PARTY_MAX_MEMBERS)
|
||||
return;
|
||||
|
||||
member[member_num].target.type = TARGET_NONE;
|
||||
member[member_num].target.loc = MapCoord();
|
||||
member[member_num].target.actor_num = 0;
|
||||
}
|
||||
|
||||
CombatTarget Party::get_combat_target(uint8 member_num) {
|
||||
if (num_in_party <= member_num) {
|
||||
CombatTarget noTarget;
|
||||
noTarget.type = TARGET_NONE;
|
||||
noTarget.loc = MapCoord();
|
||||
noTarget.actor_num = 0;
|
||||
return noTarget;
|
||||
}
|
||||
|
||||
return member[member_num].target;
|
||||
}
|
||||
|
||||
void Party::update_light_sources() {
|
||||
lightsources = 0;
|
||||
for (int i = 0; i < num_in_party; i++) {
|
||||
for (int j = 0; j < member[i].actor->get_num_light_sources(); j++)
|
||||
add_light_source();
|
||||
}
|
||||
if (game->get_event()->using_control_cheat()) {
|
||||
for (int i = 0; i < game->get_player()->get_actor()->get_num_light_sources(); i++)
|
||||
add_light_source();
|
||||
}
|
||||
game->get_map_window()->updateAmbience();
|
||||
}
|
||||
|
||||
bool Party::has_light_source() {
|
||||
if (!game->get_player()->get_actor())
|
||||
return false;
|
||||
if (lightsources > 0) { // the original engine didn't care about distance
|
||||
if (game->get_event()->using_control_cheat()) {
|
||||
if (game->get_player()->get_actor()->get_num_light_sources() > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < num_in_party; i++) {
|
||||
if (member[i].actor->get_num_light_sources() > 0) {
|
||||
if (!game->get_map_window()->tile_is_black(member[i].actor->x, member[i].actor->y)
|
||||
&& member[i].actor->is_nearby(game->get_player()->get_actor())) // within 5 tiles of player
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
233
engines/ultima/nuvie/core/party.h
Normal file
233
engines/ultima/nuvie/core/party.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_PARTY_H
|
||||
#define NUVIE_CORE_PARTY_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Game;
|
||||
class Actor;
|
||||
class ActorManager;
|
||||
class MapCoord;
|
||||
class Map;
|
||||
class NuvieIO;
|
||||
class PartyPathFinder;
|
||||
class PartySeek;
|
||||
|
||||
typedef enum { TARGET_ACTOR, TARGET_LOCATION, TARGET_NONE } CombatTargetType;
|
||||
|
||||
struct CombatTarget {
|
||||
CombatTargetType type;
|
||||
uint8 actor_num;
|
||||
MapCoord loc;
|
||||
};
|
||||
|
||||
struct PartyMember {
|
||||
char name[14];
|
||||
Actor *actor;
|
||||
//bool inactive; // true if not in formation
|
||||
uint8 combat_position;
|
||||
sint8 form_x; // relative position left or right of leader
|
||||
sint8 form_y; // relative position in front or in back of leader
|
||||
// (leader is at 0,0 in formation)
|
||||
CombatTarget target;
|
||||
};
|
||||
|
||||
#define PARTY_MAX_MEMBERS 16
|
||||
#define PARTY_NAME_MAX_LENGTH 13
|
||||
|
||||
#define PARTY_KEEP_PARTY_FLAG true
|
||||
|
||||
/* party walking formations: */
|
||||
#define PARTY_FORM_STANDARD 0
|
||||
#define PARTY_FORM_COLUMN 1
|
||||
#define PARTY_FORM_ROW 2
|
||||
#define PARTY_FORM_DELTA 3
|
||||
#define PARTY_FORM_COMBAT 7
|
||||
#define PARTY_FORM_REST 8
|
||||
|
||||
/* 0 <- standard *
|
||||
* 1 2 *
|
||||
* 4 3 5 *
|
||||
* 6 7 *
|
||||
* *
|
||||
* 0 <- column * 3210 <- row
|
||||
* 1 * 7654
|
||||
* 2 *
|
||||
* 3... *
|
||||
* *
|
||||
* 0 <- delta
|
||||
* 172
|
||||
* 38 94
|
||||
* 5A B6
|
||||
*
|
||||
* (Combat positions are dynamic, based on combat mode)
|
||||
*/
|
||||
|
||||
class Party {
|
||||
protected:
|
||||
friend class PartyPathFinder;
|
||||
Game *game; // get pointers here to avoid construct order issues in loadGame()
|
||||
const Configuration *config;
|
||||
ActorManager *actor_manager;
|
||||
Map *map;
|
||||
PartyPathFinder *pathfinder;
|
||||
|
||||
PartyMember member[PARTY_MAX_MEMBERS];
|
||||
uint8 lightsources;
|
||||
uint8 num_in_party; // number of party members.
|
||||
uint8 formation; // walking formation
|
||||
uint16 prev_leader_x; // last location of leader
|
||||
uint16 prev_leader_y;
|
||||
|
||||
bool autowalk; // party is automatically walking to a destination
|
||||
bool in_vehicle; //Party is in a vehicle.
|
||||
bool in_combat_mode;
|
||||
|
||||
bool defer_removing_dead_members;
|
||||
|
||||
Obj *rest_campfire;
|
||||
|
||||
public:
|
||||
|
||||
Party(const Configuration *cfg);
|
||||
virtual ~Party();
|
||||
|
||||
virtual bool init(Game *g, ActorManager *am);
|
||||
virtual bool load(NuvieIO *objlist);
|
||||
virtual bool save(NuvieIO *objlist);
|
||||
|
||||
// Basic methods
|
||||
void follow(sint8 rel_x, sint8 rel_y); // follow in direction leader moved
|
||||
bool move(uint16 dx, uint16 dy, uint8 dz);
|
||||
void show(); // Actor::show()
|
||||
void hide(); // Actor::hide()
|
||||
virtual void dismount_from_horses();
|
||||
virtual void update_music(); // set music depending on party location
|
||||
virtual void split_gold();
|
||||
virtual void gather_gold();
|
||||
bool add_actor(Actor *actor);
|
||||
bool remove_actor(Actor *actor, bool keep_party_flag = false);
|
||||
bool remove_dead_actor(Actor *actor);
|
||||
bool resurrect_dead_members();
|
||||
void heal();
|
||||
void cure();
|
||||
void set_ethereal(bool ethereal);
|
||||
//void set_active(uint8 member_num, bool state) { member[member_num].inactive = !state; }
|
||||
uint8 get_formation() const {
|
||||
return formation; // walking formation
|
||||
}
|
||||
void set_formation(uint8 val) {
|
||||
formation = val;
|
||||
reform_party();
|
||||
}
|
||||
// Properties
|
||||
uint8 get_party_size();
|
||||
virtual uint8 get_party_max() {
|
||||
return 8; // U6
|
||||
}
|
||||
sint8 get_leader() const; // returns -1 if party has no leader and can't move
|
||||
MapCoord get_leader_location() const;
|
||||
MapCoord get_location(uint8 m = 0) const;
|
||||
MapCoord get_formation_coords(uint8 m) const;
|
||||
void set_in_vehicle(bool value);
|
||||
void set_in_combat_mode(bool value);
|
||||
bool is_in_vehicle() const {
|
||||
return in_vehicle;
|
||||
}
|
||||
bool is_in_combat_mode() const {
|
||||
return in_combat_mode;
|
||||
}
|
||||
Actor *get_slowest_actor(); // actor with lowest move count
|
||||
|
||||
// Check specific actors
|
||||
uint8 get_actor_num(uint8 member_num) const; //get actor id_n from party_member num.
|
||||
Actor *get_actor(uint8 member_num) const;
|
||||
sint8 get_member_num(const Actor *actor) const;
|
||||
sint8 get_member_num(uint8 a) const;
|
||||
Actor *get_leader_actor() const;
|
||||
const char *get_actor_name(uint8 member_num) const;
|
||||
bool is_leader(const Actor *actor) const {
|
||||
return (get_member_num(actor) == get_leader());
|
||||
}
|
||||
bool contains_actor(const Actor *actor) const;
|
||||
bool contains_actor(uint8 actor_num) const;
|
||||
|
||||
// Check entire party
|
||||
bool is_at(uint16 x, uint16 y, uint8 z, uint32 threshold = 0) const;
|
||||
bool is_at(const MapCoord &xyz, uint32 threshold = 0) const;
|
||||
bool is_anyone_at(uint16 x, uint16 y, uint8 z, uint32 threshold = 0) const;
|
||||
bool is_anyone_at(const MapCoord &xyz, uint32 threshold = 0) const;
|
||||
bool has_obj(uint16 obj_n, uint8 quality, bool match_zero_qual = true) const;
|
||||
bool remove_obj(uint16 obj_n, uint8 quality);
|
||||
Actor *who_has_obj(uint16 obj_n, uint8 quality, bool match_zero_qual = true);
|
||||
Obj *get_obj(uint16 obj_n, uint8 quality, bool match_qual_zero = true, uint8 frame_n = 0, bool match_frame_n = false);
|
||||
bool is_horsed() const; // is anyone on a horse?
|
||||
bool is_everyone_horsed() const;
|
||||
Obj *get_food(); // used while resting
|
||||
|
||||
// Automatic-walking. These methods should be replaced with ActorActions.
|
||||
void walk(MapCoord *walkto, MapCoord *teleport, uint32 step_delay = 0);
|
||||
void walk(MapCoord *walkto, uint32 step_delay = 0) {
|
||||
walk(walkto, nullptr, step_delay);
|
||||
}
|
||||
void walk(Obj *moongate, MapCoord *teleport, uint32 step_delay = 0);
|
||||
void enter_vehicle(Obj *ship_obj, uint32 step_delay = 0);
|
||||
void exit_vehicle(uint16 x, uint16 y, uint16 z);
|
||||
void stop_walking(bool force_music_change);
|
||||
bool get_autowalk() const {
|
||||
return autowalk;
|
||||
}
|
||||
void rest_gather();
|
||||
void rest_sleep(uint8 hours, sint16 guard);
|
||||
bool can_rest(Std::string &err_str);
|
||||
|
||||
void set_combat_target(uint8 member_num, Actor *target);
|
||||
void set_combat_target(uint8 member_num, MapCoord target);
|
||||
void clear_combat_target(uint8 member_num);
|
||||
CombatTarget get_combat_target(uint8 member_num);
|
||||
bool has_light_source();
|
||||
void add_light_source() {
|
||||
lightsources++; /* fprintf(stderr, "lightsources = %d\n", lightsources); */
|
||||
}
|
||||
void subtract_light_source() { /*assert(lightsources != 0);*/
|
||||
lightsources--; /*fprintf(stderr, "lightsources = %d\n", lightsources); */
|
||||
}
|
||||
void update_light_sources();
|
||||
|
||||
bool combat_changes_music, vehicles_change_music;
|
||||
|
||||
protected:
|
||||
void reform_party(); // call when adding or removing members
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
743
engines/ultima/nuvie/core/player.cpp
Normal file
743
engines/ultima/nuvie/core/player.cpp
Normal file
@@ -0,0 +1,743 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/save/obj_list.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h"
|
||||
#include "ultima/nuvie/sound/sound_manager.h"
|
||||
#include "ultima/nuvie/core/weather.h"
|
||||
#include "ultima/nuvie/script/script.h"
|
||||
#include "ultima/nuvie/core/player.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
static const int PLAYER_BASE_MOVEMENT_COST = 5;
|
||||
|
||||
Player::Player(const Configuration *cfg) : config(cfg), _clock(nullptr),
|
||||
party(nullptr), actor(nullptr), actor_manager(nullptr), obj_manager(nullptr),
|
||||
map_window(nullptr), karma(0), gender(0), questf(0), gargishf(0), alcohol(0),
|
||||
current_weapon(0), party_mode(false), mapwindow_centered(false) {
|
||||
config->value("config/GameType", game_type);
|
||||
}
|
||||
|
||||
bool Player::init(ObjManager *om, ActorManager *am, MapWindow *mw, GameClock *c, Party *p) {
|
||||
|
||||
_clock = c;
|
||||
actor_manager = am;
|
||||
obj_manager = om;
|
||||
map_window = mw;
|
||||
party = p;
|
||||
|
||||
current_weapon = -1;
|
||||
|
||||
init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Player::init() {
|
||||
actor = nullptr;
|
||||
|
||||
party_mode = true;
|
||||
mapwindow_centered = true;
|
||||
}
|
||||
|
||||
bool Player::load(NuvieIO *objlist) {
|
||||
uint8 solo_member_num = OBJLIST_PARTY_MODE;
|
||||
|
||||
init();
|
||||
|
||||
// We can get the name from the player actor. --SB-X
|
||||
/* objlist->seek(0xf00);
|
||||
|
||||
objlist->readToBuf((unsigned char *)name,14); // read in Player name.*/
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
objlist->seek(OBJLIST_OFFSET_U6_QUEST_FLAG); // U6 Quest Flag
|
||||
questf = objlist->read1();
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_KARMA); // Player Karma.
|
||||
karma = objlist->read1();
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_ALCOHOL); // Alcohol consumed
|
||||
alcohol = objlist->read1();
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_GARGISH_LANG); // U6 Gargish Flag
|
||||
gargishf = objlist->read1();
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_SOLO_MODE); //Party Mode = 0xff other wise it is solo mode party member number starting from 0.
|
||||
solo_member_num = objlist->read1();
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_GENDER); // Player Gender.
|
||||
gender = objlist->read1();
|
||||
}
|
||||
|
||||
if (game_type == NUVIE_GAME_MD) {
|
||||
objlist->seek(OBJLIST_OFFSET_MD_GENDER); // Player Gender.
|
||||
gender = objlist->read1();
|
||||
}
|
||||
|
||||
if (solo_member_num == OBJLIST_PARTY_MODE) {
|
||||
party_mode = true;
|
||||
set_party_mode(find_actor());
|
||||
} else
|
||||
set_solo_mode(party->get_actor(solo_member_num));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Player::save(NuvieIO *objlist) {
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
objlist->seek(OBJLIST_OFFSET_U6_QUEST_FLAG); // U6 Quest Flag
|
||||
objlist->write1(questf);
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_KARMA); // Player Karma.
|
||||
objlist->write1(karma);
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_ALCOHOL); // Alcohol consumed
|
||||
objlist->write1(alcohol);
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_GARGISH_LANG); // U6 Gargish Flag
|
||||
objlist->write1(gargishf);
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_SOLO_MODE); // solo member num.
|
||||
if (party_mode)
|
||||
objlist->write1(OBJLIST_PARTY_MODE); // 0xff
|
||||
else
|
||||
objlist->write1(party->get_member_num(actor)); //write the party member number of the solo actor
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_GENDER); // Player Gender.
|
||||
objlist->write1(gender);
|
||||
}
|
||||
|
||||
if (game_type == NUVIE_GAME_MD) {
|
||||
objlist->seek(OBJLIST_OFFSET_MD_GENDER); // Player Gender.
|
||||
objlist->write1(gender);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Actor *Player::find_actor() {
|
||||
for (int p = 0; p < ACTORMANAGER_MAX_ACTORS; p++) {
|
||||
Actor *theActor = actor_manager->get_actor(p);
|
||||
if (theActor->get_worktype() == 0x02 && theActor->is_immobile() == false) // WT_U6_PLAYER
|
||||
return theActor;
|
||||
}
|
||||
|
||||
sint8 party_leader = party->get_leader();
|
||||
|
||||
if (party_leader != -1)
|
||||
return party->get_actor(party_leader);
|
||||
|
||||
return actor_manager->get_avatar();
|
||||
}
|
||||
|
||||
|
||||
// keep MapWindow focused on Player actor, or remove focus
|
||||
void Player::set_mapwindow_centered(bool state) {
|
||||
uint16 x, y;
|
||||
uint8 z;
|
||||
|
||||
mapwindow_centered = state;
|
||||
if (mapwindow_centered == false)
|
||||
return;
|
||||
map_window->centerMapOnActor(actor); // center immediately
|
||||
|
||||
get_location(&x, &y, &z);
|
||||
actor_manager->updateActors(x, y, z);
|
||||
obj_manager->update(x, y, z); // spawn eggs when teleporting. eg red moongate.
|
||||
}
|
||||
|
||||
void Player::set_actor(Actor *new_actor) {
|
||||
MsgScroll *scroll = Game::get_game()->get_scroll();
|
||||
if (new_actor == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor != nullptr) {
|
||||
if (party->contains_actor(actor))
|
||||
actor->set_worktype(0x01); //WT_U6_IN_PARTY
|
||||
else
|
||||
actor->set_worktype(0x00); //no worktype
|
||||
}
|
||||
bool same_actor = (actor == new_actor);
|
||||
actor = new_actor;
|
||||
|
||||
actor->set_worktype(0x02); // WT_U6_PLAYER
|
||||
actor->delete_pathfinder();
|
||||
|
||||
current_weapon = ACTOR_NO_READIABLE_LOCATION;
|
||||
map_window->centerCursor();
|
||||
if (same_actor)
|
||||
return;
|
||||
actor_manager->set_player(actor);
|
||||
Std::string prompt = get_name();
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
prompt += ":\n";
|
||||
}
|
||||
|
||||
prompt += ">";
|
||||
|
||||
scroll->set_prompt(prompt.c_str());
|
||||
}
|
||||
|
||||
Actor *Player::get_actor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
const Actor *Player::get_actor() const {
|
||||
return actor;
|
||||
}
|
||||
|
||||
void Player::get_location(uint16 *ret_x, uint16 *ret_y, uint8 *ret_level) const {
|
||||
actor->get_location(ret_x, ret_y, ret_level);
|
||||
}
|
||||
|
||||
uint8 Player::get_location_level() const {
|
||||
return actor->z;
|
||||
}
|
||||
|
||||
const char *Player::get_name() {
|
||||
if (actor->get_actor_num() == ACTOR_VEHICLE_ID_N)
|
||||
return actor_manager->get_avatar()->get_name();
|
||||
|
||||
return actor->get_name(true);
|
||||
}
|
||||
|
||||
|
||||
/* Add to Player karma. Handle appropriately the karma min/max limits. */
|
||||
void Player::add_karma(uint8 val) {
|
||||
karma = ((karma + val) <= 99) ? karma + val : 99;
|
||||
}
|
||||
|
||||
/* Subtract from Player karma. Handle appropriately the karma min/max limits. */
|
||||
void Player::subtract_karma(uint8 val) {
|
||||
karma = ((karma - val) >= 0) ? karma - val : 0;
|
||||
}
|
||||
|
||||
void Player::subtract_movement_points(uint8 points) {
|
||||
Game::get_game()->get_script()->call_actor_subtract_movement_points(get_actor(), points);
|
||||
}
|
||||
|
||||
const char *Player::get_gender_title() const {
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
if (gender == 0)
|
||||
return "milord";
|
||||
else
|
||||
return "milady";
|
||||
case NUVIE_GAME_MD :
|
||||
if (gender == 0)
|
||||
return "Sir";
|
||||
else
|
||||
return "Madam";
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
return "Sir"; //FIXME is this needed for SE?
|
||||
}
|
||||
|
||||
bool Player::check_moveRelative(sint16 rel_x, sint16 rel_y) {
|
||||
if (!actor->moveRelative(rel_x, rel_y, ACTOR_IGNORE_DANGER)) { /**MOVE**/
|
||||
ActorError *ret = actor->get_error();
|
||||
// FIXME: When in combat, U6 attempts to move party members with role "front" forward instead of swapping.
|
||||
if (ret->err == ACTOR_BLOCKED_BY_ACTOR
|
||||
&& (game_type != NUVIE_GAME_U6 || actor->obj_n != OBJ_U6_MOUSE) // Only exchange positions if player is not U6 mouse
|
||||
&& party->contains_actor(ret->blocking_actor) && ret->blocking_actor->is_immobile() == false)
|
||||
ret->blocking_actor->push(actor, ACTOR_PUSH_HERE);
|
||||
// There could be more party members at the destination, but U6 ignores them - hence the ACTOR_IGNORE_PARTY_MEMBERS.
|
||||
if (!actor->moveRelative(rel_x, rel_y, ACTOR_IGNORE_DANGER | ACTOR_IGNORE_PARTY_MEMBERS)) /**MOVE**/
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// walk to adjacent square
|
||||
void Player::moveRelative(sint16 rel_x, sint16 rel_y, bool mouse_movement) {
|
||||
const NuvieDir raft_movement_tbl[] = {
|
||||
NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_N, NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_NW, NUVIE_DIR_N,
|
||||
NUVIE_DIR_NE, NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_N, NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_NE, NUVIE_DIR_N,
|
||||
NUVIE_DIR_NE, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_E, NUVIE_DIR_E, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_NE,
|
||||
NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_E, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_SE,
|
||||
NUVIE_DIR_S, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_SE, NUVIE_DIR_S, NUVIE_DIR_S, NUVIE_DIR_SW,
|
||||
NUVIE_DIR_W, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_SW, NUVIE_DIR_SW, NUVIE_DIR_S, NUVIE_DIR_SW, NUVIE_DIR_W,
|
||||
NUVIE_DIR_NW, NUVIE_DIR_W, NUVIE_DIR_SW, NUVIE_DIR_W, NUVIE_DIR_NW, NUVIE_DIR_SW, NUVIE_DIR_W, NUVIE_DIR_W,
|
||||
NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_W, NUVIE_DIR_NW, NUVIE_DIR_N, NUVIE_DIR_NW, NUVIE_DIR_W, NUVIE_DIR_NW
|
||||
};
|
||||
const uint8 ship_cost[8] = {0xA, 5, 3, 4, 5, 4, 3, 5};
|
||||
const uint8 skiff_cost[8] = {3, 4, 5, 7, 0xA, 7, 5, 4};
|
||||
|
||||
MovementStatus movementStatus = CAN_MOVE;
|
||||
bool can_change_rel_dir = true;
|
||||
NuvieDir wind_dir = Game::get_game()->get_weather()->get_wind_dir();
|
||||
uint16 x, y;
|
||||
uint8 z;
|
||||
actor->get_location(&x, &y, &z);
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
if (actor->id_n == 0) { // vehicle actor
|
||||
if (actor->obj_n == OBJ_U6_INFLATED_BALLOON &&
|
||||
(!Game::get_game()->has_free_balloon_movement() || !party->has_obj(OBJ_U6_FAN, 0, false))) {
|
||||
can_change_rel_dir = false;
|
||||
NuvieDir dir = get_reverse_direction(Game::get_game()->get_weather()->get_wind_dir());
|
||||
if (dir == NUVIE_DIR_NONE) {
|
||||
Game::get_game()->get_scroll()->display_string("Thou canst not move without wind!\n\n");
|
||||
Game::get_game()->get_scroll()->display_prompt();
|
||||
actor->set_moves_left(0);
|
||||
rel_x = 0;
|
||||
rel_y = 0;
|
||||
} else {
|
||||
get_relative_dir(dir, &rel_x, &rel_y);
|
||||
}
|
||||
} else if (actor->obj_n == OBJ_U6_RAFT) {
|
||||
NuvieDir dir = NUVIE_DIR_N;
|
||||
can_change_rel_dir = false;
|
||||
const Tile *t = Game::get_game()->get_game_map()->get_tile(x, y, z, true);
|
||||
if (t->flags1 & TILEFLAG_BLOCKING) { //deep water tiles are blocking. Shore tiles should allow player movement.
|
||||
//deep water, so take control away from player.
|
||||
if (t->tile_num >= 8 && t->tile_num < 16) {
|
||||
dir = static_cast<NuvieDir>(t->tile_num - 8);
|
||||
}
|
||||
if (wind_dir != NUVIE_DIR_NONE) {
|
||||
dir = raft_movement_tbl[dir * 8 + get_reverse_direction(wind_dir)];
|
||||
} else {
|
||||
dir = get_nuvie_dir_code(dir);
|
||||
}
|
||||
|
||||
get_relative_dir(dir, &rel_x, &rel_y);
|
||||
}
|
||||
}
|
||||
} else { // normal actor
|
||||
if (alcohol > 3 && NUVIE_RAND() % 4 != 0) {
|
||||
rel_x = NUVIE_RAND() % 3 - 1; // stumble and change direction
|
||||
rel_y = NUVIE_RAND() % 3 - 1;
|
||||
can_change_rel_dir = false;
|
||||
Game::get_game()->get_scroll()->display_string("Hic!\n");
|
||||
}
|
||||
}
|
||||
|
||||
ActorMoveFlags move_flags = ACTOR_IGNORE_DANGER | ACTOR_IGNORE_PARTY_MEMBERS;
|
||||
// don't allow diagonal move between blocked tiles (player only)
|
||||
if (rel_x && rel_y && !actor->check_move(x + rel_x, y + 0, z, move_flags)
|
||||
&& !actor->check_move(x + 0, y + rel_y, z, move_flags)) {
|
||||
movementStatus = BLOCKED;
|
||||
}
|
||||
} else if (game_type == NUVIE_GAME_MD) {
|
||||
if (Game::get_game()->get_clock()->get_timer(GAMECLOCK_TIMER_MD_BLUE_BERRY) != 0 && NUVIE_RAND() % 2 == 0) {
|
||||
rel_x = NUVIE_RAND() % 3 - 1; // stumble and change direction
|
||||
rel_y = NUVIE_RAND() % 3 - 1;
|
||||
can_change_rel_dir = false;
|
||||
Game::get_game()->get_scroll()->display_string("you are dizzy!\n"); //FIXME need i18n support here.
|
||||
}
|
||||
}
|
||||
|
||||
if (actor->is_immobile() && actor->id_n != 0)
|
||||
movementStatus = BLOCKED;
|
||||
|
||||
if (movementStatus != BLOCKED && game_type != NUVIE_GAME_U6) {
|
||||
movementStatus = Game::get_game()->get_script()->call_player_before_move_action(&rel_x, &rel_y);
|
||||
}
|
||||
|
||||
if (movementStatus != BLOCKED) {
|
||||
if (movementStatus == FORCE_MOVE) {
|
||||
actor->moveRelative(rel_x, rel_y, ACTOR_FORCE_MOVE);
|
||||
} else if (!check_moveRelative(rel_x, rel_y)) {
|
||||
movementStatus = BLOCKED;
|
||||
if (mouse_movement && rel_x != 0 && rel_y != 0 && can_change_rel_dir) {
|
||||
if (check_moveRelative(rel_x, 0)) { // try x movement only
|
||||
rel_y = 0;
|
||||
movementStatus = CAN_MOVE;
|
||||
} else if (check_moveRelative(0, rel_y)) { // try y movement only
|
||||
rel_x = 0;
|
||||
movementStatus = CAN_MOVE;
|
||||
}
|
||||
}
|
||||
// Try opening a door FIXME: shouldn't be U6 specific
|
||||
if (movementStatus == BLOCKED) {
|
||||
if (obj_manager->is_door(x + rel_x, y + rel_y, z))
|
||||
try_open_door(x + rel_x, y + rel_y, z);
|
||||
}
|
||||
if (movementStatus == BLOCKED) {
|
||||
Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_BLOCKED);
|
||||
if (actor->id_n == 0) //vehicle actor.
|
||||
actor->set_moves_left(0); //zero movement points here so U6 can change wind direction by advancing game time.
|
||||
}
|
||||
}
|
||||
}
|
||||
actor->set_direction(rel_x, rel_y);
|
||||
|
||||
// post-move
|
||||
if (movementStatus != BLOCKED) {
|
||||
if (party_mode && party->is_leader(actor)) { // lead party
|
||||
party->follow(rel_x, rel_y);
|
||||
} else if (actor->id_n == 0 && game_type != NUVIE_GAME_MD) { // using vehicle; drag party along
|
||||
MapCoord new_xyz = actor->get_location();
|
||||
party->move(new_xyz.x, new_xyz.y, new_xyz.z);
|
||||
}
|
||||
|
||||
if (game_type == NUVIE_GAME_U6 && (actor->obj_n == OBJ_U6_INFLATED_BALLOON || actor->obj_n == OBJ_U6_RAFT)) {
|
||||
actor->set_moves_left(actor->get_moves_left() - PLAYER_BASE_MOVEMENT_COST);
|
||||
} else if (game_type == NUVIE_GAME_U6 && actor->obj_n == OBJ_U6_SHIP && wind_dir != WEATHER_WIND_CALM) {
|
||||
NuvieDir nuvie_dir = get_direction_code(rel_x, rel_y);
|
||||
if (nuvie_dir != NUVIE_DIR_NONE) {
|
||||
sint8 dir = get_original_dir_code(nuvie_dir);
|
||||
|
||||
actor->set_moves_left(actor->get_moves_left() - ship_cost[abs(dir - wind_dir)]);
|
||||
//DEBUG(0, LEVEL_DEBUGGING, "Ship movement cost = %d\n", ship_cost[abs(dir-wind_dir)]);
|
||||
}
|
||||
} else if (game_type == NUVIE_GAME_U6 && actor->obj_n == OBJ_U6_SKIFF) {
|
||||
NuvieDir nuvie_dir = get_direction_code(rel_x, rel_y);
|
||||
if (nuvie_dir != NUVIE_DIR_NONE) {
|
||||
sint8 dir = get_original_dir_code(nuvie_dir);
|
||||
sint8 water_dir = dir;
|
||||
const Tile *t = Game::get_game()->get_game_map()->get_tile(x, y, z, true);
|
||||
if (t->tile_num >= 8 && t->tile_num < 16) {
|
||||
dir = t->tile_num - 8;
|
||||
}
|
||||
actor->set_moves_left(actor->get_moves_left() - skiff_cost[abs(dir - water_dir)]);
|
||||
//DEBUG(0, LEVEL_DEBUGGING, "Skiff movement cost = %d\n", skiff_cost[abs(dir-water_dir)]);
|
||||
}
|
||||
} else {
|
||||
actor->set_moves_left(actor->get_moves_left() - (PLAYER_BASE_MOVEMENT_COST + Game::get_game()->get_game_map()->get_impedance(x, y, z)));
|
||||
if (rel_x != 0 && rel_y != 0) // diagonal move, double cost
|
||||
actor->set_moves_left(actor->get_moves_left() - PLAYER_BASE_MOVEMENT_COST);
|
||||
}
|
||||
}
|
||||
|
||||
if (game_type != NUVIE_GAME_U6) {
|
||||
Game::get_game()->get_script()->call_player_post_move_action(movementStatus != BLOCKED);
|
||||
actor->get_location(&x, &y, &z); //update location in case we have moved.
|
||||
}
|
||||
|
||||
// update world around player
|
||||
actor_manager->updateActors(x, y, z);
|
||||
obj_manager->update(x, y, z); // remove temporary objs, hatch eggs
|
||||
_clock->inc_move_counter(); // doesn't update time
|
||||
actor_manager->startActors(); // end player turn
|
||||
}
|
||||
|
||||
void Player::try_open_door(uint16 x, uint16 y, uint8 z) {
|
||||
UseCode *usecode = Game::get_game()->get_usecode();
|
||||
Obj *obj = obj_manager->get_obj(x, y, z);
|
||||
if (!usecode->is_door(obj))
|
||||
return;
|
||||
usecode->use_obj(obj, get_actor());
|
||||
subtract_movement_points(MOVE_COST_USE);
|
||||
map_window->updateBlacking();
|
||||
}
|
||||
|
||||
// teleport-type move
|
||||
void Player::move(sint16 new_x, sint16 new_y, uint8 new_level, bool teleport) {
|
||||
if (actor->move(new_x, new_y, new_level, ACTOR_FORCE_MOVE)) {
|
||||
//map_window->centerMapOnActor(actor);
|
||||
if (party->is_leader(actor)) { // lead party
|
||||
if (actor_manager->get_avatar()->get_hp() == 0) { // need to end turn if Avatar died
|
||||
actor_manager->startActors();
|
||||
return;
|
||||
}
|
||||
party->move(new_x, new_y, new_level); // center everyone first
|
||||
party->follow(0, 0); // then try to move them to correct positions
|
||||
}
|
||||
actor_manager->updateActors(new_x, new_y, new_level);
|
||||
if (teleport && new_level != 0 && new_level != 5)
|
||||
Game::get_game()->get_weather()->set_wind_dir(NUVIE_DIR_NONE);
|
||||
obj_manager->update(new_x, new_y, new_level, teleport); // remove temporary objs, hatch eggs
|
||||
// it's still the player's turn
|
||||
}
|
||||
}
|
||||
|
||||
void Player::moveLeft() {
|
||||
moveRelative(-1, 0);
|
||||
}
|
||||
|
||||
void Player::moveRight() {
|
||||
moveRelative(1, 0);
|
||||
}
|
||||
|
||||
void Player::moveUp() {
|
||||
moveRelative(0, -1);
|
||||
}
|
||||
|
||||
void Player::moveDown() {
|
||||
moveRelative(0, 1);
|
||||
}
|
||||
|
||||
void Player::pass() {
|
||||
Game::get_game()->get_script()->call_player_pass();
|
||||
// uint16 x = actor->x, y = actor->y;
|
||||
// uint8 z = actor->z;
|
||||
|
||||
// Move balloon / raft if required.
|
||||
if (game_type == NUVIE_GAME_U6 && (actor->obj_n == OBJ_U6_INFLATED_BALLOON || actor->obj_n == OBJ_U6_RAFT)) {
|
||||
if (Game::get_game()->get_weather()->get_wind_dir() != NUVIE_DIR_NONE) {
|
||||
moveRelative(0, 0);
|
||||
//return;
|
||||
}
|
||||
}
|
||||
|
||||
if (actor->get_moves_left() > 0)
|
||||
actor->set_moves_left(0); // Pass and use up moves
|
||||
|
||||
// update world around player
|
||||
if (party_mode && party->is_leader(actor)) // lead party
|
||||
party->follow(0, 0);
|
||||
// actor_manager->updateActors(x, y, z); // not needed because position is unchanged
|
||||
_clock->inc_move_counter_by_a_minute(); // doesn't update time
|
||||
actor_manager->startActors(); // end player turn
|
||||
//actor_manager->moveActors();
|
||||
Game::get_game()->time_changed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Enter party mode, with everyone following actor. (must be in the party)
|
||||
*/
|
||||
bool Player::set_party_mode(Actor *new_actor) {
|
||||
if (party->contains_actor(new_actor) || party->is_in_vehicle()) {
|
||||
party_mode = true;
|
||||
set_actor(new_actor);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Enter solo mode as actor. (must be in the party)
|
||||
*/
|
||||
bool Player::set_solo_mode(Actor *new_actor) {
|
||||
if (party->contains_actor(new_actor)) {
|
||||
if (new_actor->is_charmed()) {
|
||||
Game::get_game()->get_scroll()->display_fmt_string("%s fails to respond.\n\n", new_actor->get_name());
|
||||
return false;
|
||||
}
|
||||
party_mode = false;
|
||||
set_actor(new_actor);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the delay in continuous movement for the actor type we control.
|
||||
*/
|
||||
uint32 Player::get_walk_delay() const {
|
||||
if (game_type != NUVIE_GAME_U6)
|
||||
return 125; // normal movement about 8 spaces per second
|
||||
|
||||
if (actor->obj_n == OBJ_U6_BALLOON_BASKET)
|
||||
return 10; // 10x normal (wow!)
|
||||
else if (actor->obj_n == OBJ_U6_SHIP)
|
||||
return 20; // 5x normal
|
||||
else if (actor->obj_n == OBJ_U6_SKIFF)
|
||||
return 50; // 2x normal
|
||||
else if (actor->obj_n == OBJ_U6_RAFT)
|
||||
return 100; // normal
|
||||
else if (actor->obj_n == OBJ_U6_HORSE_WITH_RIDER && party->is_everyone_horsed())
|
||||
return 50; // 2x normal
|
||||
else
|
||||
return 125; // normal movement about 8 spaces per second
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if it's time for the player to take another step.
|
||||
* (call during continuous movement)
|
||||
*/
|
||||
bool Player::check_walk_delay() {
|
||||
static uint32 walk_delay = 0, // start with no delay
|
||||
last_time = _clock->get_ticks();
|
||||
uint32 this_time = _clock->get_ticks();
|
||||
uint32 time_passed = this_time - last_time;
|
||||
|
||||
// subtract time_passed until delay is 0
|
||||
if (sint32(walk_delay - time_passed) < 0)
|
||||
walk_delay = 0;
|
||||
else
|
||||
walk_delay -= time_passed;
|
||||
last_time = this_time; // set each call to get time_passed
|
||||
if (walk_delay == 0) {
|
||||
walk_delay = get_walk_delay(); // reset
|
||||
return true;
|
||||
}
|
||||
return false; // not time yet
|
||||
}
|
||||
|
||||
bool Player::weapon_can_hit(uint16 x, uint16 y) {
|
||||
return actor->weapon_can_hit(actor->get_weapon(current_weapon), x, y);
|
||||
}
|
||||
|
||||
void Player::attack_select_init(bool use_attack_text) {
|
||||
uint16 x, y;
|
||||
uint8 z;
|
||||
|
||||
current_weapon = ACTOR_NO_READIABLE_LOCATION;
|
||||
|
||||
if (attack_select_next_weapon(false, use_attack_text) == false)
|
||||
attack_select_weapon_at_location(ACTOR_NO_READIABLE_LOCATION, use_attack_text); // attack with hands.
|
||||
|
||||
map_window->centerCursor();
|
||||
|
||||
CombatTarget target = party->get_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor));
|
||||
Actor *target_actor = nullptr;
|
||||
|
||||
switch (target.type) {
|
||||
case TARGET_ACTOR :
|
||||
target_actor = actor_manager->get_actor(target.actor_num);
|
||||
uint16 target_x, target_y;
|
||||
map_window->get_pos(&x, &y, &z);
|
||||
target_x = x;
|
||||
target_y = y;
|
||||
if (target_actor && target_actor->is_onscreen() && target_actor->is_alive() && target_actor->is_visible() && actor->weapon_can_hit(actor->get_weapon(current_weapon), target_actor, &target_x, &target_y)) {
|
||||
map_window->moveCursor(target_x - x, target_y - y);
|
||||
} else {
|
||||
party->clear_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor));
|
||||
}
|
||||
break;
|
||||
|
||||
case TARGET_LOCATION :
|
||||
if (target.loc.z == actor->get_z() && weapon_can_hit(target.loc.x, target.loc.y)) {
|
||||
map_window->get_pos(&x, &y, &z);
|
||||
map_window->moveCursor(target.loc.x - x, target.loc.y - y);
|
||||
} else {
|
||||
party->clear_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor));
|
||||
}
|
||||
break;
|
||||
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool Player::attack_select_next_weapon(bool add_newline, bool use_attack_text) {
|
||||
sint8 i;
|
||||
|
||||
for (i = current_weapon + 1; i < ACTOR_MAX_READIED_OBJECTS; i++) {
|
||||
if (attack_select_weapon_at_location(i, add_newline, use_attack_text) == true)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Player::attack_select_weapon_at_location(sint8 location, bool add_newline, bool use_attack_text) {
|
||||
const CombatType *weapon;
|
||||
MsgScroll *scroll = Game::get_game()->get_scroll();
|
||||
|
||||
if (location == ACTOR_NO_READIABLE_LOCATION) {
|
||||
current_weapon = location;
|
||||
if (use_attack_text == false)
|
||||
return true;
|
||||
if (add_newline)
|
||||
scroll->display_string("\n");
|
||||
if (game_type == NUVIE_GAME_U6 && actor->obj_n == OBJ_U6_SHIP)
|
||||
scroll->display_string("Attack with ship cannons-");
|
||||
else
|
||||
scroll->display_string("Attack with bare hands-");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
weapon = actor->get_weapon(location);
|
||||
|
||||
if (weapon && weapon->attack > 0) {
|
||||
current_weapon = location;
|
||||
if (use_attack_text == false)
|
||||
return true;
|
||||
if (add_newline)
|
||||
scroll->display_string("\n");
|
||||
scroll->display_fmt_string("Attack with %s-", obj_manager->get_obj_name(weapon->obj_n));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Player::attack(MapCoord target, Actor *target_actor) {
|
||||
MsgScroll *scroll = Game::get_game()->get_scroll();
|
||||
|
||||
if (weapon_can_hit(target.x, target.y)) {
|
||||
if (!target_actor)
|
||||
target_actor = actor_manager->get_actor(target.x, target.y, actor->get_z());
|
||||
|
||||
actor->attack(current_weapon, target, target_actor);
|
||||
|
||||
if (target_actor) {
|
||||
party->set_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor), target_actor);
|
||||
} else {
|
||||
Obj *target_obj = obj_manager->get_obj(target.x, target.y, actor->get_z());
|
||||
if (target_obj) {
|
||||
party->set_combat_target(actor->id_n == 0 ? 0 : party->get_member_num(actor), MapCoord(target_obj));
|
||||
}
|
||||
}
|
||||
} else
|
||||
scroll->display_string("\nOut of range!\n");
|
||||
|
||||
//actor_manager->startActors(); // end player turn
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch to controlling another actor
|
||||
void Player::update_player(Actor *next_player) {
|
||||
MsgScroll *scroll = Game::get_game()->get_scroll();
|
||||
bool same_actor = (next_player == get_actor());
|
||||
set_actor(next_player); // redirects to ActorManager::set_player()
|
||||
set_mapwindow_centered(true);
|
||||
|
||||
if (!scroll->can_display_prompt() && same_actor)
|
||||
return;
|
||||
scroll->display_string("\n");
|
||||
scroll->display_prompt();
|
||||
}
|
||||
|
||||
/* Rest and repair ship. */
|
||||
void Player::repairShip() {
|
||||
MsgScroll *scroll = Game::get_game()->get_scroll();
|
||||
Actor *ship = get_actor();
|
||||
|
||||
if (ship->get_obj_n() != OBJ_U6_SHIP)
|
||||
return;
|
||||
// ship actor's health is hull strength
|
||||
if (ship->get_hp() + 5 > 100) ship->set_hp(100);
|
||||
else ship->set_hp(ship->get_hp() + 5);
|
||||
|
||||
scroll->display_fmt_string("Hull Strength: %d%%\n", ship->get_hp());
|
||||
|
||||
Game::get_game()->get_script()->call_advance_time(5);
|
||||
Game::get_game()->time_changed();
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
173
engines/ultima/nuvie/core/player.h
Normal file
173
engines/ultima/nuvie/core/player.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_PLAYER_H
|
||||
#define NUVIE_CORE_PLAYER_H
|
||||
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define MOVE_COST_USE 5
|
||||
|
||||
class Configuration;
|
||||
class GameClock;
|
||||
class Actor;
|
||||
class ActorManager;
|
||||
class MapWindow;
|
||||
class Party;
|
||||
class NuvieIO;
|
||||
|
||||
class Player {
|
||||
const Configuration *config;
|
||||
int game_type;
|
||||
GameClock *_clock;
|
||||
Party *party;
|
||||
bool party_mode;
|
||||
bool mapwindow_centered;
|
||||
Actor *actor;
|
||||
ActorManager *actor_manager;
|
||||
ObjManager *obj_manager;
|
||||
|
||||
// char name[14]; We can get the name from the player actor. --SB-X
|
||||
uint8 gender;
|
||||
|
||||
uint8 questf;
|
||||
uint8 karma;
|
||||
uint8 gargishf; // learned Gargish
|
||||
uint8 alcohol; // number of alcoholic drinks consumed
|
||||
|
||||
MapWindow *map_window;
|
||||
|
||||
sint8 current_weapon;
|
||||
|
||||
public:
|
||||
|
||||
Player(const Configuration *cfg);
|
||||
|
||||
bool init(ObjManager *om, ActorManager *am, MapWindow *mw, GameClock *c, Party *p);
|
||||
void init();
|
||||
bool load(NuvieIO *objlist);
|
||||
bool save(NuvieIO *objlist);
|
||||
|
||||
Actor *find_actor();
|
||||
void update_player(Actor *next_player);
|
||||
|
||||
bool is_mapwindow_centered() const {
|
||||
return mapwindow_centered;
|
||||
}
|
||||
void set_mapwindow_centered(bool state);
|
||||
|
||||
bool is_in_vehicle() const {
|
||||
return (get_actor()->get_actor_num() == 0);
|
||||
}
|
||||
|
||||
Party *get_party() {
|
||||
return party;
|
||||
}
|
||||
bool set_party_mode(Actor *new_actor);
|
||||
bool set_solo_mode(Actor *new_actor);
|
||||
bool in_party_mode() const {
|
||||
return party_mode;
|
||||
}
|
||||
|
||||
void set_karma(uint8 val) {
|
||||
karma = val;
|
||||
}
|
||||
uint8 get_karma() const {
|
||||
return karma;
|
||||
}
|
||||
void add_karma(uint8 val = 1);
|
||||
void subtract_karma(uint8 val = 1);
|
||||
void subtract_movement_points(uint8 points);
|
||||
void add_alcohol(uint8 val = 1) {
|
||||
alcohol = clamp_max(alcohol + val, 255);
|
||||
}
|
||||
void dec_alcohol(uint8 val = 1) {
|
||||
if (alcohol > val) {
|
||||
alcohol -= val;
|
||||
} else {
|
||||
alcohol = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void set_quest_flag(uint8 val) {
|
||||
questf = val;
|
||||
}
|
||||
uint8 get_quest_flag() const {
|
||||
return questf;
|
||||
}
|
||||
void set_gargish_flag(uint8 val) {
|
||||
gargishf = val;
|
||||
}
|
||||
uint8 get_gargish_flag() const {
|
||||
return gargishf;
|
||||
}
|
||||
|
||||
void set_actor(Actor *new_actor);
|
||||
Actor *get_actor();
|
||||
const Actor *get_actor() const;
|
||||
void get_location(uint16 *ret_x, uint16 *ret_y, uint8 *ret_level) const;
|
||||
uint8 get_location_level() const;
|
||||
|
||||
const char *get_name();
|
||||
void set_gender(uint8 val) {
|
||||
gender = val;
|
||||
}
|
||||
const char *get_gender_title() const;
|
||||
uint8 get_gender() const {
|
||||
return gender;
|
||||
}
|
||||
|
||||
bool check_moveRelative(sint16 rel_x, sint16 rel_y);
|
||||
void moveRelative(sint16 rel_x, sint16 rel_y, bool mouse_movement = false);
|
||||
void try_open_door(uint16 x, uint16 y, uint8 z);
|
||||
void move(sint16 new_x, sint16 new_y, uint8 new_level, bool teleport);
|
||||
void moveLeft();
|
||||
void moveRight();
|
||||
void moveUp();
|
||||
void moveDown();
|
||||
void pass();
|
||||
void repairShip();
|
||||
|
||||
uint32 get_walk_delay() const;
|
||||
bool check_walk_delay();
|
||||
|
||||
bool weapon_can_hit(uint16 x, uint16 y);
|
||||
void attack_select_init(bool use_attack_text = true);
|
||||
bool attack_select_next_weapon(bool add_newline = false, bool use_attack_text = true);
|
||||
|
||||
void attack(MapCoord target, Actor *target_actor);
|
||||
sint8 get_current_weapon() const {
|
||||
return current_weapon;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool attack_select_weapon_at_location(sint8 location, bool add_newline = false, bool use_attack_text = true);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
947
engines/ultima/nuvie/core/tile_manager.cpp
Normal file
947
engines/ultima/nuvie/core/tile_manager.cpp
Normal file
@@ -0,0 +1,947 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/gui/widgets/console.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/files/nuvie_bmp_file.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/screen/game_palette.h"
|
||||
#include "ultima/nuvie/screen/dither.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/core/look.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define NUM_ORIGINAL_TILES 2048
|
||||
|
||||
static const char article_tbl[][5] = {"", "a ", "an ", "the "};
|
||||
|
||||
static const uint16 U6_ANIM_SRC_TILE[32] = {0x16, 0x16, 0x1a, 0x1a, 0x1e, 0x1e, 0x12, 0x12,
|
||||
0x1a, 0x1e, 0x16, 0x12, 0x16, 0x1a, 0x1e, 0x12,
|
||||
0x1a, 0x1e, 0x1e, 0x12, 0x12, 0x16, 0x16, 0x1a,
|
||||
0x12, 0x16, 0x1e, 0x1a, 0x1a, 0x1e, 0x12, 0x16
|
||||
};
|
||||
|
||||
//static const uint16 U6_WALL_TYPES[1][2] = {{156,176}};
|
||||
|
||||
static const Tile gump_cursor = {
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
//uint8 qty;
|
||||
//uint8 flags;
|
||||
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
{
|
||||
15, 15, 15, 15, 255, 255, 255, 255, 255, 255, 255, 255, 15, 15, 15, 15,
|
||||
15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
|
||||
15, 15, 15, 15, 255, 255, 255, 255, 255, 255, 255, 255, 15, 15, 15, 15
|
||||
}
|
||||
};
|
||||
|
||||
TileManager::TileManager(const Configuration *cfg) : desc_buf(nullptr), config(cfg),
|
||||
look(nullptr), game_counter(0), rgame_counter(0), extendedTiles(nullptr),
|
||||
numTiles(NUM_ORIGINAL_TILES) {
|
||||
memset(tileindex, 0, sizeof(tileindex));
|
||||
memset(tile, 0, sizeof(tile));
|
||||
memset(&animdata, 0, sizeof animdata);
|
||||
|
||||
config->value("config/GameType", game_type);
|
||||
}
|
||||
|
||||
TileManager::~TileManager() {
|
||||
// remove tiles
|
||||
free(desc_buf);
|
||||
delete look;
|
||||
if (extendedTiles) {
|
||||
free(extendedTiles);
|
||||
}
|
||||
}
|
||||
|
||||
bool TileManager::loadTiles() {
|
||||
Common::Path maptiles_path, masktype_path, path;
|
||||
NuvieIOFileRead objtiles_vga;
|
||||
NuvieIOFileRead tileindx_vga;
|
||||
NuvieIOFileRead file;
|
||||
U6Lib_n lib_file;
|
||||
U6Lzw *lzw;
|
||||
uint32 tile_offset;
|
||||
|
||||
unsigned char *tile_data = nullptr;
|
||||
uint32 maptiles_size = 0;
|
||||
uint32 objtiles_size;
|
||||
|
||||
unsigned char *masktype = nullptr;
|
||||
uint32 masktype_size;
|
||||
uint16 i;
|
||||
|
||||
Dither *dither;
|
||||
|
||||
dither = Game::get_game()->get_dither();
|
||||
|
||||
|
||||
config_get_path(config, "maptiles.vga", maptiles_path);
|
||||
config_get_path(config, "masktype.vga", masktype_path);
|
||||
|
||||
lzw = new U6Lzw();
|
||||
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
tile_data = lzw->decompress_file(maptiles_path, maptiles_size);
|
||||
if (tile_data == nullptr) {
|
||||
ConsoleAddError("Decompressing " + maptiles_path.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
masktype = lzw->decompress_file(masktype_path, masktype_size);
|
||||
if (masktype == nullptr) {
|
||||
ConsoleAddError("Decompressing " + masktype_path.toString());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case NUVIE_GAME_MD :
|
||||
case NUVIE_GAME_SE :
|
||||
if (lib_file.open(maptiles_path, 4, game_type) == false) {
|
||||
ConsoleAddError("Opening " + maptiles_path.toString());
|
||||
return false;
|
||||
}
|
||||
maptiles_size = lib_file.get_item_size(0);
|
||||
|
||||
tile_data = lib_file.get_item(0);
|
||||
lib_file.close();
|
||||
|
||||
if (lib_file.open(masktype_path, 4, game_type) == false) {
|
||||
ConsoleAddError("Opening " + masktype_path.toString());
|
||||
return false;
|
||||
}
|
||||
//masktype_size = lib_file.get_item_size(0);
|
||||
|
||||
masktype = lib_file.get_item(0);
|
||||
lib_file.close();
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile_data == nullptr) {
|
||||
ConsoleAddError("Loading maptiles.vga");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (masktype == nullptr) {
|
||||
ConsoleAddError("Loading masktype.vga");
|
||||
return false;
|
||||
}
|
||||
|
||||
config_get_path(config, "objtiles.vga", path);
|
||||
if (objtiles_vga.open(path) == false) {
|
||||
ConsoleAddError("Opening " + path.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
objtiles_size = objtiles_vga.get_size();
|
||||
|
||||
tile_data = (unsigned char *)nuvie_realloc(tile_data, maptiles_size + objtiles_size);
|
||||
|
||||
objtiles_vga.readToBuf(&tile_data[maptiles_size], objtiles_size);
|
||||
|
||||
config_get_path(config, "tileindx.vga", path);
|
||||
|
||||
if (tileindx_vga.open(path) == false) {
|
||||
ConsoleAddError("Opening " + path.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2048; i++) {
|
||||
tile_offset = tileindx_vga.read2() * 16;
|
||||
tile[i].tile_num = i;
|
||||
|
||||
tile[i].transparent = false;
|
||||
|
||||
switch (masktype[i]) {
|
||||
case U6TILE_TRANS :
|
||||
tile[i].transparent = true;
|
||||
memcpy(tile[i].data, &tile_data[tile_offset], 256);
|
||||
break;
|
||||
|
||||
case U6TILE_PLAIN :
|
||||
memcpy(tile[i].data, &tile_data[tile_offset], 256);
|
||||
break;
|
||||
|
||||
case U6TILE_PBLCK :
|
||||
tile[i].transparent = true;
|
||||
decodePixelBlockTile(&tile_data[tile_offset], i);
|
||||
break;
|
||||
}
|
||||
|
||||
dither->dither_bitmap(tile[i].data, 16, 16, tile[i].transparent);
|
||||
|
||||
tileindex[i] = i; //set all tile indexs to default value. this is changed in update() for animated tiles
|
||||
}
|
||||
|
||||
loadAnimData();
|
||||
loadTileFlag();
|
||||
|
||||
free(masktype);
|
||||
free(tile_data);
|
||||
|
||||
look = new Look(config);
|
||||
if (look->init() == false) {
|
||||
ConsoleAddError("Initialising Look Class");
|
||||
return false;
|
||||
}
|
||||
|
||||
desc_buf = (char *)malloc(look->get_max_len() + 6); // add space for "%03d \n\0" or "the \n\0"
|
||||
if (desc_buf == nullptr) {
|
||||
ConsoleAddError("Allocating desc_buf");
|
||||
return false;
|
||||
}
|
||||
|
||||
loadAnimMask();
|
||||
|
||||
#ifdef TILEMANAGER_DEBUG
|
||||
|
||||
look->print();
|
||||
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Dumping tile flags:");
|
||||
for (i = 0; i < 2048; i++) {
|
||||
bool plural;
|
||||
DEBUG(1, LEVEL_DEBUGGING, "%04d : ", i);
|
||||
print_b(LEVEL_DEBUGGING, tile[i].flags1);
|
||||
DEBUG(1, LEVEL_DEBUGGING, " ");
|
||||
print_b(LEVEL_DEBUGGING, tile[i].flags2);
|
||||
DEBUG(1, LEVEL_DEBUGGING, " ");
|
||||
print_b(LEVEL_DEBUGGING, tile[i].flags3);
|
||||
DEBUG(1, LEVEL_DEBUGGING, " %s\n", look->get_description(i, &plural));
|
||||
}
|
||||
#endif
|
||||
|
||||
delete lzw;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Tile *TileManager::get_tile(uint16 tile_num) {
|
||||
if (tile_num < NUM_ORIGINAL_TILES) {
|
||||
return &tile[tileindex[tile_num]];
|
||||
}
|
||||
|
||||
return get_extended_tile(tile_num);
|
||||
}
|
||||
|
||||
Tile *TileManager::get_anim_base_tile(uint16 tile_num) {
|
||||
return &tile[tileindex[U6_ANIM_SRC_TILE[tile_num - 16] / 2]];
|
||||
}
|
||||
|
||||
Tile *TileManager::get_original_tile(uint16 tile_num) {
|
||||
if (tile_num < NUM_ORIGINAL_TILES) {
|
||||
return &tile[tile_num];
|
||||
}
|
||||
|
||||
return get_extended_tile(tile_num);
|
||||
}
|
||||
|
||||
|
||||
Tile *TileManager::get_extended_tile(uint16 tile_num) {
|
||||
if (tile_num <= numTiles) {
|
||||
return &extendedTiles[tile_num - 2048];
|
||||
}
|
||||
return &tile[0];
|
||||
}
|
||||
|
||||
// set entry in tileindex[] to tile num
|
||||
void TileManager::set_tile_index(uint16 tile_index, uint16 tile_num) {
|
||||
tileindex[tile_index] = tile_num;
|
||||
}
|
||||
|
||||
|
||||
void TileManager::set_anim_loop(uint16 tile_num, sint8 loopc, uint8 loop) {
|
||||
for (uint32 i = 0; i < 32; i++)
|
||||
if (animdata.tile_to_animate[i] == tile_num) {
|
||||
animdata.loop_count[i] = loopc;
|
||||
animdata.loop[i] = loop;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *TileManager::lookAtTile(uint16 tile_num, uint16 qty, bool show_prefix) {
|
||||
const char *desc;
|
||||
bool plural;
|
||||
Tile *tileP;
|
||||
|
||||
tileP = get_original_tile(tile_num);
|
||||
|
||||
if (qty > 1)
|
||||
plural = true;
|
||||
else
|
||||
plural = false;
|
||||
|
||||
desc = look->get_description(tileP->tile_num, &plural);
|
||||
if (show_prefix == false)
|
||||
return desc;
|
||||
|
||||
if (qty > 0 &&
|
||||
(plural || Game::get_game()->get_game_type() == NUVIE_GAME_SE))
|
||||
Common::sprintf_s(desc_buf, look->get_max_len() + 6, "%u %s", qty, desc);
|
||||
else
|
||||
Common::sprintf_s(desc_buf, look->get_max_len() + 6, "%s%s", article_tbl[tileP->article_n], desc);
|
||||
|
||||
DEBUG(0, LEVEL_DEBUGGING, "%s (%x): flags1:", desc_buf, tile_num);
|
||||
print_b(LEVEL_INFORMATIONAL, tileP->flags1);
|
||||
DEBUG(1, LEVEL_DEBUGGING, " f2:");
|
||||
print_b(LEVEL_INFORMATIONAL, tileP->flags2);
|
||||
DEBUG(1, LEVEL_DEBUGGING, " f3:");
|
||||
print_b(LEVEL_INFORMATIONAL, tileP->flags3);
|
||||
DEBUG(1, LEVEL_DEBUGGING, "\n");
|
||||
|
||||
return desc_buf;
|
||||
}
|
||||
|
||||
bool TileManager::tile_is_stackable(uint16 tile_num) {
|
||||
return look->has_plural(tile_num); // luteijn: FIXME, doesn't take into account Zu Ylem, Silver Snake Venom, and possibly other stackables that don't have a plural defined.
|
||||
}
|
||||
|
||||
void TileManager::update() {
|
||||
uint16 i;
|
||||
uint16 current_anim_frame = 0;
|
||||
uint16 prev_tileindex;
|
||||
uint8 current_hour = Game::get_game()->get_clock()->get_hour();
|
||||
static sint8 last_hour = -1;
|
||||
|
||||
// cycle animated tiles
|
||||
|
||||
for (i = 0; i < animdata.number_of_tiles_to_animate; i++) {
|
||||
if (animdata.loop_count[i] != 0) {
|
||||
if (animdata.loop[i] == 0) // get next frame
|
||||
current_anim_frame = (game_counter & animdata.and_masks[i]) >> animdata.shift_values[i];
|
||||
else if (animdata.loop[i] == 1) // get previous frame
|
||||
current_anim_frame = (rgame_counter & animdata.and_masks[i]) >> animdata.shift_values[i];
|
||||
prev_tileindex = tileindex[animdata.tile_to_animate[i]];
|
||||
tileindex[animdata.tile_to_animate[i]] = tileindex[animdata.first_anim_frame[i] + current_anim_frame];
|
||||
// loop complete if back to first frame (and not infinite loop)
|
||||
if (animdata.loop_count[i] > 0
|
||||
&& tileindex[animdata.tile_to_animate[i]] != prev_tileindex
|
||||
&& tileindex[animdata.tile_to_animate[i]] == tileindex[animdata.first_anim_frame[i]])
|
||||
--animdata.loop_count[i];
|
||||
} else // not animating
|
||||
tileindex[animdata.tile_to_animate[i]] = tileindex[animdata.first_anim_frame[i]];
|
||||
}
|
||||
|
||||
if (Game::get_game()->anims_paused() == false) { // update counter
|
||||
if (game_counter == 65535)
|
||||
game_counter = 0;
|
||||
else
|
||||
game_counter++;
|
||||
if (rgame_counter == 0)
|
||||
rgame_counter = 65535;
|
||||
else
|
||||
rgame_counter--;
|
||||
}
|
||||
// cycle time-based animations
|
||||
if (current_hour != last_hour)
|
||||
update_timed_tiles(current_hour);
|
||||
last_hour = current_hour;
|
||||
}
|
||||
|
||||
|
||||
bool TileManager::loadTileFlag() {
|
||||
Common::Path filename;
|
||||
NuvieIOFileRead file;
|
||||
uint16 i;
|
||||
|
||||
config_get_path(config, "tileflag", filename);
|
||||
|
||||
if (file.open(filename) == false)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 2048; i++) {
|
||||
tile[i].flags1 = file.read1();
|
||||
if (tile[i].flags1 & 0x2)
|
||||
tile[i].passable = false;
|
||||
else
|
||||
tile[i].passable = true;
|
||||
|
||||
if (tile[i].flags1 & 0x1)
|
||||
tile[i].water = true;
|
||||
else
|
||||
tile[i].water = false;
|
||||
|
||||
if (tile[i].flags1 & 0x8)
|
||||
tile[i].damages = true;
|
||||
else
|
||||
tile[i].damages = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2048; i++) {
|
||||
tile[i].flags2 = file.read1();
|
||||
if (tile[i].flags2 & 0x10)
|
||||
tile[i].toptile = true;
|
||||
else
|
||||
tile[i].toptile = false;
|
||||
|
||||
if ((tile[i].flags2 & 0x4) || (tile[i].flags2 & 0x8))
|
||||
tile[i].boundary = true;
|
||||
else
|
||||
tile[i].boundary = false;
|
||||
|
||||
|
||||
if (tile[i].flags2 & 0x40)
|
||||
tile[i].dbl_height = true;
|
||||
else
|
||||
tile[i].dbl_height = false;
|
||||
|
||||
if (tile[i].flags2 & 0x80)
|
||||
tile[i].dbl_width = true;
|
||||
else
|
||||
tile[i].dbl_width = false;
|
||||
}
|
||||
|
||||
file.seek(0x1400);
|
||||
|
||||
for (i = 0; i < 2048; i++) { // '', 'a', 'an', 'the'
|
||||
tile[i].flags3 = file.read1();
|
||||
tile[i].article_n = (tile[i].flags3 & 0xC0) >> 6;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileManager::loadAnimData() {
|
||||
Common::Path filename;
|
||||
NuvieIOFileRead file;
|
||||
int gameType;
|
||||
config->value("config/GameType", gameType);
|
||||
config_get_path(config, "animdata", filename);
|
||||
|
||||
if (file.open(filename) == false)
|
||||
return false;
|
||||
|
||||
if (file.get_size() != 194)
|
||||
return false;
|
||||
|
||||
animdata.number_of_tiles_to_animate = file.read2();
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
animdata.tile_to_animate[i] = file.read2();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
animdata.first_anim_frame[i] = file.read2();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
animdata.and_masks[i] = file.read1();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
animdata.shift_values[i] = file.read1();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i++) { // FIXME: any data on which tiles don't start as animated?
|
||||
animdata.loop[i] = 0; // loop forwards
|
||||
if ((gameType == NUVIE_GAME_U6 &&
|
||||
(animdata.tile_to_animate[i] == 862 // Crank
|
||||
|| animdata.tile_to_animate[i] == 1009 // Crank
|
||||
|| animdata.tile_to_animate[i] == 1020)) // Chain
|
||||
|| (gameType == NUVIE_GAME_MD
|
||||
&& ((animdata.tile_to_animate[i] >= 1 && animdata.tile_to_animate[i] <= 4) // cistern
|
||||
|| (animdata.tile_to_animate[i] >= 16 && animdata.tile_to_animate[i] <= 23) // canal
|
||||
|| (animdata.tile_to_animate[i] >= 616 && animdata.tile_to_animate[i] <= 627) // watch --pu62 lists as 416-427
|
||||
|| animdata.tile_to_animate[i] == 1992
|
||||
|| animdata.tile_to_animate[i] == 1993
|
||||
|| animdata.tile_to_animate[i] == 1980
|
||||
|| animdata.tile_to_animate[i] == 1981)))
|
||||
|
||||
animdata.loop_count[i] = 0; // don't start animated
|
||||
else
|
||||
animdata.loop_count[i] = -1; // infinite animation
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TileManager::decodePixelBlockTile(const unsigned char *tile_data, uint16 tile_num) {
|
||||
uint8 len;
|
||||
uint16 disp;
|
||||
uint8 x;
|
||||
const unsigned char *ptr;
|
||||
unsigned char *data_ptr;
|
||||
|
||||
// num_blocks = tile_data[0];
|
||||
|
||||
ptr = &tile_data[1];
|
||||
|
||||
data_ptr = tile[tile_num].data;
|
||||
|
||||
memset(data_ptr, 0xff, 256); //set all pixels to transparent.
|
||||
|
||||
for (;;) {
|
||||
disp = (ptr[0] + (ptr[1] << 8));
|
||||
|
||||
x = disp % 160 + (disp >= 1760 ? 160 : 0);
|
||||
|
||||
len = ptr[2];
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
data_ptr += x;
|
||||
|
||||
memcpy(data_ptr, &ptr[3], len);
|
||||
|
||||
data_ptr += len;
|
||||
|
||||
ptr += (3 + len);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool TileManager::loadAnimMask() {
|
||||
Common::Path filename;
|
||||
U6Lzw lzw;
|
||||
uint16 i;
|
||||
unsigned char *animmask;
|
||||
unsigned char *mask_ptr;
|
||||
uint32 animmask_size;
|
||||
|
||||
unsigned char *tile_data;
|
||||
uint16 bytes2clear;
|
||||
uint16 displacement;
|
||||
int gameType;
|
||||
|
||||
config->value("config/GameType", gameType);
|
||||
if (gameType != NUVIE_GAME_U6) //only U6 has animmask.vga
|
||||
return true;
|
||||
|
||||
config_get_path(config, "animmask.vga", filename);
|
||||
|
||||
animmask = lzw.decompress_file(filename, animmask_size);
|
||||
|
||||
if (animmask == nullptr)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 32; i++) { // Make the 32 tiles from index 16 onwards transparent with data from animmask.vga
|
||||
tile_data = tile[16 + i].data;
|
||||
tile[16 + i].transparent = true;
|
||||
|
||||
mask_ptr = animmask + i * 64;
|
||||
bytes2clear = mask_ptr[0];
|
||||
|
||||
if (bytes2clear != 0)
|
||||
memset(tile_data, 0xff, bytes2clear);
|
||||
|
||||
tile_data += bytes2clear;
|
||||
mask_ptr++;
|
||||
|
||||
displacement = mask_ptr[0];
|
||||
bytes2clear = mask_ptr[1];
|
||||
|
||||
mask_ptr += 2;
|
||||
|
||||
for (; displacement != 0 && bytes2clear != 0; mask_ptr += 2) {
|
||||
tile_data += displacement;
|
||||
|
||||
memset(tile_data, 0xff, bytes2clear);
|
||||
tile_data += bytes2clear;
|
||||
|
||||
displacement = mask_ptr[0];
|
||||
bytes2clear = mask_ptr[1];
|
||||
}
|
||||
}
|
||||
|
||||
free(animmask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Update tiles for timed-based animations.
|
||||
*/
|
||||
void TileManager::update_timed_tiles(uint8 hour) {
|
||||
uint16 new_tile;
|
||||
if (Game::get_game()->get_game_type() == NUVIE_GAME_U6) {
|
||||
// sundials
|
||||
if (hour >= 5 && hour <= 6)
|
||||
new_tile = 328;
|
||||
else if (hour >= 7 && hour <= 8)
|
||||
new_tile = 329;
|
||||
else if (hour >= 9 && hour <= 10)
|
||||
new_tile = 330;
|
||||
else if (hour >= 11 && hour <= 12)
|
||||
new_tile = 331;
|
||||
else if (hour >= 13 && hour <= 14)
|
||||
new_tile = 332;
|
||||
else if (hour >= 15 && hour <= 16)
|
||||
new_tile = 333;
|
||||
else if (hour >= 17 && hour <= 18)
|
||||
new_tile = 334;
|
||||
else if (hour >= 19 && hour <= 20)
|
||||
new_tile = 335;
|
||||
else // 9pm to 5am
|
||||
new_tile = 861;
|
||||
set_tile_index(861, new_tile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TileManager::set_anim_first_frame(uint16 anim_number, uint16 new_start_tile_num) {
|
||||
if (anim_number < animdata.number_of_tiles_to_animate) {
|
||||
animdata.first_anim_frame[anim_number] = new_start_tile_num;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns tile rotated about the center by `rotate' degrees. (8-bit; clipped to
|
||||
* standard 16x16 size) It must be deleted after use.
|
||||
* **Fixed-point rotate function taken from the SDL Graphics Extension library
|
||||
* (SGE) (c)1999-2003 Anders Lindstr<74>m, licensed under LGPL v2+.**
|
||||
*/
|
||||
Tile *TileManager::get_rotated_tile(const Tile *tileP, float rotate, uint8 src_y_offset) {
|
||||
Tile *new_tile = new Tile(*tileP); // retain properties of original tileP
|
||||
get_rotated_tile(tileP, new_tile, rotate, src_y_offset);
|
||||
|
||||
return new_tile;
|
||||
}
|
||||
|
||||
void TileManager::get_rotated_tile(const Tile *tileP, Tile *dest_tile, float rotate, uint8 src_y_offset) {
|
||||
unsigned char tile_data[256];
|
||||
|
||||
memset(&dest_tile->data, 255, 256); // fill output with transparent color
|
||||
|
||||
int32 dy, sx, sy;
|
||||
int16 rx, ry;
|
||||
uint16 px = 8, py = 8; // rotate around these coordinates
|
||||
uint16 xmin = 0, xmax = 15, ymin = 0, ymax = 15; // size
|
||||
uint16 sxmin = xmin, sxmax = xmax, symin = ymin, symax = ymax;
|
||||
uint16 qx = 8, qy = 8; // ?? don't remember
|
||||
|
||||
float theta = float(rotate * M_PI / 180.0); /* Convert to radians. */
|
||||
|
||||
int32 const stx = int32((sin(theta)) * 8192.0);
|
||||
int32 const ctx = int32((cos(theta)) * 8192.0);
|
||||
int32 const sty = int32((sin(theta)) * 8192.0);
|
||||
int32 const cty = int32((cos(theta)) * 8192.0);
|
||||
int32 const mx = int32(px * 8192.0);
|
||||
int32 const my = int32(py * 8192.0);
|
||||
|
||||
int32 const dx = xmin - qx;
|
||||
int32 const ctdx = ctx * dx;
|
||||
int32 const stdx = sty * dx;
|
||||
|
||||
int32 const src_pitch = 16;
|
||||
int32 const dst_pitch = 16;
|
||||
uint8 const *src_row = (uint8 const *)&tileP->data;
|
||||
uint8 *dst_pixels = (uint8 *)&dest_tile->data;
|
||||
uint8 *dst_row;
|
||||
|
||||
if (src_y_offset > 0 && src_y_offset < 16) { //shift source down before rotating. This is used by bolt and arrow tiles.
|
||||
memset(&tile_data, 255, 256);
|
||||
memcpy(&tile_data[src_y_offset * 16], &tileP->data, 256 - (src_y_offset * 16));
|
||||
src_row = (uint8 *)&tile_data;
|
||||
}
|
||||
|
||||
for (uint32 y = ymin; y < ymax; y++) {
|
||||
dy = y - qy;
|
||||
|
||||
sx = int32(ctdx + stx * dy + mx); /* Compute source anchor points */
|
||||
sy = int32(cty * dy - stdx + my);
|
||||
|
||||
/* Calculate pointer to dst surface */
|
||||
dst_row = (uint8 *)dst_pixels + y * dst_pitch;
|
||||
|
||||
for (uint32 x = xmin; x < xmax; x++) {
|
||||
rx = int16(sx >> 13); /* Convert from fixed-point */
|
||||
ry = int16(sy >> 13);
|
||||
|
||||
/* Make sure the source pixel is actually in the source image. */
|
||||
if ((rx >= sxmin) && (rx <= sxmax) && (ry >= symin) && (ry <= symax))
|
||||
*(dst_row + x) = *(src_row + ry * src_pitch + rx);
|
||||
|
||||
sx += ctx; /* Incremental transformations */
|
||||
sy -= sty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//memcpy(&dest_tile->data, &tile_data, 256); // replace data
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#if 0 /* old */
|
||||
Tile *TileManager::get_rotated_tile(Tile *tile, float rotate) {
|
||||
float angle = (rotate != 0) ? (rotate * M_PI) / 180.0 : 0; // radians
|
||||
const float mul_x1 = cos(angle); // | x1 y1 |
|
||||
const float mul_y1 = sin(angle); // | x2 y2 |
|
||||
const float mul_x2 = -mul_y1;
|
||||
const float mul_y2 = mul_x1;
|
||||
unsigned char tile_data[256];
|
||||
unsigned char *input = (unsigned char *)&tile->data, *output;
|
||||
|
||||
memset(&tile_data, 255, 256); // fill output with transparent color
|
||||
|
||||
for (sint8 y = -8; y < 8; y++) { // scan input pixels
|
||||
for (sint8 x = -8; x < 8; x++) {
|
||||
sint8 rx = (sint8)rint((x * mul_x1) + (y * mul_x2)); // calculate target pixel
|
||||
sint8 ry = (sint8)rint((x * mul_y1) + (y * mul_y2));
|
||||
if (rx >= -8 && rx <= 7 && ry >= -8 && ry <= 7) {
|
||||
output = (unsigned char *)&tile_data;
|
||||
output += (ry + 8) * 16;
|
||||
output[rx + 8] = input[x + 8]; // copy
|
||||
if (rx <= 6) output[rx + 8 + 1] = input[x + 8]; // copy again to adjacent pixel
|
||||
}
|
||||
}
|
||||
input += 16; // next line
|
||||
}
|
||||
|
||||
Tile *new_tile = new Tile(*tile); // retain properties of original tile
|
||||
memcpy(&new_tile->data, &tile_data, 256); // replace data
|
||||
return new_tile;
|
||||
}
|
||||
#endif
|
||||
|
||||
Tile *TileManager::get_cursor_tile() {
|
||||
Tile *cursor_tile = nullptr;
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
cursor_tile = get_tile(365);
|
||||
break;
|
||||
|
||||
case NUVIE_GAME_MD :
|
||||
cursor_tile = get_tile(265);
|
||||
break;
|
||||
|
||||
case NUVIE_GAME_SE :
|
||||
cursor_tile = get_tile(381);
|
||||
break;
|
||||
}
|
||||
|
||||
return cursor_tile;
|
||||
}
|
||||
|
||||
Tile *TileManager::get_use_tile() {
|
||||
Tile *use_tile = nullptr;
|
||||
switch (game_type) {
|
||||
case NUVIE_GAME_U6 :
|
||||
use_tile = get_tile(364);
|
||||
break;
|
||||
|
||||
case NUVIE_GAME_MD :
|
||||
use_tile = get_tile(264);
|
||||
break;
|
||||
|
||||
case NUVIE_GAME_SE :
|
||||
use_tile = get_tile(380);
|
||||
break;
|
||||
}
|
||||
|
||||
return use_tile;
|
||||
}
|
||||
|
||||
const Tile *TileManager::get_gump_cursor_tile() {
|
||||
return &gump_cursor;
|
||||
}
|
||||
|
||||
Tile *TileManager::loadCustomTiles(const Common::Path &filename, bool overwrite_tiles, bool copy_tileflags, uint16 tile_num_start_offset) {
|
||||
NuvieBmpFile bmp;
|
||||
|
||||
if (bmp.load(filename) == false) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned char *tile_data = bmp.getRawIndexedData();
|
||||
|
||||
uint16 w = bmp.getWidth();
|
||||
uint16 h = bmp.getHeight();
|
||||
uint16 pitch = w;
|
||||
|
||||
if (w % 16 != 0 || h % 16 != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
w = w / 16;
|
||||
h = h / 16;
|
||||
|
||||
uint16 num_tiles = w * h;
|
||||
|
||||
Tile *newTilePtr = nullptr;
|
||||
Tile *origTile = nullptr;
|
||||
if (overwrite_tiles) {
|
||||
newTilePtr = get_original_tile(tile_num_start_offset);
|
||||
} else {
|
||||
newTilePtr = addNewTiles(num_tiles);
|
||||
}
|
||||
|
||||
if (copy_tileflags) {
|
||||
origTile = get_tile(tile_num_start_offset);
|
||||
}
|
||||
|
||||
Tile *t = newTilePtr;
|
||||
|
||||
Dither *dither = Game::get_game()->get_dither();
|
||||
|
||||
for (uint16 y = 0; y < h; y++) {
|
||||
for (uint16 x = 0; x < w; x++) {
|
||||
unsigned char *data = tile_data + (y * 16 * pitch) + (x * 16);
|
||||
for (uint16 i = 0; i < 16; i++) {
|
||||
memcpy(&t->data[i * 16], data, 16);
|
||||
data += pitch;
|
||||
}
|
||||
|
||||
if (origTile) {
|
||||
copyTileMetaData(t, origTile);
|
||||
origTile++;
|
||||
}
|
||||
dither->dither_bitmap(t->data, 16, 16, t->transparent);
|
||||
t++;
|
||||
}
|
||||
}
|
||||
|
||||
return newTilePtr;
|
||||
}
|
||||
|
||||
void TileManager::copyTileMetaData(Tile *dest, Tile *src) {
|
||||
dest->passable = src->passable;
|
||||
dest->water = src->water;
|
||||
dest->toptile = src->toptile;
|
||||
dest->dbl_width = src->dbl_width;
|
||||
dest->dbl_height = src->dbl_height;
|
||||
dest->transparent = src->transparent;
|
||||
dest->boundary = src->boundary;
|
||||
dest->damages = src->damages;
|
||||
dest->article_n = src->article_n;
|
||||
|
||||
dest->flags1 = src->flags1;
|
||||
dest->flags2 = src->flags2;
|
||||
dest->flags3 = src->flags3;
|
||||
}
|
||||
|
||||
Tile *TileManager::addNewTiles(uint16 num_tiles) {
|
||||
Tile *tileDataPtr = (Tile *)realloc(extendedTiles, sizeof(Tile) * (numTiles - NUM_ORIGINAL_TILES + num_tiles));
|
||||
if (tileDataPtr != nullptr) {
|
||||
extendedTiles = tileDataPtr;
|
||||
}
|
||||
|
||||
tileDataPtr += (numTiles - NUM_ORIGINAL_TILES);
|
||||
|
||||
Tile *t = tileDataPtr;
|
||||
for (uint16 i = 0; i < num_tiles; i++, t++) {
|
||||
t->tile_num = numTiles + i;
|
||||
}
|
||||
|
||||
numTiles += num_tiles;
|
||||
|
||||
return tileDataPtr;
|
||||
}
|
||||
|
||||
void TileManager::freeCustomTiles() {
|
||||
if (extendedTiles) {
|
||||
free(extendedTiles);
|
||||
extendedTiles = nullptr;
|
||||
numTiles = NUM_ORIGINAL_TILES;
|
||||
}
|
||||
}
|
||||
|
||||
void TileManager::exportTilesetToBmpFile(const Common::Path &filename, bool fixupU6Shoreline) {
|
||||
NuvieBmpFile bmp;
|
||||
|
||||
unsigned char pal[256 * 4];
|
||||
|
||||
Game::get_game()->get_palette()->loadPaletteIntoBuffer(pal);
|
||||
|
||||
//Magic background colour
|
||||
pal[255 * 4] = 0;
|
||||
pal[255 * 4 + 1] = 0xdf;
|
||||
pal[255 * 4 + 2] = 0xfc;
|
||||
|
||||
bmp.initNewBlankImage(32 * 16, 64 * 16, pal);
|
||||
|
||||
unsigned char *data = bmp.getRawIndexedData();
|
||||
|
||||
for (uint8 i = 0; i < 64; i++) {
|
||||
for (uint8 j = 0; j < 32; j++) {
|
||||
if (fixupU6Shoreline && game_type == NUVIE_GAME_U6 && (i * 32 + j) >= 16 && (i * 32 + j) < 48) { //lay down the base tile for shoreline tiles
|
||||
writeBmpTileData(&data[i * 16 * 512 + j * 16], get_anim_base_tile(i * 32 + j), false);
|
||||
writeBmpTileData(&data[i * 16 * 512 + j * 16], &tile[tileindex[i * 32 + j]], true);
|
||||
} else {
|
||||
writeBmpTileData(&data[i * 16 * 512 + j * 16], &tile[tileindex[i * 32 + j]], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.save(filename);
|
||||
}
|
||||
|
||||
void TileManager::writeBmpTileData(unsigned char *data, const Tile *t, bool transparent) {
|
||||
for (uint8 y = 0; y < 16; y++) {
|
||||
for (uint8 x = 0; x < 16; x++) {
|
||||
if (!transparent || t->data[y * 16 + x] != 255) {
|
||||
data[x] = t->data[y * 16 + x];
|
||||
}
|
||||
}
|
||||
data += 512;
|
||||
}
|
||||
}
|
||||
|
||||
void TileManager::anim_play_repeated(uint8 anim_index) {
|
||||
if (anim_index < get_number_of_animations()) {
|
||||
animdata.loop_count[anim_index] = -1; // infinite animation
|
||||
}
|
||||
}
|
||||
|
||||
void TileManager::anim_stop_playing(uint8 anim_index) {
|
||||
if (anim_index < get_number_of_animations()) {
|
||||
animdata.loop_count[anim_index] = 0; // stop animation
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
227
engines/ultima/nuvie/core/tile_manager.h
Normal file
227
engines/ultima/nuvie/core/tile_manager.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_TILE_MANAGER_H
|
||||
#define NUVIE_CORE_TILE_MANAGER_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Look;
|
||||
|
||||
// tile types stored in masktype.vga
|
||||
|
||||
#define U6TILE_PLAIN 0x0
|
||||
#define U6TILE_TRANS 0x5
|
||||
#define U6TILE_PBLCK 0xA
|
||||
|
||||
#define TILEFLAG_WALL_MASK 0xf0 // 11110000
|
||||
//flags1
|
||||
#define TILEFLAG_WALL_NORTH 0x80
|
||||
#define TILEFLAG_WALL_EAST 0x40
|
||||
#define TILEFLAG_WALL_SOUTH 0x20
|
||||
#define TILEFLAG_WALL_WEST 0x10
|
||||
#define TILEFLAG_IMPEDANCE (TILEFLAG_WALL_NORTH|TILEFLAG_WALL_EAST|TILEFLAG_WALL_SOUTH|TILEFLAG_WALL_WEST)
|
||||
#define TILEFLAG_IMPEDANCE_SHIFT 4
|
||||
#define TILEFLAG_DAMAGING 0x8
|
||||
#define TILEFLAG_WALL 0x4
|
||||
#define TILEFLAG_BLOCKING 0x2
|
||||
#define TILEFLAG_WATER 0x1
|
||||
|
||||
//flags2
|
||||
#define TILEFLAG_DOUBLE_WIDTH 0x80
|
||||
#define TILEFLAG_DOUBLE_HEIGHT 0x40
|
||||
#define TILEFLAG_MISSILE_BOUNDARY 0x20
|
||||
#define TILEFLAG_TOPTILE 0x10
|
||||
#define TILEFLAG_WINDOW 0x8
|
||||
#define TILEFLAG_BOUNDARY 0x4
|
||||
#define TILEFLAG_LIGHT_MSB 0x2
|
||||
#define TILEFLAG_LIGHT_LSB 0x1
|
||||
#define GET_TILE_LIGHT_LEVEL(x) (uint8)(x->flags2 & 0x3) // only use with a pointer
|
||||
//flags3
|
||||
#define TILEFLAG_ARTICLE_MSB 0x80 // 00 01 10 11
|
||||
#define TILEFLAG_ARTICLE_LSB 0x40 // - a an the
|
||||
#define TILEFLAG_UNKNOWN_3_5 0x20
|
||||
#define TILEFLAG_IGNORE 0x10
|
||||
#define TILEFLAG_UNKNOWN_3_3 0x8 // Flammable? Mostly_non_metal_object (not affected by acid-slug)?
|
||||
#define TILEFLAG_FORCED_PASSABLE 0x4
|
||||
#define TILEFLAG_CAN_PLACE_ONTOP 0x2
|
||||
#define TILEFLAG_UNKNOWN_LAVA 0x1 // associated with some lava tiles
|
||||
|
||||
// FIXME These should probably go else where.
|
||||
#define TILE_U6_DIRECTION_CURSOR 364
|
||||
#define TILE_U6_TARGET_CURSOR 365
|
||||
|
||||
#define TILE_U6_GREEN_MAGIC 380
|
||||
#define TILE_U6_PURPLE_MAGIC 381
|
||||
#define TILE_U6_RED_MAGIC 382
|
||||
#define TILE_U6_BLUE_MAGIC 383
|
||||
#define TILE_U6_BLOCKED_EQUIP 389
|
||||
#define TILE_U6_LIGHTNING 392
|
||||
#define TILE_U6_FIREBALL 393
|
||||
#define TILE_U6_SOME_KIND_OF_BLUE_FIELD 394 // ghost's outline for casting/going invis
|
||||
#define TILE_U6_IS_THIS_AN_ICE_SHOT 395
|
||||
#define TILE_U6_KILL_SHOT 396
|
||||
#define TILE_U6_FIRE_SHOT 397
|
||||
#define TILE_U6_SLING_STONE 398
|
||||
#define TILE_U6_CANNONBALL 399
|
||||
#define TILE_U6_EQUIP 410
|
||||
#define TILE_U6_GARGOYLE_LENS_ANIM_1 440
|
||||
#define TILE_U6_GARGOYLE_LENS_ANIM_2 441
|
||||
#define TILE_U6_BRITANNIAN_LENS_ANIM_1 442
|
||||
#define TILE_U6_BRITANNIAN_LENS_ANIM_2 443
|
||||
#define TILE_U6_WIZARD_EYE 563
|
||||
#define TILE_U6_ARROW 566
|
||||
#define TILE_U6_BOLT 567
|
||||
|
||||
#define TILE_SE_BLOCKED_EQUIP 391
|
||||
#define TILE_SE_EQUIP 392
|
||||
|
||||
#define TILE_MD_EQUIP 273
|
||||
#define TILE_MD_BLOCKED_EQUIP 274
|
||||
|
||||
#define TILE_MD_PURPLE_BERRY_MARKER 288
|
||||
#define TILE_MD_GREEN_BERRY_MARKER 289
|
||||
#define TILE_MD_BROWN_BERRY_MARKER 290
|
||||
|
||||
#define TILE_WIDTH 16
|
||||
#define TILE_HEIGHT 16
|
||||
#define TILE_DATA_SIZE 256
|
||||
|
||||
typedef struct {
|
||||
uint16 tile_num;
|
||||
bool passable;
|
||||
bool water;
|
||||
bool toptile;
|
||||
bool dbl_width;
|
||||
bool dbl_height;
|
||||
bool transparent;
|
||||
bool boundary;
|
||||
bool damages;
|
||||
uint8 article_n;
|
||||
//uint8 qty;
|
||||
//uint8 flags;
|
||||
|
||||
uint8 flags1;
|
||||
uint8 flags2;
|
||||
uint8 flags3;
|
||||
|
||||
unsigned char data[256];
|
||||
} Tile;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16 number_of_tiles_to_animate;
|
||||
uint16 tile_to_animate[0x20];
|
||||
uint16 first_anim_frame[0x20];
|
||||
uint8 and_masks[0x20];
|
||||
uint8 shift_values[0x20];
|
||||
sint8 loop_count[0x20]; // times to animate (-1 = infinite)
|
||||
uint8 loop[0x20]; // 0 = loop forwards, 1 = backwards
|
||||
} Animdata;
|
||||
|
||||
class TileManager {
|
||||
Tile tile[2048];
|
||||
uint16 tileindex[2048]; //used for animated tiles
|
||||
uint16 game_counter, rgame_counter;
|
||||
Animdata animdata;
|
||||
Look *look;
|
||||
|
||||
char *desc_buf; // for look
|
||||
const Configuration *config;
|
||||
|
||||
int game_type;
|
||||
|
||||
Tile *extendedTiles;
|
||||
uint16 numTiles;
|
||||
|
||||
public:
|
||||
|
||||
TileManager(const Configuration *cfg);
|
||||
~TileManager();
|
||||
|
||||
bool loadTiles();
|
||||
Tile *get_tile(uint16 tile_num);
|
||||
Tile *get_anim_base_tile(uint16 tile_num);
|
||||
Tile *get_original_tile(uint16 tile_num);
|
||||
void set_tile_index(uint16 tile_index, uint16 tile_num);
|
||||
uint16 get_tile_index(uint16 tile_index) const {
|
||||
return tileindex[tile_index];
|
||||
}
|
||||
void set_anim_loop(uint16 tile_num, sint8 loopc, uint8 loop = 0);
|
||||
|
||||
const char *lookAtTile(uint16 tile_num, uint16 qty, bool show_prefix);
|
||||
bool tile_is_stackable(uint16 tile_num);
|
||||
void update();
|
||||
void update_timed_tiles(uint8 hour);
|
||||
|
||||
uint8 get_number_of_animations() const {
|
||||
return animdata.number_of_tiles_to_animate;
|
||||
}
|
||||
uint16 get_numtiles() const {
|
||||
return numTiles;
|
||||
}
|
||||
uint16 get_anim_tile(uint8 anim_index) const {
|
||||
return anim_index < animdata.number_of_tiles_to_animate ? animdata.tile_to_animate[anim_index] : 0;
|
||||
}
|
||||
uint16 get_anim_first_frame(uint8 anim_index) const {
|
||||
return anim_index < animdata.number_of_tiles_to_animate ? animdata.first_anim_frame[anim_index] : 0;
|
||||
}
|
||||
void set_anim_first_frame(uint16 anim_index, uint16 new_start_tile_num);
|
||||
void anim_play_repeated(uint8 anim_index);
|
||||
void anim_stop_playing(uint8 anim_index);
|
||||
|
||||
|
||||
Tile *get_rotated_tile(const Tile *tile, float rotate, uint8 src_y_offset = 0);
|
||||
void get_rotated_tile(const Tile *tile, Tile *dest_tile, float rotate, uint8 src_y_offset = 0);
|
||||
|
||||
Tile *get_cursor_tile();
|
||||
Tile *get_use_tile();
|
||||
const Tile *get_gump_cursor_tile();
|
||||
|
||||
Tile *loadCustomTiles(const Common::Path &filename, bool overwrite_tiles, bool copy_tileflags, uint16 tile_num_start_offset);
|
||||
void freeCustomTiles();
|
||||
void exportTilesetToBmpFile(const Common::Path &filename, bool fixupU6Shoreline = true);
|
||||
protected:
|
||||
|
||||
bool loadAnimData();
|
||||
bool loadTileFlag();
|
||||
void decodePixelBlockTile(const unsigned char *tile_data, uint16 tile_num);
|
||||
|
||||
bool loadAnimMask();
|
||||
|
||||
private:
|
||||
|
||||
Tile *get_extended_tile(uint16 tile_num);
|
||||
void copyTileMetaData(Tile *dest, Tile *src);
|
||||
Tile *addNewTiles(uint16 num_tiles);
|
||||
|
||||
void writeBmpTileData(unsigned char *data, const Tile *t, bool transparent);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
844
engines/ultima/nuvie/core/timed_event.cpp
Normal file
844
engines/ultima/nuvie/core/timed_event.cpp
Normal file
@@ -0,0 +1,844 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/actors/actor.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/core/party.h"
|
||||
#include "ultima/nuvie/core/events.h"
|
||||
#include "ultima/nuvie/usecode/usecode.h"
|
||||
#include "ultima/nuvie/core/u6_objects.h"
|
||||
#include "ultima/nuvie/actors/u6_work_types.h"
|
||||
#include "ultima/nuvie/misc/u6_llist.h"
|
||||
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/gui/widgets/command_bar.h"
|
||||
#include "ultima/nuvie/views/view_manager.h"
|
||||
#include "ultima/nuvie/views/party_view.h"
|
||||
#include "ultima/nuvie/actors/actor_manager.h"
|
||||
// FIXME: effects use timers, not the other way around (make a movement effect?)
|
||||
#include "ultima/nuvie/core/effect_manager.h"
|
||||
#include "ultima/nuvie/core/effect.h"
|
||||
|
||||
#include "ultima/nuvie/core/timed_event.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define MESG_TIMED CB_TIMED
|
||||
|
||||
/* Activate all events for the current time, deleting those that have fired
|
||||
* and are of no more use. Repeated timers are requeued.
|
||||
*/
|
||||
void TimeQueue::call_timers(uint32 now) {
|
||||
while (!empty() && call_timer(now)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TimeQueue::clear() {
|
||||
while (!empty()) {
|
||||
TimedEvent *event = pop_timer();
|
||||
if (event->tq_can_delete)
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new timed event to queue, which will activate `event' when time is
|
||||
* `evtime'.
|
||||
*/
|
||||
void TimeQueue::add_timer(TimedEvent *tevent) {
|
||||
if (tq.empty()) {
|
||||
tq.push_front(tevent);
|
||||
return;
|
||||
}
|
||||
// in case it's already queued, remove the earlier instance(s)
|
||||
remove_timer(tevent);
|
||||
// add after events with earlier/equal time
|
||||
Std::list<TimedEvent *>::iterator t = tq.begin();
|
||||
while (t != tq.end() && (*t)->time <= tevent->time) t++;
|
||||
tq.insert(t, tevent);
|
||||
}
|
||||
|
||||
|
||||
/* Remove timed event from queue.
|
||||
*/
|
||||
void TimeQueue::remove_timer(TimedEvent *tevent) {
|
||||
Std::list<TimedEvent *>::iterator t = tq.begin();
|
||||
while (t != tq.end()) {
|
||||
if (*t == tevent) {
|
||||
t = tq.erase(t);
|
||||
} else ++t; // this deletes all duplicates
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Remove and return timed event at front of queue, or nullptr if empty.
|
||||
*/
|
||||
TimedEvent *TimeQueue::pop_timer() {
|
||||
TimedEvent *first = nullptr;
|
||||
if (!empty()) {
|
||||
first = tq.front();
|
||||
tq.pop_front(); // remove it
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
|
||||
/* Call timed event at front of queue, whose time is <= `now'.
|
||||
* Returns true if an event handler was called. (false if time isn't up yet)
|
||||
*/
|
||||
bool TimeQueue::call_timer(uint32 now) {
|
||||
if (empty())
|
||||
return false;
|
||||
TimedEvent *tevent = tq.front();
|
||||
if (tevent->defunct) {
|
||||
assert(pop_timer() == tevent);
|
||||
delete_timer(tevent);
|
||||
return false;
|
||||
}
|
||||
if (tevent->time > now)
|
||||
return false;
|
||||
|
||||
//dequeue event here
|
||||
pop_timer(); // remove timer in case we have recursion in the timed() call.
|
||||
|
||||
tevent->timed(now); // fire
|
||||
|
||||
//re-queue if repeating timer.
|
||||
|
||||
if (tevent->repeat_count != 0) { // repeat! same delay, add time
|
||||
// use updated time so it isn't repeated too soon
|
||||
tevent->set_time();
|
||||
// tevent->time = _clock->get_ticks() + tevent->delay;
|
||||
// tevent->time = now + tevent->delay;
|
||||
add_timer(tevent);
|
||||
if (tevent->repeat_count > 0) // don't reduce count if infinite (-1)
|
||||
--tevent->repeat_count;
|
||||
} else
|
||||
delete_timer(tevent); // if not repeated, safe to delete
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Delete a timer, if its can_delete flag is true. Remove from the queue first!
|
||||
*/
|
||||
bool TimeQueue::delete_timer(TimedEvent *tevent) {
|
||||
if (tevent->tq_can_delete) {
|
||||
delete tevent;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Accepts delay, immediate toggle(false), and realtime switch(true). It must
|
||||
* be queued afterwards to start.
|
||||
*/
|
||||
TimedEvent::TimedEvent(uint32 reltime, bool immediate, bool realtime)
|
||||
: delay(reltime), repeat_count(0), ignore_pause(false),
|
||||
real_time(realtime), tq_can_delete(true), defunct(false), tq(nullptr) {
|
||||
if (immediate) // start now (useful if repeat is true)
|
||||
time = 0;
|
||||
else
|
||||
set_time();
|
||||
}
|
||||
|
||||
|
||||
/* Add myself to the TimeQueue.
|
||||
*/
|
||||
void TimedEvent::queue() {
|
||||
Events *event = Game::get_game()->get_event();
|
||||
if (tq == nullptr) {
|
||||
if (real_time)
|
||||
tq = event->get_time_queue();
|
||||
else
|
||||
tq = event->get_game_time_queue();
|
||||
tq->add_timer(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Remove myself from the TimeQueue.
|
||||
*/
|
||||
void TimedEvent::dequeue() {
|
||||
if (tq) {
|
||||
tq->remove_timer(this);
|
||||
tq = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Add delay to current time and set absolute time.
|
||||
*/
|
||||
void TimedEvent::set_time() {
|
||||
GameClock *_clock = Game::get_game()->get_clock();
|
||||
time = delay + (real_time ? _clock->get_ticks()
|
||||
: _clock->get_game_ticks());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*** TimedPartyMove ***/
|
||||
|
||||
/* Party movement to/from dungeon or moongate, with a certain number of
|
||||
* milliseconds between each step.
|
||||
*/
|
||||
TimedPartyMove::TimedPartyMove(MapCoord *d, MapCoord *t, uint32 step_delay)
|
||||
: TimedEvent(step_delay, true) {
|
||||
init(d, t, nullptr);
|
||||
}
|
||||
|
||||
/* Movement through temporary moongate.
|
||||
*/
|
||||
TimedPartyMove::TimedPartyMove(MapCoord *d, MapCoord *t, Obj *use_obj, uint32 step_delay)
|
||||
: TimedEvent(step_delay, true) {
|
||||
init(d, t, use_obj);
|
||||
}
|
||||
|
||||
TimedPartyMove::TimedPartyMove(uint32 step_delay)
|
||||
: TimedEvent(step_delay, true), map_window(nullptr), party(nullptr),
|
||||
dest(nullptr), target(nullptr), moongate(nullptr), actor_to_hide(nullptr),
|
||||
moves_left(0), wait_for_effect(0), falling_in(false) {
|
||||
}
|
||||
|
||||
TimedPartyMove::~TimedPartyMove() {
|
||||
delete dest;
|
||||
delete target;
|
||||
}
|
||||
|
||||
/* Set destination.
|
||||
*/
|
||||
void TimedPartyMove::init(MapCoord *d, MapCoord *t, Obj *use_obj) {
|
||||
map_window = Game::get_game()->get_map_window();
|
||||
party = Game::get_game()->get_party();
|
||||
target = nullptr;
|
||||
moves_left = party->get_party_size() * 2; // step timeout
|
||||
wait_for_effect = 0;
|
||||
actor_to_hide = nullptr;
|
||||
falling_in = false;
|
||||
|
||||
dest = new MapCoord(*d);
|
||||
if (t)
|
||||
target = new MapCoord(*t);
|
||||
moongate = use_obj;
|
||||
|
||||
queue(); // start
|
||||
}
|
||||
|
||||
/* Party movement to/from dungeon or to moongate. Repeated until everyone has
|
||||
* entered, then the entire party is moved to the destination, and this waits
|
||||
* until the visual effects complete.
|
||||
*/
|
||||
void TimedPartyMove::timed(uint32 evtime) {
|
||||
if (wait_for_effect != 0) { // ignores "falling_in"
|
||||
repeat(); // repeat once more (callback() must call stop())
|
||||
return;
|
||||
}
|
||||
stop(); // cancelled further down with repeat(), if still moving
|
||||
|
||||
if (moves_left) {
|
||||
if (((falling_in == false) && move_party())
|
||||
|| ((falling_in == true) && fall_in()))
|
||||
repeat(); // still moving
|
||||
} else // timed out, make sure nobody is walking
|
||||
for (uint32 m = 0; m < party->get_party_size(); m++)
|
||||
party->get_actor(m)->delete_pathfinder();
|
||||
|
||||
// NOTE: set by repeat() or stop()
|
||||
if (repeat_count == 0) { // everyone is in position
|
||||
if (falling_in == false) { // change location, get in formation
|
||||
change_location(); // fade map, move and show party
|
||||
party->stop_walking(true); // return control (and change viewpoint)
|
||||
|
||||
// wait for effect or line up now; Party called unpause_user()
|
||||
Game::get_game()->pause_user();
|
||||
if (wait_for_effect == 0) {
|
||||
delay = 50;
|
||||
set_time(); // fall-in as fast as possible (but visibly)
|
||||
moves_left = party->get_party_size() - 1; // followers
|
||||
falling_in = true;
|
||||
}
|
||||
repeat(); // don't stop yet!
|
||||
} else { // already changed location
|
||||
Game::get_game()->unpause_user();
|
||||
stop_timer(); // done
|
||||
}
|
||||
}
|
||||
if (moves_left > 0)
|
||||
--moves_left;
|
||||
}
|
||||
|
||||
/* Assume the teleport-effect is complete. (don't bother checking msg)
|
||||
*/
|
||||
uint16 TimedPartyMove::callback(uint16 msg, CallBack *caller, void *data) {
|
||||
if (wait_for_effect == 1) { // map-change
|
||||
wait_for_effect = 0;
|
||||
Game::get_game()->unpause_anims();
|
||||
|
||||
delay = 50;
|
||||
set_time(); // fall-in as fast as possible (but visibly)
|
||||
moves_left = party->get_party_size() - 1; // followers
|
||||
falling_in = true;
|
||||
} else if (wait_for_effect == 2) { // vanish
|
||||
wait_for_effect = 0;
|
||||
Game::get_game()->unpause_anims();
|
||||
// move_party();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns true if people are still walking.
|
||||
*/
|
||||
bool TimedPartyMove::move_party() {
|
||||
bool moving = false; // moving or waiting
|
||||
Actor *used_gate = nullptr; // someone just stepped into the gate (for effect)
|
||||
|
||||
if (actor_to_hide) {
|
||||
hide_actor(actor_to_hide);
|
||||
moving = true; // allow at least one more tick so we see last actor hide
|
||||
}
|
||||
actor_to_hide = nullptr;
|
||||
|
||||
for (uint32 a = 0; a < party->get_party_size(); a++) {
|
||||
Actor *person = party->get_actor(a);
|
||||
|
||||
if (person->is_visible()) {
|
||||
MapCoord loc(person->get_location());
|
||||
bool really_visible = map_window->in_window(loc.x, loc.y, loc.z);
|
||||
|
||||
if (a == 0) // FIXME: should be done automatically, but world is frozen
|
||||
map_window->centerMapOnActor(person);
|
||||
|
||||
if (loc != *dest && really_visible) {
|
||||
// nobody has just used the gate (who may still be vanishing)
|
||||
if (!used_gate || loc.distance(*dest) > 1) { // or we aren't close to gate yet
|
||||
if (!person->get_pathfinder())
|
||||
person->pathfind_to(*dest); // start (or continue) going to gate
|
||||
person->update(); // ActorManager is paused
|
||||
loc = person->get_location(); // don't use the old location
|
||||
} else
|
||||
person->delete_pathfinder(); // wait for whoever is at gate
|
||||
}
|
||||
if (loc == *dest // actor may have just arrived this turn
|
||||
|| !really_visible) {
|
||||
person->delete_pathfinder();
|
||||
if (moongate) used_gate = person; // hide after this turn
|
||||
else if (!actor_to_hide) actor_to_hide = person; // hide before next turn
|
||||
}
|
||||
moving = true; // even if at gate, might not be hidden yet
|
||||
}
|
||||
}
|
||||
|
||||
if (used_gate) // wait until now (instead of in loop) so others can catch up before effect
|
||||
hide_actor(used_gate);
|
||||
return moving;
|
||||
}
|
||||
|
||||
/* Start a visual effect and hide the party member.
|
||||
*/
|
||||
void TimedPartyMove::hide_actor(Actor *person) {
|
||||
EffectManager *effect_mgr = Game::get_game()->get_effect_manager();
|
||||
if (wait_for_effect != 2) {
|
||||
if (moongate) { // vanish
|
||||
effect_mgr->watch_effect(this, new VanishEffect()); // wait for callback
|
||||
wait_for_effect = 2;
|
||||
delay = 1;
|
||||
set_time(); // effect will be longer than original delay
|
||||
}
|
||||
person->hide();
|
||||
person->move(target->x, target->y, target->z);
|
||||
}
|
||||
}
|
||||
|
||||
/* Start a visual effect and move the party to the target.
|
||||
*/
|
||||
void TimedPartyMove::change_location() {
|
||||
EffectManager *effect_mgr = Game::get_game()->get_effect_manager();
|
||||
Graphics::ManagedSurface *mapwindow_capture = nullptr;
|
||||
if (wait_for_effect != 1) {
|
||||
bool is_moongate = moongate != nullptr;
|
||||
if (moongate && moongate->obj_n == OBJ_U6_RED_GATE) { // leave blue moongates
|
||||
// get image before deleting moongate
|
||||
mapwindow_capture = map_window->get_sdl_surface();
|
||||
// must delete moongate here because dest may be the same as target...
|
||||
// remove moongate before moving so the tempobj cleanup doesn't bite us
|
||||
Game::get_game()->get_obj_manager()->remove_obj_from_map(moongate);
|
||||
delete_obj(moongate);
|
||||
}
|
||||
|
||||
if (is_moongate)
|
||||
Game::get_game()->get_player()->move(target->x, target->y, target->z, true);
|
||||
else
|
||||
party->move(target->x, target->y, target->z);
|
||||
party->show(); // unhide everyone
|
||||
|
||||
Game::get_game()->get_view_manager()->update(); // we do this to update party view sun moon display if visible.
|
||||
|
||||
if (mapwindow_capture) { // could check this or moongate again
|
||||
// start fade-to
|
||||
effect_mgr->watch_effect(this, /* call me */
|
||||
new FadeEffect(FADE_PIXELATED, FADE_OUT, mapwindow_capture));
|
||||
delete mapwindow_capture;
|
||||
|
||||
Game::get_game()->pause_anims();
|
||||
wait_for_effect = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Pass a few times so everyone in the party can get into formation.
|
||||
* Returns true if party needs to move more to get into formation. */
|
||||
bool TimedPartyMove::fall_in() {
|
||||
bool not_in_position = false; // assume false until someone checks true
|
||||
party->follow(0, 0);
|
||||
for (uint8 p = 1; p < party->get_party_size(); p++) {
|
||||
Actor *follower = party->get_actor(p);
|
||||
MapCoord at = follower->get_location(),
|
||||
desired = party->get_formation_coords(p);
|
||||
follower->update();
|
||||
if (at != desired)
|
||||
not_in_position = true;
|
||||
}
|
||||
return not_in_position;
|
||||
}
|
||||
|
||||
|
||||
/*** TimedPartyMoveToVehicle ***/
|
||||
|
||||
|
||||
/* Party movement to vehicle. Second target is unused.
|
||||
*/
|
||||
TimedPartyMoveToVehicle::TimedPartyMoveToVehicle(MapCoord *d, Obj *obj,
|
||||
uint32 step_delay)
|
||||
: TimedPartyMove(d, nullptr, step_delay) {
|
||||
ship_obj = obj;
|
||||
}
|
||||
|
||||
|
||||
/* Repeat until everyone is in the boat, then start it up.
|
||||
*/
|
||||
void TimedPartyMoveToVehicle::timed(uint32 evtime) {
|
||||
stop(); // cancelled further down with repeat(), if still moving
|
||||
for (uint32 a = 0; a < party->get_party_size(); a++) {
|
||||
Actor *person = party->get_actor(a);
|
||||
MapCoord loc(person->get_location());
|
||||
// not at boat location
|
||||
if (loc != *dest) {
|
||||
// offscreen (or timed out), teleport to target
|
||||
MapWindow *mapWindow = Game::get_game()->get_map_window();
|
||||
if (!mapWindow->in_window(loc.x, loc.y, loc.z) || moves_left == 0)
|
||||
person->move(dest->x, dest->y, dest->z, ACTOR_FORCE_MOVE);
|
||||
else // keep walking
|
||||
person->pathfind_to(*dest);
|
||||
person->update();
|
||||
repeat(); // repeat once more
|
||||
} else { // at destination
|
||||
person->delete_pathfinder();
|
||||
person->hide(); // set in-vehicle
|
||||
}
|
||||
}
|
||||
|
||||
if (repeat_count == 0) { // everyone is in the boat
|
||||
Game::get_game()->get_usecode()->use_obj(ship_obj);
|
||||
party->stop_walking(false); // return control to player
|
||||
}
|
||||
if (moves_left > 0)
|
||||
--moves_left;
|
||||
}
|
||||
|
||||
|
||||
/* Dump one item at a time out of a container, and print it's name to MsgScroll.
|
||||
*/
|
||||
TimedContainerSearch::TimedContainerSearch(Obj *obj) : TimedEvent(500, TIMER_DELAYED) {
|
||||
Game *game = Game::get_game();
|
||||
scroll = game->get_scroll();
|
||||
uc = game->get_usecode();
|
||||
om = game->get_obj_manager();
|
||||
|
||||
container_obj = obj;
|
||||
prev_obj = nullptr;
|
||||
|
||||
//game->set_pause_flags((GamePauseState)(game->get_pause_flags() | PAUSE_USER));
|
||||
game->pause_user();
|
||||
queue(); // start
|
||||
}
|
||||
|
||||
|
||||
void TimedContainerSearch::timed(uint32 evtime) {
|
||||
prev_obj = uc->get_obj_from_container(container_obj);
|
||||
if (prev_obj) {
|
||||
scroll->display_string(om->look_obj(prev_obj, true));
|
||||
if (container_obj->container->end()) // more objects left
|
||||
scroll->display_string(container_obj->container->end()->prev
|
||||
? ", " : ", and ");
|
||||
repeat();
|
||||
} else {
|
||||
Game::get_game()->unpause_user();
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** TimedCallback ***/
|
||||
TimedCallback::TimedCallback(CallBack *t, void *d, uint32 wait_time, bool repeat)
|
||||
: TimedEvent(wait_time, TIMER_DELAYED, TIMER_REALTIME) {
|
||||
set_target(t);
|
||||
set_user_data(d);
|
||||
repeat_count = repeat ? -1 : 0;
|
||||
|
||||
queue(); // start
|
||||
}
|
||||
|
||||
|
||||
void TimedCallback::timed(uint32 evtime) {
|
||||
if (callback_target)
|
||||
message(MESG_TIMED, &evtime);
|
||||
else
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
GameTimedCallback::GameTimedCallback(CallBack *t, void *d, uint32 wait_time, bool repeating)
|
||||
: TimedCallback(t, d, wait_time, repeating) {
|
||||
// re-queue timer using game ticks
|
||||
dequeue();
|
||||
real_time = TIMER_GAMETIME;
|
||||
set_time();// change to game time
|
||||
queue(); // start
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** TimedAdvance: Advance game time by rate until hours has passed. **/
|
||||
#define TIMEADVANCE_PER_SECOND 1000 /* frequency of timer calls */
|
||||
TimedAdvance::TimedAdvance(uint8 hours, uint16 r)
|
||||
: TimedCallback(nullptr, nullptr, 1000 / TIMEADVANCE_PER_SECOND, true),
|
||||
_clock(Game::get_game()->get_clock()),
|
||||
minutes_this_hour(0), minutes(0) {
|
||||
init(hours * 60, r);
|
||||
}
|
||||
|
||||
|
||||
/* Advance to time indicated by timestring, of the format "HH:MM".
|
||||
*/
|
||||
TimedAdvance::TimedAdvance(Std::string timestring, uint16 r)
|
||||
: TimedCallback(nullptr, nullptr, 1000 / TIMEADVANCE_PER_SECOND, true),
|
||||
_clock(Game::get_game()->get_clock()),
|
||||
minutes_this_hour(0), minutes(0) {
|
||||
uint8 hour = 0, minute = 0;
|
||||
|
||||
get_time_from_string(hour, minute, timestring); // set stop time
|
||||
|
||||
// set number of hours and minutes to advance
|
||||
uint16 advance_h = (_clock->get_hour() == hour) ? 24
|
||||
: (_clock->get_hour() < hour) ? (hour - _clock->get_hour())
|
||||
: (24 - (_clock->get_hour() - hour));
|
||||
uint16 advance_m;
|
||||
if (_clock->get_minute() <= minute)
|
||||
advance_m = minute - _clock->get_minute();
|
||||
else {
|
||||
advance_m = (60 - (_clock->get_minute() - minute));
|
||||
if (advance_h > 0)
|
||||
advance_h -= 1;
|
||||
else
|
||||
advance_h = 23;
|
||||
}
|
||||
// go
|
||||
init((advance_h * 60) + advance_m, r);
|
||||
}
|
||||
|
||||
/* Set time advance.
|
||||
*/
|
||||
void TimedAdvance::init(uint16 min, uint16 r) {
|
||||
advance = min;
|
||||
rate = r;
|
||||
prev_evtime = _clock->get_ticks();
|
||||
DEBUG(0, LEVEL_DEBUGGING, "TimedAdvance(): %02d:%02d + %02d:%02d (rate=%d)\n",
|
||||
_clock->get_hour(), _clock->get_minute(), advance / 60, advance % 60, rate);
|
||||
}
|
||||
|
||||
|
||||
/* Advance game time by rate each second. Timer is stopped after after the time
|
||||
* has been advanced as requested.
|
||||
*/
|
||||
void TimedAdvance::timed(uint32 evtime) {
|
||||
uint32 milliseconds = (evtime - prev_evtime) > 0 ? (evtime - prev_evtime) : 1;
|
||||
uint32 fraction = 1000 / milliseconds; // % of second
|
||||
uint32 minutes_per_fraction = rate / (fraction > 0 ? fraction : 1);
|
||||
bool hour_passed = false; // another hour has passed
|
||||
prev_evtime = evtime;
|
||||
|
||||
for (uint32 m = 0; m < minutes_per_fraction; m++) {
|
||||
_clock->inc_minute();
|
||||
minutes += 1;
|
||||
if (++minutes_this_hour > 59) {
|
||||
minutes_this_hour = 0;
|
||||
hour_passed = true;
|
||||
}
|
||||
if (time_passed())
|
||||
break;
|
||||
}
|
||||
Game::get_game()->time_changed();
|
||||
|
||||
if (hour_passed && callback_target) // another hour has passed
|
||||
message(MESG_TIMED, &evtime);
|
||||
|
||||
if (time_passed()) {
|
||||
DEBUG(0, LEVEL_DEBUGGING, "~TimedAdvance(): now %02d:%02d\n", _clock->get_hour(), _clock->get_minute());
|
||||
if (callback_target && !hour_passed) // make sure to call target
|
||||
message(MESG_TIMED, &evtime);
|
||||
stop(); // done
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Returns true when the requested amount of time has passed.
|
||||
*/
|
||||
bool TimedAdvance::time_passed() const {
|
||||
return minutes >= advance;
|
||||
}
|
||||
|
||||
|
||||
/* Set hour and minute from "HH:MM" string.
|
||||
*/
|
||||
void TimedAdvance::get_time_from_string(uint8 &hour, uint8 &minute, Std::string timestring) {
|
||||
char *minute_s = nullptr;
|
||||
char *hour_s = scumm_strdup(timestring.c_str());
|
||||
for (uint32 c = 0; c < strlen(hour_s); c++)
|
||||
if (hour_s[c] == ':') { // get minutes
|
||||
minute_s = scumm_strdup(&hour_s[c + 1]);
|
||||
hour_s[c] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (hour_s) {
|
||||
hour = strtol(hour_s, nullptr, 10);
|
||||
free(hour_s);
|
||||
}
|
||||
if (minute_s) {
|
||||
minute = strtol(minute_s, nullptr, 10);
|
||||
free(minute_s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TimedRestGather::TimedRestGather(uint16 x, uint16 y)
|
||||
: TimedPartyMove(50) {
|
||||
MapCoord center = MapCoord(x, y);
|
||||
init(¢er, 0, 0); // set dest to campfire location
|
||||
Game::get_game()->get_map_window()->updateAmbience();
|
||||
check_campfire();
|
||||
}
|
||||
|
||||
/* Repeat until everyone is in the circle. */
|
||||
void TimedRestGather::timed(uint32 evtime) {
|
||||
stop(); // cancelled further down with repeat(), if still moving
|
||||
|
||||
if (moves_left) {
|
||||
if (move_party())
|
||||
repeat(); // still moving
|
||||
} else // timed out, make sure nobody is walking
|
||||
for (uint32 m = 0; m < party->get_party_size(); m++)
|
||||
party->get_actor(m)->delete_pathfinder();
|
||||
|
||||
if (repeat_count == 0) {
|
||||
check_campfire();
|
||||
Game::get_game()->get_event()->rest();
|
||||
}
|
||||
|
||||
if (moves_left > 0)
|
||||
--moves_left;
|
||||
}
|
||||
|
||||
void TimedRestGather::check_campfire() {
|
||||
ActorManager *actor_manager = Game::get_game()->get_actor_manager();
|
||||
for (sint32 a = 0; a < party->get_party_size(); a++) {
|
||||
Actor *actor = party->get_actor(a);
|
||||
MapCoord loc = actor->get_location();
|
||||
if (loc.x == dest->x && loc.y == dest->y) {
|
||||
for (int x = 0; x < 3; x++)
|
||||
for (int y = 0; y < 3; y++) {
|
||||
if (x == 1 && y == 1)
|
||||
continue;
|
||||
if (actor_manager->get_actor(dest->x + x - 1, dest->y + y - 1, loc.z) == nullptr) {
|
||||
actor->move(dest->x + x - 1, dest->y + y - 1, loc.z);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
actor->face_location(dest->x, dest->y);
|
||||
}
|
||||
}
|
||||
|
||||
bool TimedRestGather::move_party() {
|
||||
bool moving = false; // moving or waiting
|
||||
const sint16 positions[3 * 3] = {
|
||||
7, 0, 4, // list of party members arranged by location
|
||||
3, -1, 2, // campfire is at positions[1][1]
|
||||
5, 1, 6
|
||||
};
|
||||
|
||||
// check everyone in party because they might not be in the positions list
|
||||
for (sint32 a = 0; a < party->get_party_size(); a++) {
|
||||
for (int x = 0; x < 3; x++)
|
||||
for (int y = 0; y < 3; y++)
|
||||
if (positions[x + y * 3] == a) {
|
||||
Actor *actor = party->get_actor(a);
|
||||
MapCoord loc = actor->get_location();
|
||||
MapCoord actor_dest(dest->x + x - 1, dest->y + y - 1, loc.z);
|
||||
if (actor_dest == loc) {
|
||||
actor->face_location(dest->x, dest->y); // look at camp
|
||||
actor->delete_pathfinder();
|
||||
} else {
|
||||
moving = true; // still moving to circle
|
||||
if (actor->get_pathfinder() == 0)
|
||||
actor->pathfind_to(actor_dest.x, actor_dest.y);
|
||||
actor->set_moves_left(actor->get_dexterity());
|
||||
actor->update(); // ActorManager is paused
|
||||
}
|
||||
x = 3;
|
||||
y = 3;
|
||||
break; // break to first loop
|
||||
}
|
||||
}
|
||||
return moving;
|
||||
}
|
||||
|
||||
TimedRest::TimedRest(uint8 hours, Actor *who_will_guard, Obj *campfire_obj)
|
||||
: TimedAdvance(hours, 80), party(Game::get_game()->get_party()),
|
||||
scroll(Game::get_game()->get_scroll()), sleeping(0),
|
||||
print_message(0), lookout(who_will_guard), campfire(campfire_obj),
|
||||
number_that_had_food(0) {
|
||||
}
|
||||
|
||||
/* This is the only place we know that the TimedAdvance has completed. */
|
||||
TimedRest::~TimedRest() {
|
||||
//MapCoord loc = Game::get_game()->get_player()->get_actor()->get_location();
|
||||
assert(campfire != 0);
|
||||
|
||||
campfire->frame_n = 0; // extinguish campfire
|
||||
|
||||
bool can_heal = (Game::get_game()->get_clock()->get_rest_counter() == 0); //only heal once every 12 hours.
|
||||
|
||||
for (int s = 0; s < party->get_party_size(); s++) {
|
||||
Actor *actor = party->get_actor(s);
|
||||
|
||||
if (can_heal && actor->is_sleeping() && s < number_that_had_food) {
|
||||
//heal actors.
|
||||
uint8 hp_diff = actor->get_maxhp() - actor->get_hp();
|
||||
if (hp_diff > 0) {
|
||||
if (hp_diff == 1)
|
||||
hp_diff = 2;
|
||||
|
||||
actor->set_hp(actor->get_hp() + NUVIE_RAND() % (hp_diff / 2) + hp_diff / 2);
|
||||
scroll->display_fmt_string("%s has healed.\n", actor->get_name());
|
||||
}
|
||||
|
||||
}
|
||||
party->get_actor(s)->revert_worktype(); // "wake up"
|
||||
}
|
||||
|
||||
if (can_heal)
|
||||
Game::get_game()->get_clock()->set_rest_counter(12); //don't heal by resting for another 12 hours.
|
||||
|
||||
Game::get_game()->get_player()->set_mapwindow_centered(true);
|
||||
Game::get_game()->unpause_user();
|
||||
Game::get_game()->get_event()->endAction(true); // exit Rest mode
|
||||
}
|
||||
|
||||
void TimedRest::timed(uint32 evtime) {
|
||||
if (sleeping == false) { // mealtime
|
||||
if (evtime - prev_evtime > 500) { // print the next message
|
||||
prev_evtime = evtime; // normally set by TimedAdvance::timed()
|
||||
|
||||
if (print_message == 0)
|
||||
bard_play(); // Iolo plays a tune.
|
||||
else if (print_message <= party->get_party_size())
|
||||
eat(party->get_actor(print_message - 1)); // print each person's message
|
||||
else {
|
||||
sleeping = true; // finished eating
|
||||
sleep();
|
||||
}
|
||||
++print_message;
|
||||
}
|
||||
} else { // sleeping
|
||||
TimedAdvance::timed(evtime);
|
||||
for (int s = 0; s < party->get_party_size(); s++)
|
||||
party->get_actor(s)->update_time(); // checks status effects
|
||||
|
||||
// FIXME: chance for random enemies to attack
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if party has any food, and consume it, allowing the actor to heal. */
|
||||
void TimedRest::eat(Actor *actor) {
|
||||
Obj *food = actor->inventory_get_food(); // search actor's inventory first
|
||||
if (!food)
|
||||
food = party->get_food();
|
||||
|
||||
if (food) {
|
||||
scroll->display_fmt_string("%s has food.\n", actor->get_name());
|
||||
Game::get_game()->get_usecode()->destroy_obj(food, 1);
|
||||
number_that_had_food++;
|
||||
} else
|
||||
scroll->display_fmt_string("%s has no food.\n", actor->get_name());
|
||||
}
|
||||
|
||||
/* Look for a bard in the party and have them play a tune. */
|
||||
void TimedRest::bard_play() {
|
||||
scroll->display_string("Mealtime!\n");
|
||||
for (int b = 0; b < party->get_party_size(); b++)
|
||||
if (party->get_actor(b)->get_obj_n() == OBJ_U6_MUSICIAN) {
|
||||
Actor *bard = party->get_actor(b);
|
||||
bard->morph(OBJ_U6_MUSICIAN_PLAYING);
|
||||
scroll->display_fmt_string("%s plays a tune.\n", bard->get_name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start sleeping until the requested time. One person can stand guard. */
|
||||
void TimedRest::sleep() {
|
||||
// FIXME: changing to SLEEP worktype should automatically do this
|
||||
for (int b = 0; b < party->get_party_size(); b++)
|
||||
if (party->get_actor(b)->get_obj_n() == OBJ_U6_MUSICIAN_PLAYING)
|
||||
party->get_actor(b)->morph(OBJ_U6_MUSICIAN);
|
||||
|
||||
for (int s = 0; s < party->get_party_size(); s++) {
|
||||
Actor *actor = party->get_actor(s);
|
||||
if (actor == lookout) {
|
||||
actor->set_worktype(WORKTYPE_U6_LOOKOUT);
|
||||
scroll->display_fmt_string("\n%s stands guard while the party rests.\n", actor->get_name());
|
||||
} else {
|
||||
actor->set_worktype(WORKTYPE_U6_SLEEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
291
engines/ultima/nuvie/core/timed_event.h
Normal file
291
engines/ultima/nuvie/core/timed_event.h
Normal file
@@ -0,0 +1,291 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_TIMED_EVENT_H
|
||||
#define NUVIE_CORE_TIMED_EVENT_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Actor;
|
||||
class CallBack;
|
||||
class Events;
|
||||
class GameClock;
|
||||
class MapCoord;
|
||||
class MapWindow;
|
||||
class MsgScroll;
|
||||
class Party;
|
||||
class TimedCallbackTarget;
|
||||
class TimedEvent;
|
||||
|
||||
/* A queue for our events.
|
||||
*/
|
||||
class TimeQueue {
|
||||
Std::list<TimedEvent *> tq;
|
||||
public:
|
||||
TimeQueue() : tq() { }
|
||||
~TimeQueue() {
|
||||
clear();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return tq.empty();
|
||||
}
|
||||
void clear();
|
||||
void add_timer(TimedEvent *tevent);
|
||||
void remove_timer(TimedEvent *tevent);
|
||||
TimedEvent *pop_timer();
|
||||
bool delete_timer(TimedEvent *tevent);
|
||||
|
||||
bool call_timer(uint32 now); // activate
|
||||
void call_timers(uint32 now); // activate all
|
||||
};
|
||||
|
||||
|
||||
#define TIMER_IMMEDIATE true
|
||||
#define TIMER_DELAYED false
|
||||
#define TIMER_REALTIME true
|
||||
#define TIMER_GAMETIME false
|
||||
|
||||
/* Events activated by a timer. Add to one of the Events time-queues to start.
|
||||
* (THERE IS ONLY ONE SET) The timed() method is called on activation,
|
||||
* and the timer may be automatically deleted or repeated.
|
||||
*/
|
||||
class TimedEvent {
|
||||
friend class TimeQueue;
|
||||
friend class Events;
|
||||
protected:
|
||||
TimeQueue *tq; // the TimeQueue; so we can add ourself
|
||||
uint32 delay, time; // timer delay, and next absolute time to activate
|
||||
sint8 repeat_count; // repeat how many times? (-1=infinite;0=stop)
|
||||
bool ignore_pause; // activates even if game is paused
|
||||
bool real_time; // time and delay is in milliseconds (false=game ticks/turns)
|
||||
bool tq_can_delete; // can TimeQueue delete this TimedEvent when done?
|
||||
bool defunct; // deleted; don't activate (use to stop timers from outside)
|
||||
|
||||
public:
|
||||
TimedEvent(uint32 reltime, bool immediate = TIMER_DELAYED, bool realtime = TIMER_REALTIME);
|
||||
virtual ~TimedEvent() { }
|
||||
virtual void timed(uint32 evtime) {
|
||||
DEBUG(0, LEVEL_ERROR, "TimedEvent: undefined timer method\n");
|
||||
}
|
||||
|
||||
protected:
|
||||
// stop repeating, remove from tq if it won't delete it
|
||||
// NOTE: potential for bug here, this doesn't prevent it from being called once more
|
||||
void stop() {
|
||||
repeat_count = 0;
|
||||
if (!tq_can_delete) dequeue();
|
||||
}
|
||||
// repeat once (or for requested count)
|
||||
void repeat(uint32 count = 1) {
|
||||
repeat_count = count;
|
||||
}
|
||||
|
||||
public:
|
||||
void queue(); // set tq, add to tq
|
||||
void dequeue(); // remove from tq, clear tq
|
||||
|
||||
void set_time(); // set `time' from `delay'
|
||||
void stop_timer() {
|
||||
stop();
|
||||
defunct = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Print to stdout. (timer test)
|
||||
*/
|
||||
class TimedMessage : public TimedEvent {
|
||||
Std::string msg;
|
||||
public:
|
||||
TimedMessage(uint32 reltime, const char *m, bool repeating = false)
|
||||
: TimedEvent(reltime), msg(m) {
|
||||
repeat_count = repeating ? -1 : 0;
|
||||
}
|
||||
void timed(uint32 evtime) override {
|
||||
DEBUG(0, LEVEL_NOTIFICATION, "Activate! evtime=%d msg=\"%s\"\n", evtime, msg.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Move the party to/from a dungeon or ladder or moongate. Characters off-screen
|
||||
* will teleport.
|
||||
*/
|
||||
class TimedPartyMove : public TimedEvent, public CallBack {
|
||||
protected:
|
||||
MapWindow *map_window;
|
||||
Party *party; // the party
|
||||
MapCoord *dest; // destination, where all actors walk to and disappear
|
||||
MapCoord *target; // where they reappear at the new plane
|
||||
uint32 moves_left; // walk timeout
|
||||
Obj *moongate; // if using a moongate
|
||||
uint8 wait_for_effect; // waiting for a visual effect to complete if not 0
|
||||
Actor *actor_to_hide; // this actor has reached exit and should be hidden
|
||||
bool falling_in;
|
||||
|
||||
public:
|
||||
TimedPartyMove(MapCoord *d, MapCoord *t, uint32 step_delay = 500);
|
||||
TimedPartyMove(MapCoord *d, MapCoord *t, Obj *use_obj, uint32 step_delay = 500);
|
||||
TimedPartyMove(uint32 step_delay = 500);
|
||||
~TimedPartyMove() override;
|
||||
void init(MapCoord *d, MapCoord *t, Obj *use_obj);
|
||||
void timed(uint32 evtime) override;
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data = nullptr) override;
|
||||
|
||||
protected:
|
||||
bool move_party();
|
||||
bool fall_in();
|
||||
void hide_actor(Actor *person);
|
||||
void change_location();
|
||||
};
|
||||
|
||||
|
||||
/* Move the party into a vehicle and start it when everyone is there.
|
||||
*/
|
||||
class TimedPartyMoveToVehicle : public TimedPartyMove {
|
||||
Obj *ship_obj; // vehicle center
|
||||
public:
|
||||
TimedPartyMoveToVehicle(MapCoord *d, Obj *obj, uint32 step_delay = 125);
|
||||
void timed(uint32 evtime) override;
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
class TimedRTC : public TimedEvent {
|
||||
public:
|
||||
TimedRTC() : TimedEvent(1000) {
|
||||
repeat_count = -1;
|
||||
}
|
||||
void timed(uint32 evtime) {
|
||||
// Game::get_game()->get_player()->pass();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
//FIXME: It isn't container search. Its a msgscroll effect to print one line at a time.
|
||||
/* Dump one item at a time out of a container, and print it's name to MsgScroll.
|
||||
*/
|
||||
class TimedContainerSearch : public TimedEvent {
|
||||
MsgScroll *scroll;
|
||||
UseCode *uc;
|
||||
ObjManager *om;
|
||||
Obj *container_obj;
|
||||
Obj *prev_obj; // removed from container
|
||||
public:
|
||||
TimedContainerSearch(Obj *obj);
|
||||
void timed(uint32 evtime) override;
|
||||
};
|
||||
|
||||
|
||||
/* Send timer message to callback target after `wait_time' is up, passing it
|
||||
* some target-defined data.
|
||||
* new TimedCallback(PowderKeg, (void *)my_powderkeg_data, time_to_explode);
|
||||
*/
|
||||
class TimedCallback : public TimedEvent, public CallBack {
|
||||
public:
|
||||
TimedCallback(CallBack *t, void *d, uint32 wait_time,
|
||||
bool repeat = false);
|
||||
~TimedCallback() override { }
|
||||
void timed(uint32 evtime) override;
|
||||
void clear_target() {
|
||||
set_target(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class GameTimedCallback : public TimedCallback {
|
||||
public:
|
||||
GameTimedCallback(CallBack *t, void *d, uint32 wait_time, bool repeat = false);
|
||||
~GameTimedCallback() override { }
|
||||
};
|
||||
|
||||
|
||||
/* Advance gameclock up to 24hours from start time. The callback is used every
|
||||
* hour from the start time, up to and including the stop time.
|
||||
*/
|
||||
class TimedAdvance : public TimedCallback {
|
||||
GameClock *_clock;
|
||||
uint16 advance; // minutes requested
|
||||
uint8 minutes_this_hour;
|
||||
protected:
|
||||
uint16 minutes; // minutes advanced
|
||||
uint16 rate; // rate is minutes-per-second
|
||||
uint32 prev_evtime; // last time the timer was called
|
||||
|
||||
public:
|
||||
TimedAdvance(uint8 hours, uint16 r = 60);
|
||||
TimedAdvance(Std::string timestring, uint16 r = 60); // "HH:MM"
|
||||
~TimedAdvance() override { }
|
||||
|
||||
void init(uint16 min, uint16 r); // start time advance
|
||||
|
||||
void timed(uint32 evtime) override;
|
||||
bool time_passed() const; // returns true if stop time has passed
|
||||
void get_time_from_string(uint8 &hour, uint8 &minute, Std::string timestring);
|
||||
};
|
||||
|
||||
|
||||
/* Camping in the wilderness. Move everyone into a circle and place a campfire
|
||||
* in the center.
|
||||
*/
|
||||
class TimedRestGather : public TimedPartyMove {
|
||||
public:
|
||||
TimedRestGather(uint16 x, uint16 y);
|
||||
|
||||
void timed(uint32 evtime) override;
|
||||
|
||||
protected:
|
||||
bool move_party();
|
||||
void check_campfire();
|
||||
};
|
||||
|
||||
/* Camping in the wilderness. Do a TimedAdvance until the requested time. The
|
||||
* camp can be broken by nearby foes.
|
||||
*/
|
||||
class TimedRest : public TimedAdvance {
|
||||
Party *party;
|
||||
MsgScroll *scroll;
|
||||
Actor *lookout;
|
||||
bool sleeping; // false: mealtime, true: sleeping
|
||||
uint8 print_message; // which message is to be printed next
|
||||
Obj *campfire;
|
||||
uint8 number_that_had_food;
|
||||
public:
|
||||
TimedRest(uint8 hours, Actor *lookout, Obj *campfire_obj);
|
||||
~TimedRest() override;
|
||||
|
||||
void timed(uint32 evtime) override;
|
||||
void eat(Actor *actor);
|
||||
void bard_play();
|
||||
void sleep();
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
506
engines/ultima/nuvie/core/u6_objects.h
Normal file
506
engines/ultima/nuvie/core/u6_objects.h
Normal file
@@ -0,0 +1,506 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_U6_OBJECTS_H
|
||||
#define NUVIE_CORE_U6_OBJECTS_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
//object numbers
|
||||
#define OBJ_U6_NOTHING 0
|
||||
|
||||
#define OBJ_U6_LEATHER_HELM 1
|
||||
#define OBJ_U6_CHAIN_COIF 2
|
||||
#define OBJ_U6_IRON_HELM 3
|
||||
#define OBJ_U6_SPIKED_HELM 4
|
||||
#define OBJ_U6_WINGED_HELM 5
|
||||
#define OBJ_U6_BRASS_HELM 6
|
||||
#define OBJ_U6_GARGOYLE_HELM 7
|
||||
#define OBJ_U6_MAGIC_HELM 8
|
||||
#define OBJ_U6_WOODEN_SHIELD 9
|
||||
#define OBJ_U6_CURVED_HEATER 10
|
||||
#define OBJ_U6_WINGED_SHIELD 11
|
||||
#define OBJ_U6_KITE_SHIELD 12
|
||||
#define OBJ_U6_SPIKED_SHIELD 13
|
||||
#define OBJ_U6_BLACK_SHIELD 14
|
||||
#define OBJ_U6_DOOR_SHIELD 15
|
||||
#define OBJ_U6_MAGIC_SHIELD 16
|
||||
#define OBJ_U6_CLOTH_ARMOUR 17
|
||||
#define OBJ_U6_LEATHER_ARMOR 18
|
||||
#define OBJ_U6_RING_MAIL 19
|
||||
#define OBJ_U6_SCALE_MAIL 20
|
||||
#define OBJ_U6_CHAIN_MAIL 21
|
||||
#define OBJ_U6_PLATE_MAIL 22
|
||||
#define OBJ_U6_MAGIC_ARMOUR 23
|
||||
#define OBJ_U6_SPIKED_COLLAR 24
|
||||
#define OBJ_U6_GUILD_BELT 25
|
||||
#define OBJ_U6_GARGOYLE_BELT 26
|
||||
#define OBJ_U6_LEATHER_BOOTS 27
|
||||
#define OBJ_U6_SWAMP_BOOTS 28
|
||||
#define OBJ_U6_TILE_DIRT 29
|
||||
#define OBJ_U6_TILE_BOARDS 30
|
||||
#define OBJ_U6_TILE_TILES 31
|
||||
#define OBJ_U6_TILE_BLUE_TILES 32
|
||||
|
||||
#define OBJ_U6_SLING 33
|
||||
#define OBJ_U6_CLUB 34
|
||||
#define OBJ_U6_MAIN_GAUCHE 35
|
||||
#define OBJ_U6_SPEAR 36
|
||||
#define OBJ_U6_THROWING_AXE 37
|
||||
#define OBJ_U6_DAGGER 38
|
||||
#define OBJ_U6_MACE 39
|
||||
#define OBJ_U6_MORNING_STAR 40
|
||||
#define OBJ_U6_BOW 41
|
||||
#define OBJ_U6_CROSSBOW 42
|
||||
#define OBJ_U6_SWORD 43
|
||||
#define OBJ_U6_TWO_HANDED_HAMMER 44
|
||||
#define OBJ_U6_TWO_HANDED_AXE 45
|
||||
#define OBJ_U6_TWO_HANDED_SWORD 46
|
||||
#define OBJ_U6_HALBERD 47
|
||||
#define OBJ_U6_GLASS_SWORD 48
|
||||
#define OBJ_U6_BOOMERANG 49
|
||||
#define OBJ_U6_TRIPLE_CROSSBOW 50
|
||||
#define OBJ_U6_FORCE_FIELD 51
|
||||
#define OBJ_U6_WIZARD_EYE 52
|
||||
#define OBJ_U6_OBJECT_53 53
|
||||
#define OBJ_U6_MAGIC_BOW 54
|
||||
#define OBJ_U6_ARROW 55
|
||||
#define OBJ_U6_BOLT 56
|
||||
#define OBJ_U6_SPELLBOOK 57
|
||||
#define OBJ_U6_SPELL 58
|
||||
#define OBJ_U6_CODEX 59
|
||||
#define OBJ_U6_BOOK_OF_PROPHECIES 60
|
||||
#define OBJ_U6_BOOK_OF_CIRCLES 61
|
||||
#define OBJ_U6_VORTEX_CUBE 62
|
||||
#define OBJ_U6_LOCK_PICK 63
|
||||
#define OBJ_U6_KEY 64
|
||||
|
||||
#define OBJ_U6_BLACK_PEARL 65
|
||||
#define OBJ_U6_BLOOD_MOSS 66
|
||||
#define OBJ_U6_GARLIC 67
|
||||
#define OBJ_U6_GINSENG 68
|
||||
#define OBJ_U6_MANDRAKE_ROOT 69
|
||||
#define OBJ_U6_NIGHTSHADE 70
|
||||
#define OBJ_U6_SPIDER_SILK 71
|
||||
#define OBJ_U6_SULFUROUS_ASH 72
|
||||
#define OBJ_U6_MOONSTONE 73
|
||||
#define OBJ_U6_ANKH_AMULET 74
|
||||
#define OBJ_U6_SNAKE_AMULET 75
|
||||
#define OBJ_U6_AMULET_OF_SUBMISSION 76
|
||||
#define OBJ_U6_GEM 77
|
||||
#define OBJ_U6_STAFF 78
|
||||
#define OBJ_U6_LIGHTNING_WAND 79
|
||||
#define OBJ_U6_FIRE_WAND 80
|
||||
#define OBJ_U6_STORM_CLOAK 81
|
||||
#define OBJ_U6_RING 82
|
||||
#define OBJ_U6_FLASK_OF_OIL 83
|
||||
#define OBJ_U6_RED_GATE 84
|
||||
#define OBJ_U6_MOONGATE 85
|
||||
#define OBJ_U6_GAVEL 86
|
||||
#define OBJ_U6_ORB_OF_THE_MOONS 87
|
||||
#define OBJ_U6_GOLD 88
|
||||
#define OBJ_U6_GOLD_NUGGET 89
|
||||
#define OBJ_U6_TORCH 90
|
||||
#define OBJ_U6_ZU_YLEM 91
|
||||
#define OBJ_U6_SNAKE_VENOM 92
|
||||
#define OBJ_U6_SEXTANT 93
|
||||
#define OBJ_U6_SPINNING_WHEEL 94
|
||||
#define OBJ_U6_GRAPES 95
|
||||
#define OBJ_U6_BUTTER 96
|
||||
|
||||
#define OBJ_U6_GARGISH_VOCABULARY 97
|
||||
#define OBJ_U6_CHEST 98
|
||||
#define OBJ_U6_BACKPACK 99
|
||||
#define OBJ_U6_SCYTHE 100
|
||||
#define OBJ_U6_PITCHFORK 101
|
||||
#define OBJ_U6_RAKE 102
|
||||
#define OBJ_U6_PICK 103
|
||||
#define OBJ_U6_SHOVEL 104
|
||||
#define OBJ_U6_HOE 105
|
||||
#define OBJ_U6_WOODEN_LADDER 106
|
||||
#define OBJ_U6_YOKE 107
|
||||
#define OBJ_U6_OVEN_SPATULA 108
|
||||
#define OBJ_U6_ROLLING_PIN 109
|
||||
#define OBJ_U6_SPATULA 110
|
||||
#define OBJ_U6_LADLE 111
|
||||
#define OBJ_U6_COOKING_SHEET 112
|
||||
#define OBJ_U6_CLEAVER 113
|
||||
#define OBJ_U6_KNIFE 114
|
||||
#define OBJ_U6_WINE 115
|
||||
#define OBJ_U6_MEAD 116
|
||||
#define OBJ_U6_ALE 117
|
||||
#define OBJ_U6_WINE_GLASS 118
|
||||
#define OBJ_U6_PLATE 119
|
||||
#define OBJ_U6_MUG 120
|
||||
#define OBJ_U6_SILVERWARE 121
|
||||
#define OBJ_U6_CANDLE 122
|
||||
#define OBJ_U6_MIRROR 123
|
||||
#define OBJ_U6_TUNIC 124
|
||||
#define OBJ_U6_HANGER 125
|
||||
#define OBJ_U6_DRESS 126
|
||||
#define OBJ_U6_SKILLET 127
|
||||
#define OBJ_U6_BREAD 128
|
||||
|
||||
#define OBJ_U6_MEAT_PORTION 129
|
||||
#define OBJ_U6_ROLLS 130
|
||||
#define OBJ_U6_CAKE 131
|
||||
#define OBJ_U6_CHEESE 132
|
||||
#define OBJ_U6_HAM 133
|
||||
#define OBJ_U6_HORSE_CARCASS 134
|
||||
#define OBJ_U6_HORSE_CHOPS 135
|
||||
|
||||
#define OBJ_U6_PANTS 137
|
||||
#define OBJ_U6_PLANT 138
|
||||
|
||||
#define OBJ_U6_DECORATIVE_SWORD 141
|
||||
|
||||
#define OBJ_U6_PICTURE 143
|
||||
#define OBJ_U6_CANDELABRA 145
|
||||
#define OBJ_U6_PERSON_SLEEPING 146
|
||||
#define OBJ_U6_CAULDRON 147
|
||||
|
||||
#define OBJ_U6_SHIP_DEED 149
|
||||
#define OBJ_U6_BOOK 151
|
||||
#define OBJ_U6_SCROLL 152
|
||||
|
||||
#define OBJ_U6_PANPIPES 153
|
||||
#define OBJ_U6_CRYSTAL_BALL 155
|
||||
#define OBJ_U6_HARPSICHORD 156
|
||||
#define OBJ_U6_HARP 157
|
||||
#define OBJ_U6_LUTE 158
|
||||
|
||||
#define OBJ_U6_CLOCK 159
|
||||
#define OBJ_U6_WATER_VASE 161
|
||||
#define OBJ_U6_BED 163
|
||||
#define OBJ_U6_FIREPLACE 164
|
||||
|
||||
#define OBJ_U6_REMAINS 168
|
||||
|
||||
#define OBJ_U6_RUBBER_DUCKY 169
|
||||
|
||||
#define OBJ_U6_FUMAROLE 171
|
||||
#define OBJ_U6_SPIKES 172
|
||||
#define OBJ_U6_TRAP 173
|
||||
#define OBJ_U6_SWITCH 174
|
||||
#define OBJ_U6_ELECTRIC_FIELD 175
|
||||
#define OBJ_U6_DRAWER 176
|
||||
#define OBJ_U6_DESK 177
|
||||
#define OBJ_U6_BUCKET 178
|
||||
#define OBJ_U6_BUCKET_OF_WATER 179
|
||||
#define OBJ_U6_BUCKET_OF_MILK 180
|
||||
#define OBJ_U6_CHURN 181
|
||||
|
||||
#define OBJ_U6_BEEHIVE 182
|
||||
#define OBJ_U6_HONEY_JAR 183
|
||||
#define OBJ_U6_JAR_OF_HONEY 184
|
||||
|
||||
#define OBJ_U6_BARREL 186
|
||||
#define OBJ_U6_BAG 188
|
||||
|
||||
#define OBJ_U6_BASKET 191
|
||||
#define OBJ_U6_CRATE 192
|
||||
|
||||
#define OBJ_U6_PLIERS 203
|
||||
#define OBJ_U6_HAMMER 204
|
||||
|
||||
#define OBJ_U6_BRAZIER 206
|
||||
|
||||
#define OBJ_U6_MEAT 209
|
||||
#define OBJ_U6_RIBS 210
|
||||
|
||||
#define OBJ_U6_DEAD_ANIMAL 211
|
||||
|
||||
#define OBJ_U6_FAN 212
|
||||
|
||||
#define OBJ_U6_MOUSEHOLE 213
|
||||
#define OBJ_U6_CANNON 221
|
||||
#define OBJ_U6_POWDER_KEG 223
|
||||
#define OBJ_U6_THREAD 225
|
||||
#define OBJ_U6_WELL 233
|
||||
#define OBJ_U6_FOUNTAIN 234
|
||||
#define OBJ_U6_SUNDIAL 235
|
||||
#define OBJ_U6_BELL 236
|
||||
|
||||
#define OBJ_U6_RUNE_HONESTY 242
|
||||
#define OBJ_U6_RUNE_COMPASSION 243
|
||||
#define OBJ_U6_RUNE_VALOR 244
|
||||
#define OBJ_U6_RUNE_JUSTICE 245
|
||||
#define OBJ_U6_RUNE_SACRIFICE 246
|
||||
#define OBJ_U6_RUNE_HONOR 247
|
||||
#define OBJ_U6_RUNE_SPIRITUALITY 248
|
||||
#define OBJ_U6_RUNE_HUMILITY 249
|
||||
|
||||
#define OBJ_U6_CHAIR 252
|
||||
#define OBJ_U6_CAMPFIRE 253
|
||||
#define OBJ_U6_CROSS 254
|
||||
#define OBJ_U6_TOMBSTONE 255
|
||||
|
||||
#define OBJ_U6_PROTECTION_RING 256
|
||||
#define OBJ_U6_REGENERATION_RING 257
|
||||
#define OBJ_U6_INVISIBILITY_RING 258
|
||||
|
||||
#define OBJ_U6_FISHING_POLE 264
|
||||
#define OBJ_U6_FISH 265
|
||||
|
||||
#define OBJ_U6_GRAVE 266
|
||||
|
||||
#define OBJ_U6_LEVER 268
|
||||
#define OBJ_U6_DRAWBRIDGE 269
|
||||
#define OBJ_U6_BALLOON_PLANS 270
|
||||
#define OBJ_U6_POTION 275
|
||||
#define OBJ_U6_V_PASSTHROUGH 278
|
||||
#define OBJ_U6_H_PASSTHROUGH 280
|
||||
#define OBJ_U6_FENCE 281
|
||||
#define OBJ_U6_BARS 282
|
||||
#define OBJ_U6_ROPE 284
|
||||
#define OBJ_U6_WATER_WHEEL 287
|
||||
#define OBJ_U6_CRANK 288
|
||||
#define OBJ_U6_LOG_SAW 289
|
||||
#define OBJ_U6_CHAIN 293
|
||||
|
||||
#define OBJ_U6_XYLOPHONE 296
|
||||
|
||||
#define OBJ_U6_OAKEN_DOOR 297
|
||||
#define OBJ_U6_WINDOWED_DOOR 298
|
||||
#define OBJ_U6_CEDAR_DOOR 299
|
||||
#define OBJ_U6_STEEL_DOOR 300
|
||||
|
||||
#define OBJ_U6_DOORWAY 301
|
||||
#define OBJ_U6_LADDER 305
|
||||
#define OBJ_U6_VOLCANO 307
|
||||
#define OBJ_U6_HOLE 308
|
||||
#define OBJ_U6_PORTCULLIS 310
|
||||
#define OBJ_U6_STONE_LION 312
|
||||
|
||||
#define OBJ_U6_FIRE_FIELD 317
|
||||
#define OBJ_U6_POISON_FIELD 318
|
||||
#define OBJ_U6_PROTECTION_FIELD 319
|
||||
#define OBJ_U6_SLEEP_FIELD 320
|
||||
|
||||
#define OBJ_U6_CAVE 326
|
||||
|
||||
#define OBJ_U6_THRONE 327
|
||||
|
||||
#define OBJ_U6_SIGN 332
|
||||
#define OBJ_U6_SIGN_ARROW 333
|
||||
#define OBJ_U6_SECRET_DOOR 334
|
||||
|
||||
#define OBJ_U6_EGG 335
|
||||
#define OBJ_U6_CHARGE 336
|
||||
#define OBJ_U6_EFFECT 337
|
||||
#define OBJ_U6_BLOOD 338
|
||||
#define OBJ_U6_DEAD_BODY 339
|
||||
#define OBJ_U6_DEAD_CYCLOPS 340
|
||||
#define OBJ_U6_DEAD_GARGOYLE 341
|
||||
#define OBJ_U6_GIANT_RAT 342
|
||||
#define OBJ_U6_INSECTS 343
|
||||
#define OBJ_U6_GIANT_BAT 344
|
||||
#define OBJ_U6_GIANT_SQUID 345
|
||||
#define OBJ_U6_REAPER 347
|
||||
#define OBJ_U6_SEA_SERPENT 346
|
||||
#define OBJ_U6_SHEEP 348
|
||||
#define OBJ_U6_DOG 349
|
||||
#define OBJ_U6_DEER 350
|
||||
#define OBJ_U6_WOLF 351
|
||||
#define OBJ_U6_GHOST 352
|
||||
#define OBJ_U6_GREMLIN 353
|
||||
#define OBJ_U6_MOUSE 354
|
||||
#define OBJ_U6_GAZER 355
|
||||
#define OBJ_U6_BIRD 356
|
||||
#define OBJ_U6_CORPSER 357
|
||||
#define OBJ_U6_SNAKE 358
|
||||
#define OBJ_U6_RABBIT 359
|
||||
#define OBJ_U6_ROT_WORMS 360
|
||||
#define OBJ_U6_GIANT_SPIDER 361
|
||||
#define OBJ_U6_WINGED_GARGOYLE 362
|
||||
#define OBJ_U6_GARGOYLE 363
|
||||
#define OBJ_U6_ACID_SLUG 364
|
||||
#define OBJ_U6_TANGLE_VINE_POD 365
|
||||
#define OBJ_U6_TANGLE_VINE 366
|
||||
#define OBJ_U6_DAEMON 367
|
||||
#define OBJ_U6_SKELETON 368
|
||||
#define OBJ_U6_DRAKE 369
|
||||
#define OBJ_U6_HEADLESS 370
|
||||
#define OBJ_U6_TROLL 371
|
||||
#define OBJ_U6_MONGBAT 372
|
||||
#define OBJ_U6_WISP 373
|
||||
#define OBJ_U6_HYDRA 374
|
||||
#define OBJ_U6_SLIME 375
|
||||
#define OBJ_U6_FIGHTER 376
|
||||
#define OBJ_U6_SWASHBUCKLER 377
|
||||
#define OBJ_U6_MAGE 378
|
||||
#define OBJ_U6_VILLAGER 379
|
||||
#define OBJ_U6_MERCHANT 380
|
||||
#define OBJ_U6_CHILD 381
|
||||
#define OBJ_U6_GUARD 382
|
||||
|
||||
#define OBJ_U6_JESTER 383
|
||||
#define OBJ_U6_PEASANT 384
|
||||
#define OBJ_U6_FARMER 385
|
||||
#define OBJ_U6_MUSICIAN 386
|
||||
#define OBJ_U6_WOMAN 387
|
||||
#define OBJ_U6_CAT 388
|
||||
#define OBJ_U6_MUSICIAN_PLAYING 392
|
||||
#define OBJ_U6_SHRINE 393
|
||||
#define OBJ_U6_BRITANNIAN_LENS 394
|
||||
#define OBJ_U6_GARGOYLE_LENS 396
|
||||
#define OBJ_U6_STATUE_OF_MONDAIN 397
|
||||
#define OBJ_U6_STATUE_OF_MINAX 398
|
||||
#define OBJ_U6_STATUE_OF_EXODUS 399
|
||||
|
||||
|
||||
#define OBJ_U6_LORD_BRITISH 409
|
||||
#define OBJ_U6_AVATAR 410
|
||||
|
||||
#define OBJ_U6_DRAGON 411
|
||||
|
||||
#define OBJ_U6_SHIP 412
|
||||
#define OBJ_U6_SILVER_SERPENT 413
|
||||
#define OBJ_U6_SKIFF 414
|
||||
#define OBJ_U6_RAFT 415
|
||||
#define OBJ_U6_NO_VEHICLE 416
|
||||
|
||||
#define OBJ_U6_QUEST_GATE 416
|
||||
#define OBJ_U6_DRAGON_EGG 417
|
||||
#define OBJ_U6_PULL_CHAIN 419
|
||||
#define OBJ_U6_BALLOON 420
|
||||
#define OBJ_U6_MAMMOTH_SILK_BAG 421
|
||||
#define OBJ_U6_BALLOON_BASKET 422
|
||||
#define OBJ_U6_INFLATED_BALLOON 423
|
||||
|
||||
#define OBJ_U6_CYCLOPS 424
|
||||
#define OBJ_U6_HYDRA_BODY 425
|
||||
|
||||
#define OBJ_U6_GIANT_SCORPION 426
|
||||
#define OBJ_U6_GIANT_ANT 427
|
||||
#define OBJ_U6_COW 428
|
||||
#define OBJ_U6_ALLIGATOR 429
|
||||
#define OBJ_U6_HORSE 430
|
||||
#define OBJ_U6_HORSE_WITH_RIDER 431
|
||||
|
||||
#define OBJ_U6__LAST_ 431
|
||||
|
||||
// Savage Empire
|
||||
#define OBJ_SE_MAGNESIUM_RIBBON 10
|
||||
#define OBJ_SE_SPEAR 26
|
||||
#define OBJ_SE_THROWING_AXE 27
|
||||
#define OBJ_SE_POISONED_DART 36
|
||||
#define OBJ_SE_RIFLE_BULLET 41
|
||||
#define OBJ_SE_KNIFE 44
|
||||
#define OBJ_SE_ARROW 45
|
||||
#define OBJ_SE_TURTLE_BAIT 47
|
||||
#define OBJ_SE_FEATHER 48
|
||||
#define OBJ_SE_CHOCOLATL 54
|
||||
#define OBJ_SE_PINDE 55
|
||||
#define OBJ_SE_YOPO 56
|
||||
#define OBJ_SE_MORTAR 59
|
||||
#define OBJ_SE_GRINDING_STONE 60
|
||||
#define OBJ_SE_JUG_OF_PLACHTA 63
|
||||
#define OBJ_SE_GOLD 69
|
||||
#define OBJ_SE_GOLD_NUGGET 70
|
||||
#define OBJ_SE_DIAMOND 72
|
||||
#define OBJ_SE_EMERALD 73
|
||||
#define OBJ_SE_RUBY 74
|
||||
#define OBJ_SE_CORN_MEAL 93
|
||||
#define OBJ_SE_BOTTLE_OF_LIQUOR 95
|
||||
#define OBJ_SE_JAR 97
|
||||
#define OBJ_SE_TORTILLA 102
|
||||
#define OBJ_SE_MEAT_103 103
|
||||
#define OBJ_SE_BERRY 104
|
||||
#define OBJ_SE_CAKE 105
|
||||
#define OBJ_SE_CORN 108
|
||||
#define OBJ_SE_BEAN 109
|
||||
#define OBJ_SE_MEAT_110 110
|
||||
#define OBJ_SE_ORCHID 115
|
||||
#define OBJ_SE_PEPPER 120
|
||||
#define OBJ_SE_SULFUR 123
|
||||
#define OBJ_SE_CHARCOAL 129
|
||||
#define OBJ_SE_POTASSIUM_NITRATE 130
|
||||
#define OBJ_SE_SOFT_CLAY_POT 132
|
||||
#define OBJ_SE_FIRED_CLAY_POT 133
|
||||
#define OBJ_SE_CLOTH_STRIP 134
|
||||
#define OBJ_SE_GRENADE 137
|
||||
#define OBJ_SE_TAR 139
|
||||
#define OBJ_SE_WATER 140
|
||||
#define OBJ_SE_CLOTH 180
|
||||
#define OBJ_SE_JUG 181
|
||||
#define OBJ_SE_POUCH 182
|
||||
#define OBJ_SE_BASKET 183
|
||||
#define OBJ_SE_POT 184
|
||||
#define OBJ_SE_TARRED_CLOTH_STRIP 191
|
||||
#define OBJ_SE_CLAY 192
|
||||
#define OBJ_SE_GUNPOWDER 204
|
||||
#define OBJ_SE_BRANCH 206
|
||||
#define OBJ_SE_TORCH 208
|
||||
#define OBJ_SE_FLAX 210
|
||||
#define OBJ_SE_RIB_BONE 211
|
||||
#define OBJ_SE_CHOP 214
|
||||
#define OBJ_SE_DEVICE 240
|
||||
#define OBJ_SE_DEAD_BODY 249
|
||||
|
||||
// Martian Dreams
|
||||
#define OBJ_MD_DOLLAR 24
|
||||
#define OBJ_MD_PISTOL_ROUND 57
|
||||
#define OBJ_MD_SHOTGUN_SHELL 58
|
||||
#define OBJ_MD_RIFLE_ROUND 59
|
||||
#define OBJ_MD_ELEPHANT_GUN_ROUND 60
|
||||
#define OBJ_MD_SLING_STONE 63
|
||||
#define OBJ_MD_ARROW 64
|
||||
#define OBJ_MD_BERRY 73
|
||||
#define OBJ_MD_BERRY1 74
|
||||
#define OBJ_MD_BERRY2 75
|
||||
#define OBJ_MD_BERRY3 76
|
||||
#define OBJ_MD_BERRY4 77
|
||||
#define OBJ_MD_BACKPACK 80
|
||||
#define OBJ_MD_LARGE_SACK 81
|
||||
#define OBJ_MD_SMALL_POUCH 82
|
||||
#define OBJ_MD_BRASS_CHEST 83
|
||||
#define OBJ_MD_OBSIDIAN_BOX 85
|
||||
#define OBJ_MD_WOODEN_CRATE 86
|
||||
#define OBJ_MD_STEAMER_TRUNK 87
|
||||
#define OBJ_MD_CARPET_BAG 89
|
||||
#define OBJ_MD_POCKETWATCH 91
|
||||
#define OBJ_MD_MASONIC_SYMBOL 92
|
||||
#define OBJ_MD_SPECTACLES 93
|
||||
|
||||
#define OBJ_MD_BARREL 104
|
||||
#define OBJ_MD_MATCH 107
|
||||
#define OBJ_MD_TORCH 109
|
||||
#define OBJ_MD_PAGE 122
|
||||
#define OBJ_MD_CAN_OF_LAMP_OIL 124
|
||||
#define OBJ_MD_BLOB_OF_OXIUM 131
|
||||
#define OBJ_MD_RUBLE 132
|
||||
#define OBJ_MD_LEAD_BOX 139
|
||||
#define OBJ_MD_WORMSBANE_SEED 158
|
||||
#define OBJ_MD_CRATE 284
|
||||
#define OBJ_MD_BAG 285
|
||||
#define OBJ_MD_BRASS_TRUNK 304
|
||||
#define OBJ_MD_OXYGENATED_AIR_BOTTLE 324
|
||||
#define OBJ_MD_DREAMSTUFF 331
|
||||
#define OBJ_MD_DEAD_BODY 341
|
||||
#define OBJ_MD_CHIP_OF_RADIUM 449
|
||||
#define OBJ_MD_DREAM_TELEPORTER 461
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
256
engines/ultima/nuvie/core/weather.cpp
Normal file
256
engines/ultima/nuvie/core/weather.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/misc/u6_llist.h"
|
||||
#include "ultima/nuvie/core/weather.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
#include "ultima/nuvie/core/game_clock.h"
|
||||
#include "ultima/nuvie/save/obj_list.h"
|
||||
#include "ultima/nuvie/core/timed_event.h"
|
||||
#include "ultima/nuvie/views/view_manager.h"
|
||||
#include "ultima/nuvie/gui/widgets/map_window.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/script/script.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
//the longest we will go before having a change in wind direction
|
||||
#define WEATHER_MAX_WIND 30
|
||||
|
||||
Weather::Weather(const Configuration *cfg, GameClock *c, nuvie_game_t type)
|
||||
: config(cfg), _clock(c), gametype(type), wind_dir(NUVIE_DIR_NONE),
|
||||
wind_timer(nullptr) {
|
||||
string s;
|
||||
config->value(config_get_game_key(config) + "/displayed_wind_dir", s, "from");
|
||||
if (s == "to")
|
||||
display_from_wind_dir = false;
|
||||
else
|
||||
display_from_wind_dir = true;
|
||||
}
|
||||
|
||||
Weather::~Weather() {
|
||||
}
|
||||
|
||||
bool Weather::load(NuvieIO *objlist) {
|
||||
clear_wind();
|
||||
|
||||
if (gametype == NUVIE_GAME_U6) {
|
||||
wind_dir = load_wind(objlist);
|
||||
set_wind_change_callback(); //set a timer to change the wind direction in the future.
|
||||
send_wind_change_notification_callback();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MapCoord Weather::get_moonstone(uint8 moonstone) {
|
||||
if (moonstone < 8) // FIXME: hardcoded constant
|
||||
return Game::get_game()->get_script()->call_moonstone_get_loc(moonstone + 1);
|
||||
|
||||
DEBUG(0, LEVEL_ERROR, "get_moonstone(%d): Moonstone out of range\n", moonstone);
|
||||
return MapCoord(0, 0, 0);
|
||||
}
|
||||
bool Weather::set_moonstone(uint8 moonstone, MapCoord where) {
|
||||
if (moonstone < 8) { // FIXME: hardcoded constant
|
||||
Game::get_game()->get_script()->call_moonstone_set_loc(moonstone + 1, where); //phase starts at 1 in script.
|
||||
return true;
|
||||
}
|
||||
DEBUG(0, LEVEL_ERROR, "set_moonstone(%d): Moonstone out of range\n", moonstone);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Weather::update_moongates() {
|
||||
Game::get_game()->get_script()->call_update_moongates(is_moon_visible());
|
||||
}
|
||||
|
||||
NuvieDir Weather::load_wind(NuvieIO *objlist) {
|
||||
const NuvieDir wind_tbl[8] = {
|
||||
NUVIE_DIR_N,
|
||||
NUVIE_DIR_NE,
|
||||
NUVIE_DIR_E,
|
||||
NUVIE_DIR_SE,
|
||||
NUVIE_DIR_S,
|
||||
NUVIE_DIR_SW,
|
||||
NUVIE_DIR_W,
|
||||
NUVIE_DIR_NW
|
||||
};
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_WIND_DIR);
|
||||
uint8 objlist_wind = objlist->read1();
|
||||
|
||||
if (objlist_wind > 7) //objlist 0xff = Calm 'C'
|
||||
return NUVIE_DIR_NONE;
|
||||
|
||||
return wind_tbl[objlist_wind];
|
||||
}
|
||||
|
||||
void Weather::clear_wind() {
|
||||
if (wind_timer) {
|
||||
wind_timer->stop_timer();
|
||||
wind_timer = nullptr;
|
||||
}
|
||||
|
||||
|
||||
wind_dir = NUVIE_DIR_NONE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool Weather::save(NuvieIO *objlist) {
|
||||
if (gametype == NUVIE_GAME_U6) {
|
||||
save_wind(objlist);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Weather::save_wind(NuvieIO *objlist) {
|
||||
const uint8 wind_tbl[] = {
|
||||
OBJLIST_U6_WIND_DIR_N,
|
||||
OBJLIST_U6_WIND_DIR_S,
|
||||
OBJLIST_U6_WIND_DIR_E,
|
||||
OBJLIST_U6_WIND_DIR_W,
|
||||
OBJLIST_U6_WIND_DIR_NE,
|
||||
OBJLIST_U6_WIND_DIR_SE,
|
||||
OBJLIST_U6_WIND_DIR_SW,
|
||||
OBJLIST_U6_WIND_DIR_NW,
|
||||
OBJLIST_U6_WIND_DIR_C
|
||||
};
|
||||
|
||||
objlist->seek(OBJLIST_OFFSET_U6_WIND_DIR);
|
||||
objlist->write1(wind_tbl[wind_dir]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Weather::is_eclipse() const {
|
||||
if (gametype != NUVIE_GAME_U6 || _clock->get_timer(GAMECLOCK_TIMER_U6_ECLIPSE) == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Weather::is_moon_visible() const {
|
||||
//FIXME this is duplicated logic. Maybe we should look at how the original works out moon locations
|
||||
|
||||
uint8 day = _clock->get_day();
|
||||
uint8 hour = _clock->get_hour();
|
||||
// trammel (starts 1 hour ahead of sun)
|
||||
uint8 phase = uint8(nearbyint((day - 1) / TRAMMEL_PHASE)) % 8;
|
||||
uint8 posA = ((hour + 1) + 3 * phase) % 24; // advance 3 positions each phase-change
|
||||
if (posA >= 5 && posA <= 19)
|
||||
return true;
|
||||
|
||||
// felucca (starts 1 hour behind sun)
|
||||
// ...my FELUCCA_PHASE may be wrong but this method works with it...
|
||||
sint8 phaseb = (day - 1) % uint8(nearbyint(FELUCCA_PHASE * 8)) - 1;
|
||||
phase = (phaseb >= 0) ? phaseb : 0;
|
||||
uint8 posB = ((hour - 1) + 3 * phase) % 24; // advance 3 positions per phase-change
|
||||
|
||||
if (posB >= 5 && posB <= 19)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
string Weather::get_wind_dir_str() const {
|
||||
if (display_from_wind_dir) {
|
||||
static const char from_names[9][3] = {"N", "E", "S", "W", "NE", "SE", "SW", "NW", "C"};
|
||||
return from_names[wind_dir];
|
||||
} else {
|
||||
static const char to_names[9][3] = {"S", "W", "N", "E", "SW", "NW", "NE", "SE", "C"};
|
||||
return to_names[wind_dir];
|
||||
}
|
||||
}
|
||||
|
||||
void Weather::change_wind_dir() {
|
||||
NuvieDir new_wind_dir = static_cast<NuvieDir>(NUVIE_RAND() % 9);
|
||||
|
||||
set_wind_dir(new_wind_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
bool Weather::set_wind_dir(NuvieDir new_wind_dir) {
|
||||
NuvieDir old_wind_dir = wind_dir;
|
||||
|
||||
if (new_wind_dir >= 9)
|
||||
return false;
|
||||
|
||||
clear_wind();
|
||||
if (Game::get_game()->get_map_window()->in_dungeon_level())
|
||||
wind_dir = NUVIE_DIR_NONE;
|
||||
else
|
||||
wind_dir = new_wind_dir;
|
||||
|
||||
if (wind_dir != old_wind_dir)
|
||||
send_wind_change_notification_callback();
|
||||
|
||||
set_wind_change_callback();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void Weather::set_wind_change_callback() {
|
||||
uint16 length = (NUVIE_RAND() % WEATHER_MAX_WIND) + 1;
|
||||
uint8 *cb_msgid = new uint8;
|
||||
*cb_msgid = WEATHER_CB_CHANGE_WIND_DIR;
|
||||
wind_timer = new GameTimedCallback((CallBack *)this, cb_msgid, length);
|
||||
DEBUG(0, LEVEL_DEBUGGING, "Adding wind change timer. Length = %d\n", length);
|
||||
}
|
||||
|
||||
inline void Weather::send_wind_change_notification_callback() {
|
||||
for (CallBack *cb : wind_change_notification_list)
|
||||
cb->callback(WEATHER_CB_CHANGE_WIND_DIR, (CallBack *)this, nullptr);
|
||||
}
|
||||
|
||||
bool Weather::add_wind_change_notification_callback(CallBack *caller) {
|
||||
wind_change_notification_list.push_back(caller);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint16 Weather::callback(uint16 msg, CallBack *caller, void *data) {
|
||||
uint8 *cb_msgid = (uint8 *)callback_user_data;
|
||||
|
||||
switch (*cb_msgid) {
|
||||
case WEATHER_CB_CHANGE_WIND_DIR :
|
||||
wind_timer = nullptr;
|
||||
change_wind_dir();
|
||||
break;
|
||||
default :
|
||||
DEBUG(0, LEVEL_ERROR, "Weather: Unknown callback!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
delete cb_msgid;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
97
engines/ultima/nuvie/core/weather.h
Normal file
97
engines/ultima/nuvie/core/weather.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_CORE_WEATHER_H
|
||||
#define NUVIE_CORE_WEATHER_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/misc/call_back.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class NuvieIO;
|
||||
class CallBack;
|
||||
class GameClock;
|
||||
class GameTimedCallback;
|
||||
|
||||
using Std::list;
|
||||
using Std::string;
|
||||
|
||||
//our callbacks
|
||||
|
||||
#define WEATHER_CB_CHANGE_WIND_DIR 1
|
||||
#define WEATHER_CB_END_ECLIPSE 2
|
||||
#define WEATHER_WIND_CALM 8
|
||||
|
||||
class Weather: public CallBack {
|
||||
const Configuration *config;
|
||||
GameClock *_clock;
|
||||
nuvie_game_t gametype; // what game is being played?
|
||||
|
||||
NuvieDir wind_dir;
|
||||
Std::list<CallBack *>wind_change_notification_list;
|
||||
|
||||
GameTimedCallback *wind_timer;
|
||||
|
||||
public:
|
||||
|
||||
Weather(const Configuration *cfg, GameClock *c, nuvie_game_t type);
|
||||
~Weather() override;
|
||||
|
||||
bool load(NuvieIO *objlist);
|
||||
bool save(NuvieIO *objlist);
|
||||
|
||||
Std::string get_wind_dir_str() const;
|
||||
NuvieDir get_wind_dir() const {
|
||||
return wind_dir;
|
||||
}
|
||||
bool is_displaying_from_wind_dir() const {
|
||||
return display_from_wind_dir;
|
||||
}
|
||||
bool set_wind_dir(NuvieDir new_wind_dir);
|
||||
bool add_wind_change_notification_callback(CallBack *caller);
|
||||
bool set_moonstone(uint8 moonstone, MapCoord where);
|
||||
MapCoord get_moonstone(uint8 moonstone);
|
||||
void update_moongates();
|
||||
|
||||
bool is_eclipse() const;
|
||||
bool is_moon_visible() const;
|
||||
|
||||
uint16 callback(uint16 msg, CallBack *caller, void *data = nullptr) override;
|
||||
|
||||
protected:
|
||||
|
||||
NuvieDir load_wind(NuvieIO *objlist);
|
||||
bool save_wind(NuvieIO *objlist);
|
||||
void change_wind_dir();
|
||||
inline void set_wind_change_callback();
|
||||
inline void send_wind_change_notification_callback();
|
||||
void clear_wind();
|
||||
bool display_from_wind_dir;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
310
engines/ultima/nuvie/files/nuvie_bmp_file.cpp
Normal file
310
engines/ultima/nuvie/files/nuvie_bmp_file.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_bmp_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define NUVIEBMPFILE_MAGIC 0x4d42 // 'BM'
|
||||
|
||||
NuvieBmpFile::NuvieBmpFile() : data(nullptr), prev_width(0), prev_height(0),
|
||||
prev_bits(0), bmp_line_width(0) {
|
||||
memset(&header, 0, sizeof(header));
|
||||
memset(&infoHeader, 0, sizeof(infoHeader));
|
||||
ARRAYCLEAR(palette);
|
||||
}
|
||||
|
||||
NuvieBmpFile::~NuvieBmpFile() {
|
||||
if (data != nullptr)
|
||||
free(data);
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::initNewBlankImage(sint32 width, sint32 height, const unsigned char *pal) {
|
||||
infoHeader.size = 40;
|
||||
infoHeader.width = width;
|
||||
infoHeader.height = height;
|
||||
infoHeader.planes = 1;
|
||||
infoHeader.bits = 8;
|
||||
infoHeader.compression = 0;
|
||||
infoHeader.imagesize = 0;
|
||||
infoHeader.xresolution = 0; //FIXME
|
||||
infoHeader.yresolution = 0; //FIXME
|
||||
infoHeader.ncolours = 256;
|
||||
infoHeader.importantcolours = 256;
|
||||
|
||||
bmp_line_width = infoHeader.width;
|
||||
if (bmp_line_width % 4 != 0) {
|
||||
bmp_line_width += (4 - (bmp_line_width % 4));
|
||||
}
|
||||
|
||||
header.type = NUVIEBMPFILE_MAGIC;
|
||||
header.reserved1 = 0;
|
||||
header.reserved2 = 0;
|
||||
header.offset = NUVIEBMP_HEADER_SIZE + NUVIEBMP_INFOHEADER_SIZE + 256 * 4;
|
||||
header.size = header.offset + bmp_line_width * infoHeader.height;
|
||||
|
||||
memcpy(&palette, pal, sizeof(palette));
|
||||
|
||||
data = (unsigned char *)malloc(infoHeader.width * infoHeader.height);
|
||||
if (!data) {
|
||||
return handleError("Allocating pixel data");
|
||||
}
|
||||
|
||||
memset(data, 0, infoHeader.width * infoHeader.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::load(const Common::Path &filename) {
|
||||
NuvieIOFileRead file;
|
||||
|
||||
if (filename.empty())
|
||||
return handleError("zero byte file");
|
||||
|
||||
if (!file.open(filename)) {
|
||||
return handleError("opening file");
|
||||
}
|
||||
|
||||
if (file.get_size() < 0x36) { //invalid header.
|
||||
return handleError("filesize < 0x36");
|
||||
}
|
||||
|
||||
header.type = file.read2();
|
||||
header.size = file.read4();
|
||||
header.reserved1 = file.read2();
|
||||
header.reserved2 = file.read2();
|
||||
header.offset = file.read4();
|
||||
|
||||
infoHeader.size = file.read4();
|
||||
infoHeader.width = file.read4();
|
||||
infoHeader.height = file.read4();
|
||||
infoHeader.planes = file.read2();
|
||||
infoHeader.bits = file.read2();
|
||||
infoHeader.compression = file.read4();
|
||||
infoHeader.imagesize = file.read4();
|
||||
infoHeader.xresolution = file.read4();
|
||||
infoHeader.yresolution = file.read4();
|
||||
infoHeader.ncolours = file.read4();
|
||||
infoHeader.importantcolours = file.read4();
|
||||
|
||||
if (header.type != NUVIEBMPFILE_MAGIC) { //invalid magic.
|
||||
return handleError("invalid BMP magic.");
|
||||
}
|
||||
|
||||
if (infoHeader.bits != 8 && infoHeader.bits != 24) {
|
||||
return handleError("only 256 colour bitmaps supported.");
|
||||
}
|
||||
|
||||
if (infoHeader.compression != 0) { // && infoHeader.compression != 2)
|
||||
return handleError("only uncompressed BMP files are supported");
|
||||
//return handleError("only raw and bi_rle8 compression formats are supported.");
|
||||
|
||||
//FIXME need to handle rle compression.
|
||||
}
|
||||
|
||||
if (infoHeader.bits == 8) {
|
||||
for (uint32 i = 0; i < infoHeader.ncolours; i++) {
|
||||
uint8 b = file.read1();
|
||||
uint8 g = file.read1();
|
||||
uint8 r = file.read1();
|
||||
file.read1(); // 0
|
||||
palette[i] = (uint32)r | (uint32)(g << 8) | (uint32)(b << 16);
|
||||
}
|
||||
}
|
||||
|
||||
file.seekStart();
|
||||
file.seek(header.offset);
|
||||
|
||||
uint16 bytes_per_pixel = infoHeader.bits / 8;
|
||||
bmp_line_width = infoHeader.width * bytes_per_pixel;
|
||||
if (bmp_line_width % 4 != 0) {
|
||||
bmp_line_width += (4 - (bmp_line_width % 4));
|
||||
}
|
||||
|
||||
if (data == nullptr || infoHeader.width != prev_width || infoHeader.height != prev_height || prev_bits != infoHeader.bits) {
|
||||
if (data) {
|
||||
free(data);
|
||||
}
|
||||
data = (unsigned char *)malloc(infoHeader.width * infoHeader.height * bytes_per_pixel);
|
||||
prev_width = infoHeader.width;
|
||||
prev_height = infoHeader.height;
|
||||
prev_bits = infoHeader.bits;
|
||||
if (data == nullptr) {
|
||||
return handleError("allocating memory for image");
|
||||
}
|
||||
}
|
||||
|
||||
uint32 end = header.offset + bmp_line_width * infoHeader.height;
|
||||
|
||||
uint32 data_width = infoHeader.width * bytes_per_pixel;
|
||||
|
||||
for (sint32 i = 0; i < infoHeader.height; i++) {
|
||||
file.seek(end - bmp_line_width - (bmp_line_width * i));
|
||||
file.readToBuf(&data[i * data_width], data_width);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::save(const Common::Path &filename) {
|
||||
NuvieIOFileWrite file;
|
||||
|
||||
if (!file.open(filename)) {
|
||||
return handleError("Opening " + filename.toString() + ".");
|
||||
}
|
||||
|
||||
file.write2(header.type);
|
||||
file.write4(header.size);
|
||||
file.write2(header.reserved1);
|
||||
file.write2(header.reserved2);
|
||||
file.write4(header.offset);
|
||||
|
||||
file.write4(infoHeader.size);
|
||||
file.write4(infoHeader.width);
|
||||
file.write4(infoHeader.height);
|
||||
file.write2(infoHeader.planes);
|
||||
file.write2(infoHeader.bits);
|
||||
file.write4(infoHeader.compression);
|
||||
file.write4(infoHeader.imagesize);
|
||||
file.write4(infoHeader.xresolution);
|
||||
file.write4(infoHeader.yresolution);
|
||||
file.write4(infoHeader.ncolours);
|
||||
file.write4(infoHeader.importantcolours);
|
||||
|
||||
if (infoHeader.bits == 8) {
|
||||
for (uint32 i = 0; i < infoHeader.ncolours; i++) {
|
||||
file.write1((palette[i] >> 16) & 0xff); //B
|
||||
file.write1((palette[i] >> 8) & 0xff); //G
|
||||
file.write1(palette[i] & 0xff); //R
|
||||
file.write1((palette[i] >> 24) & 0xff); //A
|
||||
}
|
||||
write8BitData(&file);
|
||||
} else {
|
||||
//FIXME write out 24bit data here.
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieBmpFile::write8BitData(NuvieIOFileWrite *file) {
|
||||
uint32 i;
|
||||
for (i = infoHeader.height; i > 0; i--) {
|
||||
file->writeBuf(&data[(i - 1)*infoHeader.width], infoHeader.width);
|
||||
if ((sint32)bmp_line_width > infoHeader.width) {
|
||||
//write out padding bytes.
|
||||
for (uint8 j = 0; j < bmp_line_width - infoHeader.width; j++) {
|
||||
file->write1(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::handleError(Std::string error) {
|
||||
if (data) {
|
||||
free(data);
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
DEBUG(0, LEVEL_ERROR, error.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Tile *NuvieBmpFile::getTile() {
|
||||
if (infoHeader.width != 16 || infoHeader.height != 16 || infoHeader.bits != 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Tile *t = (Tile *)malloc(sizeof(Tile));
|
||||
if (t == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
memset(t, 0, sizeof(Tile));
|
||||
memcpy(t->data, data, 256);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
unsigned char *NuvieBmpFile::getRawIndexedData() {
|
||||
if (infoHeader.bits != 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
unsigned char *NuvieBmpFile::getRawIndexedDataCopy() {
|
||||
if (data == nullptr || infoHeader.bits != 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned char *copy = (unsigned char *)malloc(infoHeader.width * infoHeader.height);
|
||||
if (copy == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
memcpy(copy, data, infoHeader.width * infoHeader.height);
|
||||
return copy;
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *NuvieBmpFile::getSdlSurface32(const Common::Path &filename) {
|
||||
load(filename);
|
||||
return getSdlSurface32();
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *NuvieBmpFile::getSdlSurface32() {
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface(
|
||||
infoHeader.width, infoHeader.height,
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 0, 0, 8, 16, 24)
|
||||
);
|
||||
|
||||
unsigned char *src_buf = data;
|
||||
Graphics::Surface s = surface->getSubArea(Common::Rect(0, 0, surface->w, surface->h));
|
||||
uint32 *pixels = (uint32 *)s.getPixels();
|
||||
|
||||
if (infoHeader.bits == 8) {
|
||||
for (sint32 i = 0; i < infoHeader.height; i++) {
|
||||
for (sint32 j = 0; j < infoHeader.width; j++) {
|
||||
pixels[j] = palette[src_buf[j]];
|
||||
}
|
||||
src_buf += infoHeader.width;
|
||||
pixels += infoHeader.width;
|
||||
}
|
||||
} else { //bits == 24
|
||||
for (sint32 i = 0; i < infoHeader.height; i++) {
|
||||
for (sint32 j = 0; j < infoHeader.width; j++) {
|
||||
pixels[j] = (uint32)src_buf[j * 3 + 2] | (uint32)(src_buf[j * 3 + 1] << 8) | (uint32)(src_buf[j * 3 + 0] << 16);
|
||||
}
|
||||
src_buf += infoHeader.width * 3;
|
||||
pixels += infoHeader.width;
|
||||
}
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
97
engines/ultima/nuvie/files/nuvie_bmp_file.h
Normal file
97
engines/ultima/nuvie/files/nuvie_bmp_file.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_BMP_FILE_H
|
||||
#define NUVIE_FILES_NUVIE_BMP_FILE_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class NuvieBmpFile {
|
||||
private:
|
||||
unsigned char *data;
|
||||
uint32 palette[256];
|
||||
sint32 prev_width;
|
||||
sint32 prev_height;
|
||||
uint16 prev_bits;
|
||||
uint32 bmp_line_width;
|
||||
|
||||
struct {
|
||||
uint16 type; /* Magic identifier */
|
||||
uint32 size; /* File size in bytes */
|
||||
uint16 reserved1, reserved2;
|
||||
uint32 offset; /* Offset to image data, bytes */
|
||||
} header;
|
||||
|
||||
#define NUVIEBMP_HEADER_SIZE 14
|
||||
|
||||
struct {
|
||||
uint32 size; /* Header size in bytes */
|
||||
sint32 width, height; /* Width and height of image */
|
||||
uint16 planes; /* Number of colour planes */
|
||||
uint16 bits; /* Bits per pixel */
|
||||
uint32 compression; /* Compression type */
|
||||
uint32 imagesize; /* Image size in bytes */
|
||||
sint32 xresolution, yresolution; /* Pixels per meter */
|
||||
uint32 ncolours; /* Number of colours */
|
||||
uint32 importantcolours; /* Important colours */
|
||||
} infoHeader;
|
||||
|
||||
#define NUVIEBMP_INFOHEADER_SIZE 40
|
||||
|
||||
public:
|
||||
|
||||
NuvieBmpFile();
|
||||
~NuvieBmpFile();
|
||||
|
||||
bool initNewBlankImage(sint32 width, sint32 height, const unsigned char *palette);
|
||||
|
||||
bool load(const Common::Path &filename);
|
||||
bool save(const Common::Path &filename);
|
||||
|
||||
uint16 getWidth() const {
|
||||
return (uint16)infoHeader.width;
|
||||
}
|
||||
uint16 getHeight() const {
|
||||
return (uint16)infoHeader.height;
|
||||
}
|
||||
|
||||
|
||||
Tile *getTile();
|
||||
unsigned char *getRawIndexedData();
|
||||
unsigned char *getRawIndexedDataCopy();
|
||||
Graphics::ManagedSurface *getSdlSurface32();
|
||||
Graphics::ManagedSurface *getSdlSurface32(const Common::Path &filename);
|
||||
|
||||
private:
|
||||
bool handleError(Std::string error);
|
||||
void write8BitData(NuvieIOFileWrite *file);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
102
engines/ultima/nuvie/files/nuvie_file_list.cpp
Normal file
102
engines/ultima/nuvie/files/nuvie_file_list.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/gui/widgets/console.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/nuvie_file_list.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
NuvieFileList::NuvieFileList() : sort_mode(NUVIE_SORT_NAME_ASC) {
|
||||
}
|
||||
|
||||
NuvieFileList::~NuvieFileList() {
|
||||
}
|
||||
|
||||
bool NuvieFileList::open(const Common::Path &directory, const char *search, uint8 s_mode) {
|
||||
Common::ArchiveMemberPtr arcMember = SearchMan.getMember(directory);
|
||||
|
||||
sort_mode = s_mode;
|
||||
|
||||
if (!arcMember || !arcMember->isDirectory()) {
|
||||
ConsoleAddWarning(Std::string("Failed to open ") + directory.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::ArchiveMemberList children;
|
||||
|
||||
arcMember->listChildren(children, search);
|
||||
if (children.empty()) {
|
||||
ConsoleAddWarning(Std::string("Failed to get children of ") + directory.toString());
|
||||
return false;
|
||||
};
|
||||
|
||||
for (const auto &child : children) {
|
||||
if (!child->isDirectory())
|
||||
add_filename(child->getFileName());
|
||||
}
|
||||
|
||||
//sort list by time last modified in decending order.
|
||||
Common::sort(file_list.begin(), file_list.end(), NuvieFileDesc());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieFileList::add_filename(const Common::String &fileName) {
|
||||
NuvieFileDesc filedesc;
|
||||
filedesc.m_time = 0;
|
||||
filedesc.filename = fileName;
|
||||
|
||||
file_list.push_front(filedesc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Std::string *NuvieFileList::get_latest() const {
|
||||
Std::list<NuvieFileDesc>::const_iterator iter;
|
||||
|
||||
iter = file_list.begin();
|
||||
|
||||
if (iter != file_list.end()) {
|
||||
return &((*iter).filename);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 NuvieFileList::get_num_files() const {
|
||||
return (uint32)file_list.size();
|
||||
}
|
||||
|
||||
void NuvieFileList::close() {
|
||||
return;
|
||||
}
|
||||
|
||||
const Std::list<NuvieFileDesc> &NuvieFileList::get_filelist() const {
|
||||
return file_list;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
82
engines/ultima/nuvie/files/nuvie_file_list.h
Normal file
82
engines/ultima/nuvie/files/nuvie_file_list.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_FILE_LIST_H
|
||||
#define NUVIE_FILES_NUVIE_FILE_LIST_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::list;
|
||||
using Std::string;
|
||||
|
||||
|
||||
#define NUVIE_SORT_TIME_DESC 0x1
|
||||
#define NUVIE_SORT_TIME_ASC 0x2
|
||||
#define NUVIE_SORT_NAME_DESC 0x3
|
||||
#define NUVIE_SORT_NAME_ASC 0x5
|
||||
|
||||
class Configuration;
|
||||
|
||||
class NuvieFileDesc {
|
||||
public:
|
||||
Std::string filename;
|
||||
uint32 m_time;
|
||||
|
||||
bool operator<(const NuvieFileDesc &rhs) const {
|
||||
return (rhs.m_time < this->m_time);
|
||||
};
|
||||
bool operator()(const NuvieFileDesc &lhs, const NuvieFileDesc &rhs) {
|
||||
return (lhs.m_time > rhs.m_time);
|
||||
};
|
||||
};
|
||||
|
||||
class NuvieFileList {
|
||||
protected:
|
||||
Std::list<NuvieFileDesc> file_list;
|
||||
|
||||
uint8 sort_mode;
|
||||
protected:
|
||||
bool add_filename(const Common::String &fileName);
|
||||
public:
|
||||
|
||||
NuvieFileList();
|
||||
~NuvieFileList();
|
||||
|
||||
bool open(const Common::Path &directory, const char *restrict, uint8 sort_mode);
|
||||
|
||||
Std::string *next();
|
||||
const Std::string *get_latest() const;
|
||||
uint32 get_num_files() const;
|
||||
|
||||
const Std::list<NuvieFileDesc> &get_filelist() const;
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
224
engines/ultima/nuvie/files/nuvie_io.cpp
Normal file
224
engines/ultima/nuvie/files/nuvie_io.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
NuvieIO::NuvieIO() : size(0), pos(0) {
|
||||
}
|
||||
|
||||
NuvieIO::~NuvieIO() {
|
||||
}
|
||||
|
||||
unsigned char *NuvieIO::readAll() {
|
||||
uint32 bytes_read;
|
||||
return readBuf(size, &bytes_read);
|
||||
}
|
||||
|
||||
unsigned char *NuvieIO::readBuf(uint32 read_size, uint32 *bytes_read) {
|
||||
unsigned char *buf;
|
||||
|
||||
*bytes_read = 0;
|
||||
|
||||
if (pos + read_size > size)
|
||||
return nullptr;
|
||||
|
||||
buf = (unsigned char *)malloc(read_size);
|
||||
if (buf == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (readToBuf(buf, read_size) == false) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*bytes_read = read_size;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
// NuvieIOBuffer
|
||||
|
||||
NuvieIOBuffer::NuvieIOBuffer() : NuvieIO() {
|
||||
data = nullptr;
|
||||
copied_data = false;
|
||||
}
|
||||
|
||||
NuvieIOBuffer::~NuvieIOBuffer() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::open(unsigned char *buf, uint32 buf_size, bool copy_buf) {
|
||||
if (data != nullptr)
|
||||
return false;
|
||||
|
||||
if (copy_buf == NUVIE_BUF_COPY) {
|
||||
copied_data = true;
|
||||
data = (unsigned char *)malloc(buf_size);
|
||||
if (data == nullptr) {
|
||||
DEBUG(0, LEVEL_ERROR, "NuvieIOBuffer::open() allocating %d bytes.\n", buf_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, buf, buf_size);
|
||||
} else
|
||||
data = buf;
|
||||
|
||||
size = buf_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieIOBuffer::close() {
|
||||
size = 0;
|
||||
pos = 0;
|
||||
|
||||
if (copied_data && data != nullptr)
|
||||
free(data);
|
||||
|
||||
data = nullptr;
|
||||
|
||||
}
|
||||
|
||||
uint8 NuvieIOBuffer::read1() {
|
||||
if (pos >= size)
|
||||
return 0;
|
||||
|
||||
return data[pos++];
|
||||
}
|
||||
|
||||
uint16 NuvieIOBuffer::read2() {
|
||||
uint16 val;
|
||||
|
||||
if (pos > size - 2)
|
||||
return 0;
|
||||
|
||||
val = data[pos] + (data[pos + 1] << 8);
|
||||
pos += 2;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32 NuvieIOBuffer::read4() {
|
||||
uint32 val;
|
||||
|
||||
if (pos > size - 4)
|
||||
return 0;
|
||||
|
||||
val = (data[pos] + (data[pos + 1] << 8) + (data[pos + 2] << 16) + (data[pos + 3] << 24));
|
||||
pos += 4;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::readToBuf(unsigned char *buf, uint32 buf_size) {
|
||||
if (pos + buf_size > size || buf == nullptr)
|
||||
return false;
|
||||
|
||||
memcpy(buf, &data[pos], buf_size);
|
||||
|
||||
pos += buf_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::write1(uint8 src) {
|
||||
if (pos >= size)
|
||||
return false;
|
||||
|
||||
data[pos] = src;
|
||||
pos++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool NuvieIOBuffer::write2(uint16 src) {
|
||||
if (pos > size - 2)
|
||||
return false;
|
||||
|
||||
data[pos] = src & 0xff;
|
||||
data[pos + 1] = (src >> 8) & 0xff;
|
||||
pos += 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::write4(uint32 src) {
|
||||
unsigned char *ptr;
|
||||
if (pos > size - 4)
|
||||
return false;
|
||||
|
||||
ptr = &data[pos];
|
||||
|
||||
*ptr++ = src & 0xff;
|
||||
*ptr++ = (src >> 8) & 0xff;
|
||||
*ptr++ = (src >> 16) & 0xff;
|
||||
*ptr++ = (src >> 24) & 0xff;
|
||||
|
||||
pos += 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 NuvieIOBuffer::writeBuf(const unsigned char *src, uint32 src_size) {
|
||||
if (pos + src_size > size || src == nullptr)
|
||||
return 0;
|
||||
|
||||
memcpy(&data[pos], src, src_size);
|
||||
|
||||
pos += src_size;
|
||||
|
||||
return src_size;
|
||||
}
|
||||
|
||||
uint32 NuvieIOBuffer::write(NuvieIO *src) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void NuvieIOBuffer::seek(uint32 new_pos) {
|
||||
if (data && new_pos < size)
|
||||
pos = new_pos;
|
||||
}
|
||||
|
||||
char *strgets(char *str, int n, Common::ReadStream *stream) {
|
||||
int count = 0;
|
||||
char c;
|
||||
|
||||
while (!stream->eos() && (count < (n - 1)) && (c = stream->readByte()) != '\n') {
|
||||
if (c == '\r')
|
||||
continue;
|
||||
|
||||
str[count] = c;
|
||||
++count;
|
||||
}
|
||||
|
||||
str[count] = '\0';
|
||||
return count ? str : nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
143
engines/ultima/nuvie/files/nuvie_io.h
Normal file
143
engines/ultima/nuvie/files/nuvie_io.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_IO_H
|
||||
#define NUVIE_FILES_NUVIE_IO_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
|
||||
class NuvieIO {
|
||||
protected:
|
||||
uint32 size;
|
||||
uint32 pos;
|
||||
public:
|
||||
NuvieIO();
|
||||
virtual ~NuvieIO();
|
||||
|
||||
virtual void close() {
|
||||
size = 0;
|
||||
pos = 0;
|
||||
};
|
||||
|
||||
virtual uint8 read1() {
|
||||
return 0;
|
||||
};
|
||||
virtual uint16 read2() {
|
||||
return 0;
|
||||
};
|
||||
virtual uint32 read4() {
|
||||
return 0;
|
||||
};
|
||||
|
||||
unsigned char *readAll();
|
||||
unsigned char *readBuf(uint32 read_size, uint32 *bytes_read);
|
||||
virtual bool readToBuf(unsigned char *buf, uint32 buf_size) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
virtual bool write1(uint8 src) {
|
||||
return false;
|
||||
};
|
||||
virtual bool write2(uint16 src) {
|
||||
return false;
|
||||
};
|
||||
virtual bool write4(uint32 src) {
|
||||
return false;
|
||||
};
|
||||
virtual uint32 writeBuf(const unsigned char *src, uint32 src_size) {
|
||||
return 0;
|
||||
};
|
||||
virtual uint32 write(NuvieIO *src) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
uint32 get_size() const {
|
||||
return size;
|
||||
};
|
||||
|
||||
|
||||
inline void seekStart() {
|
||||
seek(0);
|
||||
};
|
||||
inline void seekEnd() {
|
||||
seek(size);
|
||||
};
|
||||
virtual void seek(uint32 new_pos) = 0;
|
||||
|
||||
inline bool is_end() const {
|
||||
return (pos == size - 1);
|
||||
};
|
||||
inline bool is_eof() const {
|
||||
return (size == 0 || pos >= size);
|
||||
};
|
||||
uint32 position() const {
|
||||
return pos;
|
||||
};
|
||||
};
|
||||
|
||||
#define NUVIE_BUF_COPY true
|
||||
#define NUVIE_BUF_NOCOPY false
|
||||
|
||||
|
||||
class NuvieIOBuffer: public NuvieIO {
|
||||
protected:
|
||||
|
||||
unsigned char *data;
|
||||
bool copied_data;
|
||||
|
||||
public:
|
||||
NuvieIOBuffer();
|
||||
~NuvieIOBuffer() override;
|
||||
|
||||
bool open(unsigned char *buf, uint32 buf_size, bool copy_buf = NUVIE_BUF_COPY);
|
||||
|
||||
void close() override;
|
||||
|
||||
unsigned char *get_raw_data() {
|
||||
return data;
|
||||
}; //hehe evil
|
||||
|
||||
uint8 read1() override;
|
||||
uint16 read2() override;
|
||||
uint32 read4() override;
|
||||
bool readToBuf(unsigned char *buf, uint32 buf_size) override;
|
||||
|
||||
bool write1(uint8 src) override;
|
||||
bool write2(uint16 src) override;
|
||||
bool write4(uint32 src) override;
|
||||
uint32 writeBuf(const unsigned char *src, uint32 src_size) override;
|
||||
uint32 write(NuvieIO *src) override;
|
||||
|
||||
void seek(uint32 new_pos) override;
|
||||
};
|
||||
|
||||
extern char *strgets(char *str, int n, Common::ReadStream *stream);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
264
engines/ultima/nuvie/files/nuvie_io_file.cpp
Normal file
264
engines/ultima/nuvie/files/nuvie_io_file.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "engines/metaengine.h"
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "engines/engine.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
NuvieIOFileRead::~NuvieIOFileRead() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool NuvieIOFileRead::open(const Common::Path &filename) {
|
||||
if (isOpen())
|
||||
// We already have a file open, lets bail.
|
||||
return false;
|
||||
|
||||
// Handle any relative files under the game path, such as for FM-Towns sound. Though path
|
||||
// delimiters can also be used for resources in ultima.dat
|
||||
|
||||
Common::StringArray components = filename.splitComponents();
|
||||
if (components.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (components.size() >= 2) {
|
||||
Common::FSNode node(ConfMan.getPath("path"));
|
||||
for(const auto &c : components) {
|
||||
node = node.getChild(c);
|
||||
if (!node.exists())
|
||||
break;
|
||||
}
|
||||
if (node.exists())
|
||||
_srcFile.open(node);
|
||||
}
|
||||
|
||||
if (!_srcFile.isOpen())
|
||||
_srcFile.open(filename);
|
||||
|
||||
if (!_srcFile.isOpen()) {
|
||||
DEBUG(0, LEVEL_ERROR, "Failed opening '%s'\n", filename.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_file = &_srcFile;
|
||||
size = _srcFile.size();
|
||||
pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileRead::open(Common::InSaveFile *saveFile) {
|
||||
assert(saveFile);
|
||||
_file = saveFile;
|
||||
|
||||
size = _file->size();
|
||||
pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieIOFileRead::close() {
|
||||
if (_srcFile.isOpen())
|
||||
_srcFile.close();
|
||||
_file = nullptr;
|
||||
|
||||
NuvieIO::close();
|
||||
}
|
||||
|
||||
void NuvieIOFileRead::seek(uint32 new_pos) {
|
||||
if (isOpen() && new_pos <= size) {
|
||||
_file->seek(new_pos);
|
||||
pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 NuvieIOFileRead::read1() {
|
||||
if (pos > size - 1)
|
||||
return 0;
|
||||
|
||||
pos++;
|
||||
|
||||
return _file->readByte();
|
||||
}
|
||||
|
||||
uint16 NuvieIOFileRead::read2() {
|
||||
if (pos > size - 2)
|
||||
return 0;
|
||||
|
||||
pos += 2;
|
||||
return _file->readUint16LE();
|
||||
}
|
||||
|
||||
uint32 NuvieIOFileRead::read4() {
|
||||
if (pos > size - 4)
|
||||
return 0;
|
||||
|
||||
pos += 4;
|
||||
return _file->readUint32LE();
|
||||
}
|
||||
|
||||
bool NuvieIOFileRead::readToBuf(unsigned char *buf, uint32 buf_size) {
|
||||
if (pos + buf_size > size)
|
||||
return false;
|
||||
|
||||
_file->read(buf, buf_size);
|
||||
pos += buf_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NuvieIOFileWrite
|
||||
//
|
||||
|
||||
NuvieIOFileWrite::NuvieIOFileWrite() : _saveFileData(DisposeAfterUse::YES),
|
||||
_file(nullptr), _saveFile(nullptr), _isAutosave(false) {
|
||||
}
|
||||
|
||||
NuvieIOFileWrite::~NuvieIOFileWrite() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::open(const Common::Path &filename) {
|
||||
if (isOpen())
|
||||
// We already have an open file
|
||||
return false;
|
||||
|
||||
// Ensure it's a relative path, that we can open for writing using a DumpFile
|
||||
assert(!filename.getParent().empty());
|
||||
|
||||
if (!_dumpFile.open(filename, true)) {
|
||||
DEBUG(0, LEVEL_ERROR, "Failed opening '%s'\n", filename.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_file = &_dumpFile;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::open(const Common::String &filename, bool isAutosave) {
|
||||
if (isOpen())
|
||||
// We already have an open file
|
||||
return false;
|
||||
|
||||
// Singular file, so open it as a save file
|
||||
_saveFile = g_system->getSavefileManager()->openForSaving(filename, false);
|
||||
assert(_saveFile);
|
||||
|
||||
// Point _file to the _saveFileData member for initial writing,
|
||||
// since save files don't allow seeking
|
||||
_file = &_saveFileData;
|
||||
_isAutosave = isAutosave;
|
||||
|
||||
size = 0;
|
||||
pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieIOFileWrite::close() {
|
||||
if (!isOpen()) {
|
||||
// Nothing needed
|
||||
} else if (_saveFile) {
|
||||
// Writing using savefile interface, so flush out data
|
||||
_saveFile->write(_saveFileData.getData(), _saveFileData.size());
|
||||
g_engine->getMetaEngine()->appendExtendedSave(_saveFile, g_engine->getTotalPlayTime(), _description, _isAutosave);
|
||||
|
||||
_saveFile->finalize();
|
||||
delete _saveFile;
|
||||
_saveFile = nullptr;
|
||||
} else {
|
||||
// Writing to a dump file, so simply close it
|
||||
_dumpFile.close();
|
||||
}
|
||||
|
||||
_file = nullptr;
|
||||
}
|
||||
|
||||
void NuvieIOFileWrite::seek(uint32 new_pos) {
|
||||
if (isOpen() && new_pos <= size) {
|
||||
_file->seek(new_pos);
|
||||
pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::write1(uint8 src) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
_file->writeByte(src);
|
||||
++pos;
|
||||
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::write2(uint16 src) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
_file->writeUint16LE(src);
|
||||
pos += 2;
|
||||
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::write4(uint32 src) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
_file->writeUint32LE(src);
|
||||
pos += 4;
|
||||
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 NuvieIOFileWrite::writeBuf(const unsigned char *src, uint32 src_size) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
pos += src_size;
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return (_file->write(src, src_size));
|
||||
}
|
||||
|
||||
uint32 NuvieIOFileWrite::write(NuvieIO *src) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
109
engines/ultima/nuvie/files/nuvie_io_file.h
Normal file
109
engines/ultima/nuvie/files/nuvie_io_file.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_IO_FILE_H
|
||||
#define NUVIE_FILES_NUVIE_IO_FILE_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class NuvieIOFile : public NuvieIO {
|
||||
public:
|
||||
NuvieIOFile() {}
|
||||
~NuvieIOFile() override {}
|
||||
|
||||
virtual bool open(const Common::Path &filename) {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
class NuvieIOFileRead : public NuvieIOFile {
|
||||
private:
|
||||
Common::SeekableReadStream *_file;
|
||||
Common::File _srcFile;
|
||||
public:
|
||||
NuvieIOFileRead() : NuvieIOFile(), _file(nullptr) {}
|
||||
~NuvieIOFileRead() override;
|
||||
|
||||
bool open(const Common::Path &filename) override;
|
||||
virtual bool open(Common::InSaveFile *saveFile);
|
||||
void close() override;
|
||||
void seek(uint32 new_pos) override;
|
||||
|
||||
uint8 read1() override;
|
||||
uint16 read2() override;
|
||||
uint32 read4() override;
|
||||
|
||||
bool readToBuf(unsigned char *buf, uint32 buf_size) override;
|
||||
|
||||
bool isOpen() const {
|
||||
return _file != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* File writing class. This can be done in one of two ways.
|
||||
* If it's a simple filename, then it uses the savefile interface to
|
||||
* write it as uncompresed to the save folder. However, if it has
|
||||
* relative paths, such as used by the code to dump out all tiles or maps,
|
||||
* it uses a Common::DumpFile instead
|
||||
*/
|
||||
class NuvieIOFileWrite : public NuvieIOFile {
|
||||
private:
|
||||
Common::SeekableWriteStream *_file;
|
||||
Common::DumpFile _dumpFile;
|
||||
Common::OutSaveFile *_saveFile;
|
||||
Common::MemoryWriteStreamDynamic _saveFileData;
|
||||
Common::String _description;
|
||||
bool _isAutosave;
|
||||
protected:
|
||||
bool isOpen() const {
|
||||
return _file != nullptr;
|
||||
}
|
||||
public:
|
||||
NuvieIOFileWrite();
|
||||
~NuvieIOFileWrite() override;
|
||||
bool open(const Common::Path &filename) override;
|
||||
bool open(const Common::String &filename, bool isAutosave);
|
||||
void close() override;
|
||||
void seek(uint32 new_pos) override;
|
||||
|
||||
bool write1(uint8 src) override;
|
||||
bool write2(uint16 src) override;
|
||||
bool write4(uint32 src) override;
|
||||
void writeDesc(const Common::String &desc) {
|
||||
_description = desc;
|
||||
}
|
||||
|
||||
uint32 writeBuf(const unsigned char *src, uint32 src_size) override;
|
||||
uint32 write(NuvieIO *src) override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
249
engines/ultima/nuvie/files/tmx_map.cpp
Normal file
249
engines/ultima/nuvie/files/tmx_map.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/tmx_map.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
TMXMap::TMXMap(TileManager *tm, Map *m, ObjManager *om) : tile_manager(tm),
|
||||
map(m), obj_manager(om), mapdata(nullptr) {
|
||||
}
|
||||
|
||||
TMXMap::~TMXMap() {
|
||||
|
||||
}
|
||||
|
||||
bool TMXMap::exportTmxMapFiles(const Common::Path &dir, nuvie_game_t type) {
|
||||
savedir = dir;
|
||||
savename = get_game_tag(type);
|
||||
Common::Path filename;
|
||||
build_path(savedir, savename + "_tileset.bmp", filename);
|
||||
|
||||
|
||||
|
||||
tile_manager->exportTilesetToBmpFile(filename);
|
||||
|
||||
for (uint8 i = 0; i < 6; i++) {
|
||||
writeRoofTileset(i);
|
||||
exportMapLevel(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TMXMap::writeRoofTileset(uint8 level) {
|
||||
if (map->get_roof_data(level) == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Path filename = map->getRoofTilesetFilename();
|
||||
Common::Path destFilename;
|
||||
build_path(savedir, savename + "_roof_tileset.bmp", destFilename);
|
||||
NuvieIOFileRead read;
|
||||
NuvieIOFileWrite write;
|
||||
read.open(filename);
|
||||
write.open(destFilename);
|
||||
unsigned char *buf = read.readAll();
|
||||
write.writeBuf(buf, read.get_size());
|
||||
write.close();
|
||||
read.close();
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void TMXMap::writeLayer(NuvieIOFileWrite *tmx, uint16 sideLength, Std::string layerName,
|
||||
uint16 gidOffset, uint16 bitsPerTile, const unsigned char *data) {
|
||||
Std::string slen = sint32ToString((sint32)sideLength);
|
||||
Std::string header = " <layer name=\"" + layerName + "\" width=\"" + slen + "\" height=\""
|
||||
+ slen + "\">\n";
|
||||
header += " <data encoding=\"csv\">\n";
|
||||
|
||||
tmx->writeBuf((const unsigned char *)header.c_str(), header.size());
|
||||
|
||||
uint16 mx, my;
|
||||
for (my = 0; my < sideLength; my++) {
|
||||
for (mx = 0; mx < sideLength; mx++) {
|
||||
uint16 gid = 0;
|
||||
if (bitsPerTile == 8) { //base map is uint8
|
||||
gid = (uint16)data[my * sideLength + mx] + 1 + gidOffset;
|
||||
} else { //everything else is uint16
|
||||
gid = ((const uint16 *)data)[my * sideLength + mx] + 1 + gidOffset;
|
||||
}
|
||||
// 'nnnn\0'
|
||||
Common::String temp = Common::String::format("%d", gid);
|
||||
tmx->writeBuf((const unsigned char *)temp.c_str(), temp.size());
|
||||
if (mx < sideLength - 1 || my < sideLength - 1) { //don't write comma after last element in the array.
|
||||
tmx->write1(',');
|
||||
}
|
||||
}
|
||||
tmx->write1('\n');
|
||||
}
|
||||
|
||||
Std::string footer = " </data>\n";
|
||||
footer += " </layer>\n";
|
||||
|
||||
tmx->writeBuf((const unsigned char *)footer.c_str(), footer.size());
|
||||
}
|
||||
|
||||
void TMXMap::writeObjectLayer(NuvieIOFileWrite *tmx, uint8 level) {
|
||||
Std::string xml = "<objectgroup name=\"Object Layer\">\n";
|
||||
tmx->writeBuf((const unsigned char *)xml.c_str(), xml.size());
|
||||
|
||||
writeObjects(tmx, level, true, false);
|
||||
writeObjects(tmx, level, false, false);
|
||||
writeObjects(tmx, level, false, true);
|
||||
|
||||
xml = "</objectgroup>\n";
|
||||
tmx->writeBuf((const unsigned char *)xml.c_str(), xml.size());
|
||||
}
|
||||
|
||||
bool TMXMap::canDrawTile(Tile *t, bool forceLower, bool toptile) {
|
||||
if (forceLower == false && (t->flags3 & 0x4) && toptile == false) //don't display force lower tiles.
|
||||
return false;
|
||||
|
||||
if (forceLower == true && !(t->flags3 & 0x4))
|
||||
return false;
|
||||
|
||||
if ((toptile && !t->toptile) || (!toptile && t->toptile))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Std::string TMXMap::writeObjectTile(Obj *obj, Std::string nameSuffix, uint16 tile_num, uint16 x, uint16 y, bool forceLower, bool toptile) {
|
||||
Tile *t = tile_manager->get_tile(tile_num);
|
||||
|
||||
if (canDrawTile(t, forceLower, toptile)) {
|
||||
return " <object name=\"" + encode_xml_entity(Std::string(obj_manager->get_obj_name(obj))) + nameSuffix + "\" gid=\"" + sint32ToString(tile_num + 1) + "\" x=\"" + sint32ToString(x * 16) + "\" y=\"" + sint32ToString((y + 1) * 16) + "\" width=\"16\" height=\"16\"/>\n";
|
||||
}
|
||||
|
||||
return Std::string();
|
||||
}
|
||||
|
||||
void TMXMap::writeObjects(NuvieIOFileWrite *tmx, uint8 level, bool forceLower, bool toptiles) {
|
||||
uint16 width = map->get_width(level);
|
||||
|
||||
for (uint16 y = 0; y < width; y++) {
|
||||
for (uint16 x = 0; x < width; x++) {
|
||||
U6LList *list = obj_manager->get_obj_list(x, y, level);
|
||||
if (list) {
|
||||
for (U6Link *link = list->start(); link != nullptr; link = link->next) {
|
||||
Obj *obj = (Obj *)link->data;
|
||||
Tile *t = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
|
||||
Std::string s;
|
||||
if (canDrawTile(t, forceLower, toptiles)) {
|
||||
s = " <object name=\"" + encode_xml_entity(Std::string(obj_manager->get_obj_name(obj))) + "\" gid=\"" + sint32ToString(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n + 1) + "\" x=\"" + sint32ToString((x) * 16) + "\" y=\"" + sint32ToString((y + 1) * 16) + "\" width=\"16\" height=\"16\">\n";
|
||||
s += " <properties>\n";
|
||||
s += " <property name=\"obj_n\" value=\"" + sint32ToString(obj->obj_n) + "\"/>\n";
|
||||
s += " <property name=\"frame_n\" value=\"" + sint32ToString(obj->frame_n) + "\"/>\n";
|
||||
s += " <property name=\"qty\" value=\"" + sint32ToString(obj->qty) + "\"/>\n";
|
||||
s += " <property name=\"quality\" value=\"" + sint32ToString(obj->quality) + "\"/>\n";
|
||||
s += " <property name=\"status\" value=\"" + sint32ToString(obj->status) + "\"/>\n";
|
||||
s += " <property name=\"toptile\" value=\"" + boolToString(t->toptile) + "\"/>\n";
|
||||
s += " </properties>\n";
|
||||
s += " </object>\n";
|
||||
}
|
||||
if (t->dbl_width) {
|
||||
s += writeObjectTile(obj, " -x", t->tile_num - 1, x - 1, y, forceLower, toptiles);
|
||||
}
|
||||
if (t->dbl_height) {
|
||||
uint16 tile_num = t->tile_num - 1;
|
||||
if (t->dbl_width) {
|
||||
tile_num--;
|
||||
}
|
||||
s += writeObjectTile(obj, " -y", tile_num, x, y - 1, forceLower, toptiles);
|
||||
}
|
||||
if (t->dbl_width && t->dbl_height) {
|
||||
s += writeObjectTile(obj, " -x,-y", t->tile_num - 3, x - 1, y - 1, forceLower, toptiles);
|
||||
}
|
||||
tmx->writeBuf((const unsigned char *)s.c_str(), s.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TMXMap::exportMapLevel(uint8 level) {
|
||||
NuvieIOFileWrite tmx;
|
||||
uint16 width = map->get_width(level);
|
||||
mapdata = map->get_map_data(level);
|
||||
Common::String level_string = Common::String::format("%d", level); // 'nn\0'
|
||||
Common::Path filename;
|
||||
build_path(savedir, savename + "_" + Std::string(level_string.c_str()) + ".tmx", filename);
|
||||
|
||||
tmx.open(filename);
|
||||
Std::string swidth = sint32ToString((sint32)width);
|
||||
Std::string header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
header +=
|
||||
"<map version=\"1.0\" orientation=\"orthogonal\" renderorder=\"right-down\" width=\""
|
||||
+ swidth + "\" height=\"" + swidth
|
||||
+ "\" tilewidth=\"16\" tileheight=\"16\">\n";
|
||||
header +=
|
||||
" <tileset firstgid=\"1\" name=\"tileset\" tilewidth=\"16\" tileheight=\"16\">\n";
|
||||
header += " <image source=\"" + savename
|
||||
+ "_tileset.bmp\" trans=\"00dffc\" width=\"512\" height=\"1024\"/>\n";
|
||||
header += " </tileset>\n";
|
||||
|
||||
if (map->get_roof_data(level) != nullptr) {
|
||||
header +=
|
||||
" <tileset firstgid=\"2048\" name=\"roof_tileset\" tilewidth=\"16\" tileheight=\"16\">\n";
|
||||
header += " <image source=\"" + savename + "_roof_tileset.bmp\" trans=\"0070fc\" width=\"80\" height=\"3264\"/>\n";
|
||||
header += " </tileset>\n";
|
||||
}
|
||||
|
||||
tmx.writeBuf((const unsigned char *)header.c_str(), header.size());
|
||||
|
||||
writeLayer(&tmx, width, "BaseLayer", 0, 8, mapdata);
|
||||
|
||||
writeObjectLayer(&tmx, level);
|
||||
|
||||
if (map->get_roof_data(level) != nullptr) {
|
||||
writeLayer(&tmx, width, "RoofLayer", 2047, 16, (const unsigned char *)map->get_roof_data(level));
|
||||
}
|
||||
|
||||
Std::string footer = "</map>\n";
|
||||
|
||||
|
||||
tmx.writeBuf((const unsigned char *)footer.c_str(), footer.size());
|
||||
|
||||
|
||||
|
||||
tmx.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Std::string TMXMap::sint32ToString(sint32 value) {
|
||||
char buf[12];
|
||||
snprintf(buf, sizeof(buf), "%d", value);
|
||||
return Std::string(buf);
|
||||
}
|
||||
|
||||
Std::string TMXMap::boolToString(bool value) {
|
||||
return value ? Std::string("true") : Std::string("false");
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
67
engines/ultima/nuvie/files/tmx_map.h
Normal file
67
engines/ultima/nuvie/files/tmx_map.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_TMX_MAP_H
|
||||
#define NUVIE_FILES_TMX_MAP_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Map;
|
||||
class ObjManager;
|
||||
class TileManager;
|
||||
class NuvieIOFileWrite;
|
||||
|
||||
class TMXMap {
|
||||
private:
|
||||
unsigned char *mapdata;
|
||||
NuvieIOFileWrite file;
|
||||
TileManager *tile_manager;
|
||||
Map *map;
|
||||
ObjManager *obj_manager;
|
||||
Common::Path savedir;
|
||||
Std::string savename;
|
||||
//nuvie_game_t game_type;
|
||||
|
||||
public:
|
||||
TMXMap(TileManager *tm, Map *m, ObjManager *om);
|
||||
virtual ~TMXMap();
|
||||
bool exportTmxMapFiles(const Common::Path &dir, nuvie_game_t type);
|
||||
private:
|
||||
bool exportMapLevel(uint8 level);
|
||||
void writeRoofTileset(uint8 level);
|
||||
void writeLayer(NuvieIOFileWrite *tmx, uint16 width, Std::string layerName,
|
||||
uint16 gidOffset, uint16 bitsPerTile, const unsigned char *data);
|
||||
void writeObjectLayer(NuvieIOFileWrite *tmx, uint8 level);
|
||||
void writeObjects(NuvieIOFileWrite *tmx, uint8 level, bool forceLower, bool toptiles);
|
||||
Std::string writeObjectTile(Obj *obj, Std::string nameSuffix, uint16 tile_num, uint16 x, uint16 y, bool forceLower, bool toptile);
|
||||
Std::string sint32ToString(sint32 value);
|
||||
Std::string boolToString(bool value);
|
||||
bool canDrawTile(Tile *t, bool forceLower, bool toptile);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
65
engines/ultima/nuvie/files/u6_bmp.cpp
Normal file
65
engines/ultima/nuvie/files/u6_bmp.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_bmp.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Bmp::U6Bmp(): U6Shape(), data(nullptr) {
|
||||
}
|
||||
|
||||
U6Bmp::~U6Bmp() {
|
||||
if (data != nullptr)
|
||||
free(data);
|
||||
|
||||
raw = nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool U6Bmp::load(const Common::Path &filename) {
|
||||
U6Lzw lzw;
|
||||
uint32 data_size;
|
||||
|
||||
if (data != nullptr)
|
||||
return false;
|
||||
|
||||
if (filename.empty())
|
||||
return false;
|
||||
|
||||
data = lzw.decompress_file(filename, data_size);
|
||||
|
||||
if (data == nullptr)
|
||||
return false;
|
||||
|
||||
width = (data[0] + (data[1] << 8));
|
||||
height = (data[2] + (data[3] << 8));
|
||||
|
||||
raw = data + 0x4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
48
engines/ultima/nuvie/files/u6_bmp.h
Normal file
48
engines/ultima/nuvie/files/u6_bmp.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6BMP_H
|
||||
#define NUVIE_FILES_U6BMP_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
#include "ultima/nuvie/files/u6_shape.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class U6Bmp: public U6Shape {
|
||||
private:
|
||||
unsigned char *data;
|
||||
|
||||
public:
|
||||
|
||||
U6Bmp();
|
||||
~U6Bmp() override;
|
||||
|
||||
bool load(const Common::Path &filename) override;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
475
engines/ultima/nuvie/files/u6_lib_n.cpp
Normal file
475
engines/ultima/nuvie/files/u6_lib_n.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Lib_n::U6Lib_n() : num_offsets(0), items(nullptr), data(nullptr),
|
||||
del_data(false), filesize(0), game_type(NUVIE_GAME_U6), lib_size(0) {
|
||||
}
|
||||
|
||||
|
||||
U6Lib_n::~U6Lib_n(void) {
|
||||
close();
|
||||
}
|
||||
|
||||
// load u6lib from `filename'
|
||||
bool U6Lib_n::open(const Common::Path &filename, uint8 size, uint8 type) {
|
||||
NuvieIOFileRead *file;
|
||||
|
||||
file = new NuvieIOFileRead();
|
||||
|
||||
if (file->open(filename) == false) {
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
|
||||
del_data = true;
|
||||
|
||||
return open((NuvieIO *)file, size, type);
|
||||
}
|
||||
|
||||
|
||||
// load u6lib from opened stream
|
||||
bool U6Lib_n::open(NuvieIO *new_data, uint8 size, uint8 type) {
|
||||
game_type = type;
|
||||
data = new_data;
|
||||
|
||||
lib_size = size;
|
||||
this->parse_lib();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void U6Lib_n::close() {
|
||||
if (items) {
|
||||
for (uint32 i = 0; i < num_offsets; i++)
|
||||
delete items[i].name;
|
||||
free(items);
|
||||
}
|
||||
items = nullptr;
|
||||
|
||||
if (del_data) {
|
||||
if (data != nullptr)
|
||||
data->close();
|
||||
delete data;
|
||||
}
|
||||
|
||||
data = nullptr;
|
||||
del_data = false;
|
||||
|
||||
num_offsets = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open a ^new^ file for writing, with lib_size and type.
|
||||
*/
|
||||
bool U6Lib_n::create(const Common::Path &filename, uint8 size, uint8 type) {
|
||||
NuvieIOFileWrite *file = new NuvieIOFileWrite();
|
||||
if (!file->open(filename)) {
|
||||
DEBUG(0, LEVEL_ERROR, "U6Lib: Error creating %s\n", filename.toString().c_str());
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
game_type = type;
|
||||
lib_size = size;
|
||||
data = (NuvieIO *)file;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint32 U6Lib_n::get_num_items(void) {
|
||||
return num_offsets;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the location of `item_number' in the library file.
|
||||
*/
|
||||
uint32 U6Lib_n::get_item_offset(uint32 item_number) {
|
||||
if (item_number >= num_offsets)
|
||||
return 0;
|
||||
return (items[item_number].offset);
|
||||
}
|
||||
|
||||
uint32 U6Lib_n::get_item_size(uint32 item_number) {
|
||||
if (item_number >= num_offsets)
|
||||
return 0;
|
||||
|
||||
return (items[item_number].uncomp_size);
|
||||
}
|
||||
|
||||
|
||||
// read and return item data
|
||||
unsigned char *U6Lib_n::get_item(uint32 item_number, unsigned char *ret_buf) {
|
||||
U6LibItem *item;
|
||||
unsigned char *buf, *lzw_buf;
|
||||
|
||||
if (item_number >= num_offsets)
|
||||
return nullptr;
|
||||
|
||||
item = &items[item_number];
|
||||
|
||||
if (item->size == 0 || item->offset == 0)
|
||||
return nullptr;
|
||||
|
||||
if (ret_buf == nullptr)
|
||||
buf = (unsigned char *)malloc(item->uncomp_size);
|
||||
else
|
||||
buf = ret_buf;
|
||||
|
||||
data->seek(item->offset);
|
||||
|
||||
if (is_compressed(item_number)) {
|
||||
U6Lzw lzw;
|
||||
lzw_buf = (unsigned char *)malloc(item->size);
|
||||
data->readToBuf(lzw_buf, item->size);
|
||||
lzw.decompress_buffer(lzw_buf, item->size, buf, item->uncomp_size);
|
||||
} else {
|
||||
data->readToBuf(buf, item->size);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool U6Lib_n::is_compressed(uint32 item_number) {
|
||||
uint32 i;
|
||||
|
||||
switch (items[item_number].flag) {
|
||||
case 0x1 :
|
||||
case 0x20 :
|
||||
return true;
|
||||
case 0xff :
|
||||
for (i = item_number; i < num_offsets; i++) {
|
||||
if (items[i].flag != 0xff)
|
||||
break;
|
||||
}
|
||||
if (i < num_offsets)
|
||||
return is_compressed(i);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void U6Lib_n::parse_lib() {
|
||||
uint32 i;
|
||||
bool skip4 = false;
|
||||
|
||||
if (lib_size != 2 && lib_size != 4)
|
||||
return;
|
||||
|
||||
data->seekStart();
|
||||
|
||||
if (game_type != NUVIE_GAME_U6) { //U6 doesn't have a 4 byte filesize header.
|
||||
skip4 = true;
|
||||
filesize = data->read4();
|
||||
} else
|
||||
filesize = data->get_size();
|
||||
|
||||
num_offsets = calculate_num_offsets(skip4);
|
||||
|
||||
items = (U6LibItem *)malloc(sizeof(U6LibItem) * (num_offsets + 1));
|
||||
memset(items, 0, sizeof(U6LibItem) * (num_offsets + 1));
|
||||
|
||||
data->seekStart();
|
||||
if (skip4)
|
||||
data->seek(0x4);
|
||||
for (i = 0; i < num_offsets && !data->is_end(); i++) {
|
||||
if (lib_size == 2)
|
||||
items[i].offset = data->read2();
|
||||
else {
|
||||
items[i].offset = data->read4();
|
||||
// U6 converse files dont have flag?
|
||||
items[i].flag = (items[i].offset & 0xff000000) >> 24; //extract flag byte
|
||||
items[i].offset &= 0xffffff;
|
||||
}
|
||||
}
|
||||
|
||||
items[num_offsets].offset = filesize; //this is used to calculate the size of the last item in the lib.
|
||||
|
||||
calculate_item_sizes();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// for reading, calculate item sizes based on offsets
|
||||
void U6Lib_n::calculate_item_sizes() {
|
||||
uint32 i, next_offset = 0;
|
||||
|
||||
for (i = 0; i < num_offsets; i++) {
|
||||
items[i].size = 0;
|
||||
// get next non-zero offset, including the filesize at items[num_offsets]
|
||||
for (uint32 o = (i + 1); o <= num_offsets; o++)
|
||||
if (items[o].offset) {
|
||||
next_offset = items[o].offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (items[i].offset && (next_offset > items[i].offset))
|
||||
items[i].size = next_offset - items[i].offset;
|
||||
|
||||
items[i].uncomp_size = calculate_item_uncomp_size(&items[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// for reading, calculate uncompressed item size based on item flag
|
||||
uint32 U6Lib_n::calculate_item_uncomp_size(U6LibItem *item) {
|
||||
uint32 uncomp_size = 0;
|
||||
|
||||
switch (item->flag) {
|
||||
case 0x01 : //compressed
|
||||
case 0x20 : //MD fonts.lzc, MDD_MUS.LZC use this tag among others
|
||||
data->seek(item->offset);
|
||||
uncomp_size = data->read4();
|
||||
break;
|
||||
|
||||
//FIX check this. uncompressed 4 byte item size header
|
||||
case 0xc1 :
|
||||
uncomp_size = item->size; // - 4;
|
||||
break;
|
||||
|
||||
// uncompressed
|
||||
case 0x0 :
|
||||
case 0x2 :
|
||||
case 0xe0 :
|
||||
default :
|
||||
uncomp_size = item->size;
|
||||
break;
|
||||
}
|
||||
|
||||
return uncomp_size;
|
||||
}
|
||||
|
||||
// we need to handle nullptr offsets at the start of the offset table in the converse.a file
|
||||
uint32 U6Lib_n::calculate_num_offsets(bool skip4) { //skip4 bytes of header.
|
||||
uint32 i;
|
||||
uint32 offset = 0;
|
||||
|
||||
if (skip4)
|
||||
data->seek(0x4);
|
||||
|
||||
|
||||
// We assume the first data in the file is directly behind the offset table,
|
||||
// so we continue scanning until we hit a data block.
|
||||
uint32 max_count = 0xffffffff;
|
||||
for (i = 0; !data->is_end(); i++) {
|
||||
if (i == max_count)
|
||||
return i;
|
||||
|
||||
if (lib_size == 2)
|
||||
offset = data->read2();
|
||||
else {
|
||||
offset = data->read4();
|
||||
offset &= 0xffffff; // clear flag byte.
|
||||
}
|
||||
if (offset != 0) {
|
||||
if (skip4)
|
||||
offset -= 4;
|
||||
|
||||
if (offset / lib_size < max_count)
|
||||
max_count = offset / lib_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* For writing multiple files to a lib, read in source filenames and offsets
|
||||
* from an opened index file. Offsets may be ignored when writing.
|
||||
*/
|
||||
void U6Lib_n::load_index(Common::ReadStream *index_f) {
|
||||
char input[256] = "", // input line
|
||||
offset_str[9] = "", // listed offset
|
||||
name[256] = ""; // source file name
|
||||
int in_len = 0, oc = 0; // length of input line, character in copy string
|
||||
int c = 0, entry_count = 0; // character in input line, number of entries
|
||||
|
||||
if (!index_f)
|
||||
return;
|
||||
while (strgets(input, 256, index_f)) {
|
||||
in_len = strlen(input);
|
||||
// skip spaces, read offset, break on #
|
||||
for (c = 0; c < in_len && Common::isSpace(input[c]) && input[c] != '#'; c++);
|
||||
for (oc = 0; c < in_len && !Common::isSpace(input[c]) && input[c] != '#'; c++)
|
||||
offset_str[oc++] = input[c];
|
||||
offset_str[oc] = '\0';
|
||||
// skip spaces, read name, break on # or \n or \r
|
||||
for (; c < in_len && Common::isSpace(input[c]) && input[c] != '#'; c++);
|
||||
for (oc = 0; c < in_len && input[c] != '\n' && input[c] != '\r' && input[c] != '#'; c++)
|
||||
name[oc++] = input[c];
|
||||
name[oc] = '\0';
|
||||
if (strlen(offset_str)) { // if line is not empty (!= zero entry)
|
||||
uint32 offset32 = strtol(offset_str, nullptr, 16);
|
||||
add_item(offset32, name);
|
||||
++entry_count;
|
||||
}
|
||||
offset_str[0] = '\0';
|
||||
oc = 0;
|
||||
}
|
||||
(void)entry_count; // Fix "unused varable" warning
|
||||
}
|
||||
|
||||
|
||||
/* Append an offset and a name to the library. The other fields are initialized.
|
||||
*/
|
||||
void U6Lib_n::add_item(uint32 offset32, const char *name) {
|
||||
if (!num_offsets)
|
||||
items = (U6LibItem *)malloc(sizeof(U6LibItem));
|
||||
else
|
||||
items = (U6LibItem *)nuvie_realloc(items, sizeof(U6LibItem) * (num_offsets + 1));
|
||||
U6LibItem *item = &items[num_offsets];
|
||||
item->offset = offset32;
|
||||
item->name = new string(name);
|
||||
item->size = 0;
|
||||
item->uncomp_size = 0;
|
||||
item->flag = 0; // uncompressed
|
||||
item->data = nullptr;
|
||||
++num_offsets;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the name of (filename associated with) `item_number'.
|
||||
*/
|
||||
const char *U6Lib_n::get_item_name(uint32 item_number) {
|
||||
if (item_number >= num_offsets)
|
||||
return nullptr;
|
||||
return (items[item_number].name ? items[item_number].name->c_str() : nullptr);
|
||||
}
|
||||
|
||||
|
||||
/* Set data for an item, in preparation of writing or to cache the library.
|
||||
* Size & uncompressed size is set to source length.
|
||||
*/
|
||||
void U6Lib_n::set_item_data(uint32 item_number, unsigned char *src, uint32 src_len) {
|
||||
if (item_number >= num_offsets)
|
||||
return;
|
||||
// FIXME: need a way to set an item as compressed or uncompressed so we know
|
||||
// which size to set
|
||||
items[item_number].size = src_len;
|
||||
items[item_number].uncomp_size = src_len;
|
||||
if (src_len) {
|
||||
unsigned char *dcopy = (unsigned char *)malloc(src_len);
|
||||
memcpy(dcopy, src, src_len);
|
||||
items[item_number].data = dcopy;
|
||||
} else
|
||||
items[item_number].data = 0;
|
||||
}
|
||||
|
||||
|
||||
/* For writing, (re)calculate item offsets from item sizes.
|
||||
*/
|
||||
void U6Lib_n::calc_item_offsets() {
|
||||
if (num_offsets == 0)
|
||||
return;
|
||||
if (items[0].size) // first offset is past library index
|
||||
items[0].offset = (num_offsets * lib_size);
|
||||
else
|
||||
items[0].offset = 0; // 0 = no data, no affect on other items
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: sizes[0] == %d\n", sizes[0]);
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: offsets[0] == %d\n", offsets[0]);
|
||||
for (uint32 i = 1; i < num_offsets; i++) {
|
||||
if (items[i].size) {
|
||||
// find previous item with non-zero offset
|
||||
uint32 prev_i = 0;
|
||||
for (uint32 i_sub = 1; i_sub <= i; i_sub++) {
|
||||
prev_i = i - i_sub;
|
||||
if (items[prev_i].offset != 0)
|
||||
break;
|
||||
}
|
||||
items[i].offset = (items[prev_i].offset + items[prev_i].size);
|
||||
if (items[i].offset == 0) // last item had no data; skip index here
|
||||
items[i].offset = (num_offsets * lib_size);
|
||||
} else
|
||||
items[i].offset = 0; // 0 = no data, no affect on other items
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: sizes[%d] == %d\n", i, sizes[i]);
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: offsets[%d] == %d\n", i, offsets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void U6Lib_n::write_header() {
|
||||
data->seekStart();
|
||||
if (game_type == NUVIE_GAME_U6)
|
||||
return;
|
||||
|
||||
uint32 totalSize = 4 + num_offsets * lib_size;
|
||||
|
||||
for (uint i = 0; i < num_offsets; i++) {
|
||||
totalSize += items[i].size;
|
||||
}
|
||||
|
||||
data->write4(totalSize);
|
||||
}
|
||||
|
||||
/* Write the library index. (the 2 or 4 byte offsets before the data)
|
||||
*/
|
||||
void U6Lib_n::write_index() {
|
||||
data->seekStart();
|
||||
if (game_type != NUVIE_GAME_U6) {
|
||||
data->seek(4);
|
||||
}
|
||||
|
||||
for (uint32 o = 0; o < num_offsets; o++) {
|
||||
uint32 offset = items[o].offset;
|
||||
if (game_type != NUVIE_GAME_U6 && offset != 0) {
|
||||
offset += 4;
|
||||
}
|
||||
if (lib_size == 2)
|
||||
data->write2((uint16)offset);
|
||||
else if (lib_size == 4)
|
||||
data->write4(offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Write all item data to the library file at their respective offsets.
|
||||
*/
|
||||
void U6Lib_n::write_items() {
|
||||
for (uint32 i = 0; i < num_offsets; i++)
|
||||
write_item(i);
|
||||
}
|
||||
|
||||
|
||||
/* Write item data to the library file at the indicated offset, unless the
|
||||
* offset is 0 (then the data is considered empty).
|
||||
*/
|
||||
void U6Lib_n::write_item(uint32 item_number) {
|
||||
if (item_number >= num_offsets
|
||||
|| items[item_number].offset == 0 || items[item_number].size == 0)
|
||||
return;
|
||||
if (game_type == NUVIE_GAME_U6)
|
||||
data->seek(items[item_number].offset);
|
||||
else
|
||||
data->seek(items[item_number].offset + 4);
|
||||
((NuvieIOFileWrite *)data)->writeBuf(items[item_number].data, items[item_number].size);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
104
engines/ultima/nuvie/files/u6_lib_n.h
Normal file
104
engines/ultima/nuvie/files/u6_lib_n.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6LIB_N_H
|
||||
#define NUVIE_FILES_U6LIB_N_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::string;
|
||||
//using Std::vector;
|
||||
|
||||
class NuvieIO;
|
||||
|
||||
struct U6LibItem {
|
||||
uint32 offset;
|
||||
uint8 flag;
|
||||
uint32 uncomp_size;
|
||||
uint32 size;
|
||||
string *name;
|
||||
unsigned char *data; // for writing or cache
|
||||
};
|
||||
|
||||
class U6Lib_n {
|
||||
uint32 filesize; // total size of file
|
||||
uint8 game_type; // there are three types of lib files.
|
||||
uint8 lib_size; // measured in bytes either 2 or 4
|
||||
uint32 num_offsets; // number of items, size of lists
|
||||
U6LibItem *items;
|
||||
NuvieIO *data;
|
||||
bool del_data;
|
||||
|
||||
public:
|
||||
U6Lib_n();
|
||||
~U6Lib_n();
|
||||
|
||||
bool open(const Common::Path &filename, uint8 size, uint8 type = NUVIE_GAME_U6);
|
||||
bool open(NuvieIO *new_data, uint8 size, uint8 type = NUVIE_GAME_U6);
|
||||
void close();
|
||||
bool create(const Common::Path &filename, uint8 size, uint8 type = NUVIE_GAME_U6);
|
||||
uint8 get_game_type() {
|
||||
return game_type;
|
||||
}
|
||||
|
||||
unsigned char *get_item(uint32 item_number, unsigned char *buf = nullptr); // read
|
||||
void set_item_data(uint32 item_number, unsigned char *src, uint32 src_len);
|
||||
|
||||
uint32 get_num_items();
|
||||
uint32 get_item_size(uint32 item_number);
|
||||
uint32 get_item_offset(uint32 item_number);
|
||||
const char *get_item_name(uint32 item_number);
|
||||
bool is_compressed(uint32 item_number);
|
||||
|
||||
void add_item(uint32 offset32, const char *name = nullptr);
|
||||
void write_item(uint32 item_number);
|
||||
void write_items();
|
||||
|
||||
void load_index(Common::ReadStream *index_f);
|
||||
void write_index();
|
||||
void write_header();
|
||||
|
||||
void calc_item_offsets();
|
||||
|
||||
protected:
|
||||
void parse_lib();
|
||||
void calculate_item_sizes();
|
||||
uint32 calculate_item_uncomp_size(U6LibItem *item);
|
||||
uint32 calculate_num_offsets(bool skip4);
|
||||
};
|
||||
|
||||
#if 0
|
||||
class U6ConverseLib: U6Lib_n {
|
||||
private:
|
||||
uint32 zero_offset_count;
|
||||
string *conversefile;
|
||||
};
|
||||
#endif
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
|
||||
#endif
|
||||
454
engines/ultima/nuvie/files/u6_lzw.cpp
Normal file
454
engines/ultima/nuvie/files/u6_lzw.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// This code is a modified version of the code from nodling's Ultima 6 website.
|
||||
// https://web.archive.org/web/20091019144234/http://www.geocities.com/nodling/
|
||||
//
|
||||
|
||||
// =============================================================
|
||||
// This program decompresses Ultima_6-style LZW-compressed files
|
||||
// =============================================================
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Lzw::U6Lzw() : dict(new U6LzwDict), stack(new U6LzwStack), errstr("unknown error") {
|
||||
}
|
||||
|
||||
U6Lzw::~U6Lzw() {
|
||||
delete dict;
|
||||
delete stack;
|
||||
}
|
||||
|
||||
|
||||
/* Copy and return the contents of `src' buffer, in LZW form. It is not really
|
||||
* compressed, it just makes it suitable to be read by an LZW decoder.
|
||||
*/
|
||||
unsigned char *U6Lzw::compress_buffer(unsigned char *src, uint32 src_len,
|
||||
uint32 &dest_len) {
|
||||
// FIXME - didn't bother fixing this since its output will be larger than
|
||||
// the uncompressed data
|
||||
uint32 blocks = 0; //, block = 0, b = 0, d = 0, rshift = 0;
|
||||
//uint16 val = 0;
|
||||
//unsigned char *dest_pt = nullptr;
|
||||
unsigned char *dest_buf = (unsigned char *)malloc(4);
|
||||
// add 4 byte uncompressed length value
|
||||
dest_len = 4;
|
||||
memcpy(dest_buf, &src_len, dest_len);
|
||||
blocks = (src_len / 64);
|
||||
if ((blocks * 64) < src_len)
|
||||
blocks += 1;
|
||||
dest_buf = (unsigned char *)nuvie_realloc(dest_buf, src_len + 4);
|
||||
dest_len = src_len + 4;
|
||||
memset(&dest_buf[4], 0, src_len);
|
||||
#if 0
|
||||
for (block = 0, d = 4; block < blocks && b < src_len; block++) {
|
||||
dest_len += 128;
|
||||
dest_buf = (unsigned char *)realloc(dest_buf, dest_len);
|
||||
// add 9 bit value 0x100
|
||||
// rshift += (rshift < 7) ? 1 : -rshift;
|
||||
for (; b < src_len; b++) {
|
||||
// for each byte in block, add 9bit value, upper bit = 0
|
||||
}
|
||||
}
|
||||
// add 9 bit value 0x101
|
||||
#endif
|
||||
return (dest_buf);
|
||||
}
|
||||
|
||||
|
||||
// this function only checks a few *necessary* conditions
|
||||
// returns "FALSE" if the file doesn't satisfy these conditions
|
||||
// return "TRUE" otherwise
|
||||
|
||||
bool U6Lzw::is_valid_lzw_file(NuvieIOFileRead *input_file) {
|
||||
// file must contain 4-byte size header and space for the 9-bit value 0x100
|
||||
if (input_file->get_size() < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the last byte of the size header must be 0 (U6's files aren't *that* big)
|
||||
input_file->seek(3);
|
||||
unsigned char byte3 = input_file->read1();
|
||||
if (byte3 != 0) {
|
||||
return false;
|
||||
}
|
||||
// the 9 bits after the size header must be 0x100
|
||||
input_file->seek(4);
|
||||
unsigned char b0 = input_file->read1();
|
||||
unsigned char b1 = input_file->read1();
|
||||
input_file->seekStart();
|
||||
if ((b0 != 0) || ((b1 & 1) != 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool U6Lzw::is_valid_lzw_buffer(unsigned char *buf, uint32 length) {
|
||||
if (length < 6) {
|
||||
errstr = "is_valid_lzw_buffer: buffer length < 6";
|
||||
return false;
|
||||
}
|
||||
if (buf[3] != 0) {
|
||||
errstr = "is_valid_lzw_buffer: buffer size > 16MB";
|
||||
return false;
|
||||
}
|
||||
if ((buf[4] != 0) || ((buf[5] & 1) != 1)) {
|
||||
errstr = "is_valid_lzw_buffer: first 9 bits of data != 0x100";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
long U6Lzw::get_uncompressed_file_size(NuvieIOFileRead *input_file) {
|
||||
long uncompressed_file_length;
|
||||
|
||||
if (is_valid_lzw_file(input_file)) {
|
||||
input_file->seekStart();
|
||||
uncompressed_file_length = input_file->read4();
|
||||
input_file->seekStart();
|
||||
return (uncompressed_file_length);
|
||||
} else {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
long U6Lzw::get_uncompressed_buffer_size(unsigned char *buf, uint32 length) {
|
||||
if (is_valid_lzw_buffer(buf, length)) {
|
||||
return (buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LZW-decompress from buffer to buffer.
|
||||
// The parameters "source_length" and "destination_length" are currently unused.
|
||||
// They might be used to prevent reading/writing outside the buffers.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned char *U6Lzw::decompress_buffer(unsigned char *source, uint32 source_length, uint32 &destination_length) {
|
||||
unsigned char *destination;
|
||||
sint32 uncomp_size;
|
||||
|
||||
uncomp_size = this->get_uncompressed_buffer_size(source, source_length);
|
||||
if (uncomp_size == -1)
|
||||
return nullptr;
|
||||
else
|
||||
destination_length = uncomp_size;
|
||||
|
||||
destination = (unsigned char *)malloc(destination_length);
|
||||
|
||||
if (decompress_buffer(source, source_length, destination, destination_length) == false) {
|
||||
free(destination);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
bool U6Lzw::decompress_buffer(unsigned char *source, uint32 source_length, unsigned char *destination, uint32 destination_length) {
|
||||
const int max_codeword_length = 12;
|
||||
bool end_marker_reached = false;
|
||||
int codeword_size = 9;
|
||||
long bits_read = 0;
|
||||
int next_free_codeword = 0x102;
|
||||
int dictionary_size = 0x200;
|
||||
|
||||
long bytes_written = 0;
|
||||
|
||||
int cW;
|
||||
int pW = 0; // get rid of uninitialized warning.
|
||||
unsigned char C;
|
||||
|
||||
source += 4; //skip the filesize dword.
|
||||
|
||||
while (! end_marker_reached) {
|
||||
cW = get_next_codeword(&bits_read, source, codeword_size);
|
||||
switch (cW) {
|
||||
// re-init the dictionary
|
||||
case 0x100:
|
||||
codeword_size = 9;
|
||||
next_free_codeword = 0x102;
|
||||
dictionary_size = 0x200;
|
||||
dict->reset();
|
||||
cW = get_next_codeword(&bits_read, source, codeword_size);
|
||||
output_root((unsigned char)cW, destination, &bytes_written);
|
||||
break;
|
||||
// end of compressed file has been reached
|
||||
case 0x101:
|
||||
end_marker_reached = true;
|
||||
break;
|
||||
// (cW <> 0x100) && (cW <> 0x101)
|
||||
default:
|
||||
if (cW < next_free_codeword) { // codeword is already in the dictionary
|
||||
// create the string associated with cW (on the stack)
|
||||
get_string(cW);
|
||||
C = stack->gettop();
|
||||
// output the string represented by cW
|
||||
while (!stack->is_empty()) {
|
||||
output_root(stack->pop(), destination, &bytes_written);
|
||||
}
|
||||
// add pW+C to the dictionary
|
||||
dict->add(C, pW);
|
||||
|
||||
next_free_codeword++;
|
||||
if (next_free_codeword >= dictionary_size) {
|
||||
if (codeword_size < max_codeword_length) {
|
||||
codeword_size += 1;
|
||||
dictionary_size *= 2;
|
||||
}
|
||||
}
|
||||
} else { // codeword is not yet defined
|
||||
// create the string associated with pW (on the stack)
|
||||
get_string(pW);
|
||||
C = stack->gettop();
|
||||
// output the string represented by pW
|
||||
while (!stack->is_empty()) {
|
||||
output_root(stack->pop(), destination, &bytes_written);
|
||||
}
|
||||
// output the char C
|
||||
output_root(C, destination, &bytes_written);
|
||||
// the new dictionary entry must correspond to cW
|
||||
// if it doesn't, something is wrong with the lzw-compressed data.
|
||||
if (cW != next_free_codeword) {
|
||||
DEBUG(0, LEVEL_ERROR, "cW != next_free_codeword!\n");
|
||||
return false;
|
||||
}
|
||||
// add pW+C to the dictionary
|
||||
dict->add(C, pW);
|
||||
|
||||
next_free_codeword++;
|
||||
if (next_free_codeword >= dictionary_size) {
|
||||
if (codeword_size < max_codeword_length) {
|
||||
codeword_size += 1;
|
||||
dictionary_size *= 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
// shift roles - the current cW becomes the new pW
|
||||
pW = cW;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// from file to file
|
||||
// -----------------
|
||||
unsigned char *U6Lzw::decompress_file(const Common::Path &filename, uint32 &destination_length) {
|
||||
unsigned char *source_buffer;
|
||||
unsigned char *destination_buffer;
|
||||
uint32 source_buffer_size;
|
||||
NuvieIOFileRead input_file;
|
||||
|
||||
destination_length = 0;
|
||||
if (input_file.open(filename) == false)
|
||||
return nullptr;
|
||||
|
||||
if (this->is_valid_lzw_file(&input_file)) {
|
||||
// determine the buffer sizes
|
||||
source_buffer_size = input_file.get_size();
|
||||
// destination_buffer_size = this->get_uncompressed_file_size(input_file);
|
||||
|
||||
// create the buffers
|
||||
source_buffer = (unsigned char *)malloc(sizeof(unsigned char) * source_buffer_size);
|
||||
// destination_buffer = (unsigned char *)malloc(sizeof(unsigned char *) * destination_buffer_size);
|
||||
|
||||
// read the input file into the source buffer
|
||||
input_file.seekStart();
|
||||
input_file.readToBuf(source_buffer, source_buffer_size);
|
||||
|
||||
// decompress the input file
|
||||
destination_buffer = this->decompress_buffer(source_buffer, source_buffer_size, destination_length);
|
||||
|
||||
// write the destination buffer to the output file
|
||||
//fwrite(destination_buffer, 1, destination_buffer_size, output_file);
|
||||
|
||||
// destroy the buffers
|
||||
free(source_buffer);
|
||||
//free(destination_buffer);
|
||||
} else {
|
||||
// uncompressed file
|
||||
uint32 destination_buffer_size = input_file.get_size();
|
||||
destination_length = destination_buffer_size - 8;
|
||||
|
||||
destination_buffer = (unsigned char *)malloc(destination_length);
|
||||
|
||||
// data starts at offset 8
|
||||
input_file.seek(8);
|
||||
input_file.readToBuf(destination_buffer, destination_length);
|
||||
}
|
||||
|
||||
return destination_buffer;
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
// Read the next code word from the source buffer
|
||||
// ----------------------------------------------
|
||||
int U6Lzw::get_next_codeword(long *bits_read, unsigned char *source, int codeword_size) {
|
||||
unsigned char b0, b1, b2;
|
||||
int codeword;
|
||||
|
||||
b0 = source[*bits_read / 8];
|
||||
b1 = source[*bits_read / 8 + 1];
|
||||
if (codeword_size + (*bits_read % 8) > 16)
|
||||
b2 = source[*bits_read / 8 + 2]; // only read next byte if necessary
|
||||
else
|
||||
b2 = 0;
|
||||
|
||||
codeword = ((b2 << 16) + (b1 << 8) + b0);
|
||||
codeword = codeword >> (*bits_read % 8);
|
||||
switch (codeword_size) {
|
||||
case 0x9:
|
||||
codeword = codeword & 0x1ff;
|
||||
break;
|
||||
case 0xa:
|
||||
codeword = codeword & 0x3ff;
|
||||
break;
|
||||
case 0xb:
|
||||
codeword = codeword & 0x7ff;
|
||||
break;
|
||||
case 0xc:
|
||||
codeword = codeword & 0xfff;
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, LEVEL_ERROR, "U6Lzw Error: weird codeword size!\n");
|
||||
break;
|
||||
}
|
||||
*bits_read += codeword_size;
|
||||
|
||||
return codeword;
|
||||
}
|
||||
|
||||
void U6Lzw::output_root(unsigned char root, unsigned char *destination, long *position) {
|
||||
destination[*position] = root;
|
||||
*position = *position + 1;
|
||||
}
|
||||
|
||||
void U6Lzw::get_string(int codeword) {
|
||||
unsigned char root;
|
||||
int current_codeword;
|
||||
|
||||
current_codeword = codeword;
|
||||
stack->reset();
|
||||
while (current_codeword > 0xff) {
|
||||
root = dict->get_root(current_codeword);
|
||||
current_codeword = dict->get_codeword(current_codeword);
|
||||
stack->push(root);
|
||||
}
|
||||
|
||||
// push the root at the leaf
|
||||
stack->push((unsigned char)current_codeword);
|
||||
}
|
||||
|
||||
|
||||
U6LzwStack::U6LzwStack() {
|
||||
memset(stack, 0, STACK_SIZE);
|
||||
this->reset();
|
||||
}
|
||||
|
||||
void U6LzwStack::reset(void) {
|
||||
contains = 0;
|
||||
}
|
||||
|
||||
bool U6LzwStack::is_empty(void) {
|
||||
if (contains == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool U6LzwStack::is_full(void) {
|
||||
if (contains == STACK_SIZE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void U6LzwStack::push(unsigned char element) {
|
||||
if (!this->is_full()) {
|
||||
stack[contains] = element;
|
||||
contains++;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char U6LzwStack::pop(void) {
|
||||
unsigned char element;
|
||||
|
||||
if (!this->is_empty()) {
|
||||
element = stack[contains - 1];
|
||||
contains--;
|
||||
} else {
|
||||
element = 0;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
unsigned char U6LzwStack::gettop(void) {
|
||||
if (!this->is_empty()) {
|
||||
return (stack[contains - 1]);
|
||||
}
|
||||
|
||||
return '\0'; /* what should we return here!? */
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------
|
||||
a dictionary class
|
||||
--------------------------------------------------
|
||||
*/
|
||||
|
||||
U6LzwDict::U6LzwDict() {
|
||||
this->reset();
|
||||
memset(&dict, 0, sizeof(dict));
|
||||
}
|
||||
|
||||
void U6LzwDict::reset(void) {
|
||||
contains = 0x102;
|
||||
}
|
||||
|
||||
void U6LzwDict::add(unsigned char root, int codeword) {
|
||||
dict[contains].root = root;
|
||||
dict[contains].codeword = codeword;
|
||||
contains++;
|
||||
}
|
||||
|
||||
unsigned char U6LzwDict::get_root(int codeword) const {
|
||||
return dict[codeword].root;
|
||||
}
|
||||
|
||||
int U6LzwDict::get_codeword(int codeword) const {
|
||||
return dict[codeword].codeword;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
111
engines/ultima/nuvie/files/u6_lzw.h
Normal file
111
engines/ultima/nuvie/files/u6_lzw.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6_LZW_H
|
||||
#define NUVIE_FILES_U6_LZW_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class NuvieIOFileRead;
|
||||
|
||||
// LZW Stack
|
||||
|
||||
#define STACK_SIZE 10000
|
||||
|
||||
class U6LzwStack {
|
||||
protected:
|
||||
unsigned char stack[STACK_SIZE];
|
||||
int contains;
|
||||
|
||||
public:
|
||||
U6LzwStack();
|
||||
|
||||
void reset(void);
|
||||
bool is_empty(void);
|
||||
bool is_full(void);
|
||||
void push(unsigned char element);
|
||||
unsigned char pop(void);
|
||||
unsigned char gettop(void);
|
||||
};
|
||||
|
||||
// LZW dictionary
|
||||
|
||||
#define DICT_SIZE 10000
|
||||
|
||||
typedef struct {
|
||||
unsigned char root;
|
||||
int codeword;
|
||||
int contains;
|
||||
} dict_entry;
|
||||
|
||||
class U6LzwDict {
|
||||
dict_entry dict[DICT_SIZE];
|
||||
int contains;
|
||||
|
||||
public:
|
||||
|
||||
U6LzwDict();
|
||||
|
||||
void reset(void);
|
||||
void add(unsigned char root, int codeword);
|
||||
unsigned char get_root(int codeword) const;
|
||||
int get_codeword(int codeword) const;
|
||||
};
|
||||
|
||||
class U6Lzw {
|
||||
U6LzwStack *stack;
|
||||
U6LzwDict *dict;
|
||||
const char *errstr; // error string
|
||||
public:
|
||||
|
||||
U6Lzw(void);
|
||||
~U6Lzw(void);
|
||||
|
||||
unsigned char *decompress_buffer(unsigned char *source, uint32 source_length, uint32 &destination_length);
|
||||
bool decompress_buffer(unsigned char *source, uint32 source_length, unsigned char *destination, uint32 destination_length);
|
||||
unsigned char *decompress_file(const Common::Path &filename, uint32 &destination_length);
|
||||
unsigned char *compress_buffer(unsigned char *src, uint32 src_len,
|
||||
uint32 &dest_len);
|
||||
const char *strerror() const {
|
||||
return errstr; // get error string
|
||||
}
|
||||
protected:
|
||||
|
||||
bool is_valid_lzw_file(NuvieIOFileRead *input_file);
|
||||
bool is_valid_lzw_buffer(unsigned char *buf, uint32 length);
|
||||
|
||||
long get_uncompressed_file_size(NuvieIOFileRead *input_file);
|
||||
long get_uncompressed_buffer_size(unsigned char *buf, uint32 length);
|
||||
|
||||
int get_next_codeword(long *bits_read, unsigned char *source,
|
||||
int codeword_size);
|
||||
void output_root(unsigned char root, unsigned char *destination,
|
||||
long *position);
|
||||
void get_string(int codeword);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
408
engines/ultima/nuvie/files/u6_shape.cpp
Normal file
408
engines/ultima/nuvie/files/u6_shape.cpp
Normal file
@@ -0,0 +1,408 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "ultima/nuvie/files/u6_shape.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/*
|
||||
* Structure of shape file:
|
||||
* ========================
|
||||
*
|
||||
* -> means input from file
|
||||
* <- means storing pixel data
|
||||
*
|
||||
* .shp files are lzw compressed. After decompressing the file represents
|
||||
* following structure: -> file size (dword)
|
||||
* -> set of offsets (each word)
|
||||
* -> set of shapes
|
||||
*
|
||||
* File size should be quite clear.
|
||||
*
|
||||
* Offsets are stored as unsigned words. The first offset in file is the
|
||||
* offset of the first object (simple, huh?). The number of offsets (objects)
|
||||
* in the file can be calculated as follows:
|
||||
* num_objects = (1st offset - 4) / 4.
|
||||
*
|
||||
* Frame structure: -> num of pixels right from hot spot, X1 (word)
|
||||
* -> num of pixels left from hot spot, X2 (word)
|
||||
* -> num of pixels above hot spot, Y1 (word)
|
||||
* -> num of pixels below hot spot, Y2 (word)
|
||||
* -> set of pixel blocks
|
||||
*
|
||||
* The width of the shape can be calculated by adding X1 and X2 together and
|
||||
* height by adding Y1 and Y2 together. Coordinates for hot spot are X2 and Y1.
|
||||
*
|
||||
* Now the data it self is stored in pixel blocks which are quite complex:
|
||||
* -> number of pixels or repeats, num1 (word)
|
||||
* if (num1 and 1)
|
||||
* repeat num1 >> 1 times
|
||||
* -> temp value (unsigned byte)
|
||||
* if (temp value and 1)
|
||||
* -> pixel
|
||||
* <- store pixel temp value >> 1 times
|
||||
* else
|
||||
* <- read temp value >> 1 bytes
|
||||
* end
|
||||
* else
|
||||
* <- read num >> 1 bytes
|
||||
*
|
||||
* Color number 255 seems to be transperent.
|
||||
*
|
||||
* I hope this clears things up a bit.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* =====================
|
||||
* U6Shape::U6Shape();
|
||||
* =====================
|
||||
*
|
||||
* Just intializes all structures to 0.
|
||||
*/
|
||||
U6Shape::U6Shape() : raw(nullptr), hotx(0), hoty(0), width(0), height(0) {
|
||||
}
|
||||
|
||||
/*
|
||||
* ======================
|
||||
* U6Shape::~U6Shape();
|
||||
* ======================
|
||||
*
|
||||
* Frees all memory allocated by this instance of U6Shape class.
|
||||
*/
|
||||
U6Shape::~U6Shape(void) {
|
||||
if (raw)
|
||||
free(raw);
|
||||
}
|
||||
|
||||
bool U6Shape::init(uint16 w, uint16 h, uint16 hx, uint16 hy) {
|
||||
width = w;
|
||||
height = h;
|
||||
hotx = hx;
|
||||
hoty = hy;
|
||||
raw = (uint8 *)malloc(width * height);
|
||||
|
||||
if (raw == nullptr) {
|
||||
DEBUG(0, LEVEL_ERROR, "malloc failed to allocate space for shape\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(raw, 0xff, width * height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool U6Shape::load(const Common::Path &filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool U6Shape::load(U6Lib_n *file, uint32 index) {
|
||||
unsigned char *buf = file->get_item(index);
|
||||
if (buf != nullptr) {
|
||||
if (load(buf)) {
|
||||
free(buf);
|
||||
return true;
|
||||
} else
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool U6Shape::load_from_lzc(const Common::Path &filename, uint32 idx, uint32 sub_idx) {
|
||||
U6Lib_n lib_n;
|
||||
|
||||
if (!lib_n.open(filename, 4, NUVIE_GAME_MD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idx >= lib_n.get_num_items()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char *buf = lib_n.get_item(idx, nullptr);
|
||||
NuvieIOBuffer io;
|
||||
io.open(buf, lib_n.get_item_size(idx), false);
|
||||
U6Lib_n lib1;
|
||||
lib1.open(&io, 4, NUVIE_GAME_MD);
|
||||
|
||||
if (sub_idx >= lib1.get_num_items()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (load(&lib1, (uint32)sub_idx)) {
|
||||
free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* =========================================
|
||||
* bool U6Shape::load(unsigned char *buf);
|
||||
* =========================================
|
||||
*
|
||||
* Loads shape from buf
|
||||
* Returns true if successful, else returns false.
|
||||
*/
|
||||
bool U6Shape::load(unsigned char *buf) {
|
||||
/* A file already loaded. */
|
||||
if (raw != nullptr)
|
||||
return false;
|
||||
/* NOT REACHED */
|
||||
|
||||
unsigned char *data = buf;
|
||||
|
||||
/* Size and hot point. */
|
||||
width = READ_LE_UINT16(data);
|
||||
data += 2;
|
||||
width += hotx = READ_LE_UINT16(data);
|
||||
data += 2;
|
||||
|
||||
height = hoty = READ_LE_UINT16(data);
|
||||
data += 2;
|
||||
height += READ_LE_UINT16(data);
|
||||
data += 2;
|
||||
|
||||
width++;
|
||||
height++;
|
||||
|
||||
/* Allocate memory for shape and make it all transperent. */
|
||||
raw = (unsigned char *)malloc(width * height);
|
||||
if (raw == nullptr) {
|
||||
DEBUG(0, LEVEL_ERROR, "malloc failed to allocate space for shape\n");
|
||||
return false;
|
||||
}
|
||||
memset(raw, 255, width * height);
|
||||
|
||||
/* Get the pixel data. */
|
||||
uint16 num_pixels;
|
||||
while ((num_pixels = READ_LE_UINT16(data)) != 0) {
|
||||
|
||||
data += 2;
|
||||
|
||||
/* Coordinates relative to hot spot. */
|
||||
sint16 xpos = READ_LE_UINT16(data);
|
||||
data += 2;
|
||||
sint16 ypos = READ_LE_UINT16(data);
|
||||
data += 2;
|
||||
|
||||
if (((hotx + xpos) >= width) || ((hoty + ypos) >= height)) {
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Test if this block of pixels is encoded
|
||||
* (bit0 is set).
|
||||
*/
|
||||
int encoded = num_pixels & 1;
|
||||
|
||||
/* Divide it by 2. */
|
||||
num_pixels >>= 1;
|
||||
|
||||
/* Normal pixel:
|
||||
* =============
|
||||
*
|
||||
* Just fetch as many pixels as num_pixels suggests.
|
||||
*/
|
||||
if (!encoded) {
|
||||
memcpy(raw + (hotx + xpos) +
|
||||
(hoty + ypos) * width, data, num_pixels);
|
||||
data += num_pixels;
|
||||
|
||||
continue;
|
||||
/* NOT REACHED */
|
||||
}
|
||||
|
||||
/* Encoded pixel:
|
||||
* ==============
|
||||
*
|
||||
* Do as many repeats as num_pixels suggests.
|
||||
*/
|
||||
for (int j = 0; j < num_pixels;) {
|
||||
unsigned char num_pixels2 = *data++;
|
||||
int repeat = num_pixels2 & 1;
|
||||
|
||||
num_pixels2 >>= 1;
|
||||
|
||||
/*
|
||||
* Repeat pixel value (data + 1) num_pixels2
|
||||
* times.
|
||||
*/
|
||||
if (repeat) {
|
||||
memset(raw + (hotx + xpos) +
|
||||
(hoty + ypos) * width + j,
|
||||
*data++, num_pixels2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Just fetch as many pixels as num_pixels2
|
||||
* suggests.
|
||||
*/
|
||||
else {
|
||||
memcpy(raw + (hotx + xpos) +
|
||||
(hoty + ypos) * width + j, data,
|
||||
num_pixels2);
|
||||
data += num_pixels2;
|
||||
}
|
||||
j += num_pixels2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO - allow for failure
|
||||
bool U6Shape::load_WoU_background(const Configuration *config, nuvie_game_t game_type) {
|
||||
U6Lib_n file;
|
||||
Common::Path filename;
|
||||
|
||||
if (game_type == NUVIE_GAME_MD)
|
||||
config_get_path(config, "mdscreen.lzc", filename);
|
||||
else // SE
|
||||
config_get_path(config, "screen.lzc", filename);
|
||||
|
||||
file.open(filename, 4, game_type);
|
||||
unsigned char *temp_buf = file.get_item(0);
|
||||
load(temp_buf + 8);
|
||||
free(temp_buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* =====================================
|
||||
* unsigned char *U6Shape::get_data();
|
||||
* =====================================
|
||||
*
|
||||
* Returns raw data representing the shape or nullptr on failure.
|
||||
*/
|
||||
const unsigned char *U6Shape::get_data() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
unsigned char *U6Shape::get_data() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/*
|
||||
* ============================================
|
||||
* Graphics::ManagedSurface *U6Shape::get_shape_surface();
|
||||
* ============================================
|
||||
*
|
||||
* Returns a Graphics::ManagedSurface representing the shape
|
||||
* or nullptr on failure. NOTE! user must free this
|
||||
* data.
|
||||
*/
|
||||
Graphics::ManagedSurface *U6Shape::get_shape_surface() {
|
||||
if (raw == nullptr)
|
||||
return nullptr;
|
||||
|
||||
// Create the surface
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface(width, height,
|
||||
Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
// Copy the raw pixels into it
|
||||
byte *dest = (byte *)surface->getPixels();
|
||||
Common::copy(raw, raw + width * height, dest);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
/*
|
||||
* ====================================================
|
||||
* bool U6Shape::get_hot_point(uint16 *x, uint16 *y);
|
||||
* ====================================================
|
||||
*
|
||||
* Puts the coordinates of the shape in x and y and
|
||||
* returns true or on failure just returns false.
|
||||
*/
|
||||
bool U6Shape::get_hot_point(uint16 *x, uint16 *y) {
|
||||
if (raw == nullptr)
|
||||
return false;
|
||||
/* NOT REACHED */
|
||||
|
||||
*x = hotx;
|
||||
*y = hoty;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ===============================================
|
||||
* bool U6Shape::get_size(uint16 *w, uint16 *h);
|
||||
* ===============================================
|
||||
*
|
||||
* Puts the size of the shape in w and h and
|
||||
* returns true or on failure just returns false.
|
||||
*/
|
||||
bool U6Shape::get_size(uint16 *w, uint16 *h) {
|
||||
if (raw == nullptr)
|
||||
return false;
|
||||
/* NOT REACHED */
|
||||
|
||||
*w = width;
|
||||
*h = height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void U6Shape::draw_line(uint16 sx, uint16 sy, uint16 ex, uint16 ey, uint8 color) {
|
||||
if (raw == nullptr)
|
||||
return;
|
||||
|
||||
draw_line_8bit(sx, sy, ex, ey, color, raw, width, height);
|
||||
}
|
||||
|
||||
bool U6Shape::blit(U6Shape *shp, uint16 x, uint16 y) {
|
||||
if (shp == nullptr)
|
||||
return false;
|
||||
|
||||
const unsigned char *src_data = shp->get_data();
|
||||
uint16 src_w = 0, src_h = 0;
|
||||
|
||||
shp->get_size(&src_w, &src_h);
|
||||
|
||||
if (x + src_w > width || y + src_h > height)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < src_h; i++) {
|
||||
memcpy(&raw[x + y * width + i * width], &src_data[i * src_w], src_w);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void U6Shape::fill(uint8 color) {
|
||||
memset(raw, color, width * height);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
80
engines/ultima/nuvie/files/u6_shape.h
Normal file
80
engines/ultima/nuvie/files/u6_shape.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6_SHAPE_H
|
||||
#define NUVIE_FILES_U6_SHAPE_H
|
||||
|
||||
/*
|
||||
* ==========
|
||||
* Includes
|
||||
* ==========
|
||||
*/
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class U6Lib_n;
|
||||
class Configuration;
|
||||
|
||||
/*
|
||||
* ==================
|
||||
* Class definition
|
||||
* ==================
|
||||
*
|
||||
* U6Shape can load Ultima VI shape files and return the shapes
|
||||
* stored into these files either as a Graphics::ManagedSurface or as raw data.
|
||||
*/
|
||||
class U6Shape {
|
||||
private:
|
||||
uint16 hotx, hoty;
|
||||
|
||||
protected:
|
||||
unsigned char *raw;
|
||||
uint16 width, height;
|
||||
|
||||
public:
|
||||
U6Shape();
|
||||
virtual ~U6Shape();
|
||||
|
||||
bool init(uint16 w, uint16 h, uint16 hx = 0, uint16 hy = 0);
|
||||
virtual bool load(const Common::Path &filename);
|
||||
bool load(U6Lib_n *file, uint32 index);
|
||||
virtual bool load(unsigned char *buf);
|
||||
bool load_from_lzc(const Common::Path &filename, uint32 idx, uint32 sub_idx);
|
||||
bool load_WoU_background(const Configuration *config, nuvie_game_t game_type);
|
||||
|
||||
const unsigned char *get_data() const;
|
||||
unsigned char *get_data();
|
||||
Graphics::ManagedSurface *get_shape_surface();
|
||||
bool get_hot_point(uint16 *x, uint16 *y);
|
||||
bool get_size(uint16 *w, uint16 *h);
|
||||
|
||||
void draw_line(uint16 sx, uint16 sy, uint16 ex, uint16 ey, uint8 color);
|
||||
bool blit(U6Shape *shp, uint16 x, uint16 y);
|
||||
void fill(uint8 color);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
58
engines/ultima/nuvie/files/utils.cpp
Normal file
58
engines/ultima/nuvie/files/utils.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/files/utils.h"
|
||||
#include "ultima/nuvie/nuvie.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/*
|
||||
* Open a file for input,
|
||||
* trying the original name (lower case), and the upper case version
|
||||
* of the name.
|
||||
*
|
||||
* Output: 0 if couldn't open.
|
||||
*/
|
||||
|
||||
bool openFile(Common::ReadStream *&in, const Common::Path &fname) {
|
||||
Common::File *f = new Common::File();
|
||||
if (f->open(fname)) {
|
||||
in = f;
|
||||
return true;
|
||||
} else {
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See if a file exists.
|
||||
*/
|
||||
|
||||
bool fileExists(const Common::Path &fname) {
|
||||
return Common::File::exists(fname);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/nuvie/files/utils.h
Normal file
41
engines/ultima/nuvie/files/utils.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_UTILS_H
|
||||
#define NUVIE_FILES_UTILS_H
|
||||
|
||||
#include "common/path.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
bool openFile(
|
||||
Common::ReadStream *&in, // Input stream to open.
|
||||
const Common::Path &fname // Filename
|
||||
);
|
||||
|
||||
extern bool fileExists(const Common::Path &fname);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
133
engines/ultima/nuvie/fonts/bmp_font.cpp
Normal file
133
engines/ultima/nuvie/fonts/bmp_font.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/fonts/bmp_font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
BMPFont::BMPFont() : char_w(0), char_h(0), font_width_data(nullptr),
|
||||
font_surface(nullptr), rune_mode(false), dual_font_mode(false) {
|
||||
}
|
||||
|
||||
BMPFont::~BMPFont() {
|
||||
if (font_surface)
|
||||
delete font_surface;
|
||||
|
||||
if (font_width_data)
|
||||
free(font_width_data);
|
||||
}
|
||||
|
||||
bool BMPFont::init(const Common::Path &bmp_filename, bool dual_fontmap) {
|
||||
dual_font_mode = dual_fontmap;
|
||||
num_chars = 256;
|
||||
|
||||
Common::Path full_filename = bmp_filename;
|
||||
|
||||
full_filename.appendInPlace(".bmp");
|
||||
|
||||
font_surface = SDL_LoadBMP(full_filename);
|
||||
|
||||
font_surface->setTransparentColor(font_surface->format.RGBToColor(0, 0x70, 0xfc));
|
||||
|
||||
char_w = font_surface->w / 16;
|
||||
char_h = font_surface->h / 16;
|
||||
|
||||
//read font width data. For variable width fonts.
|
||||
full_filename = bmp_filename;
|
||||
full_filename.appendInPlace(".dat");
|
||||
|
||||
NuvieIOFileRead font_width_data_file;
|
||||
if (font_width_data_file.open(full_filename)) {
|
||||
font_width_data = font_width_data_file.readAll();
|
||||
font_width_data_file.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 BMPFont::getStringWidth(const char *str, uint16 string_len) {
|
||||
uint16 i;
|
||||
uint16 w = 0;
|
||||
|
||||
for (i = 0; i < string_len; i++) {
|
||||
if (dual_font_mode && str[i] == '<') {
|
||||
offset = 128;
|
||||
} else if (dual_font_mode && str[i] == '>') {
|
||||
offset = 0;
|
||||
} else {
|
||||
w += getCharWidth(str[i] + offset);
|
||||
}
|
||||
}
|
||||
|
||||
return w;
|
||||
|
||||
|
||||
}
|
||||
uint16 BMPFont::getCharWidth(uint8 c) {
|
||||
if (font_width_data) {
|
||||
return font_width_data[c];
|
||||
}
|
||||
|
||||
return char_w;
|
||||
}
|
||||
|
||||
uint16 BMPFont::drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) {
|
||||
Common::Rect src;
|
||||
Common::Rect dst;
|
||||
|
||||
if (dual_font_mode) {
|
||||
if (char_num == '<') {
|
||||
rune_mode = true;
|
||||
return 0;
|
||||
} else if (char_num == '>') {
|
||||
rune_mode = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (rune_mode) {
|
||||
char_num += 128;
|
||||
}
|
||||
|
||||
src.left = (char_num % 16) * char_w;
|
||||
src.top = (char_num / 16) * char_h;
|
||||
src.setWidth(char_w);
|
||||
src.setHeight(char_h);
|
||||
|
||||
dst.left = x;
|
||||
dst.top = y;
|
||||
dst.setWidth(char_w);
|
||||
dst.setHeight(char_h);
|
||||
|
||||
SDL_BlitSurface(font_surface, &src, screen->get_sdl_surface(), &dst);
|
||||
|
||||
return getCharWidth(char_num);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
61
engines/ultima/nuvie/fonts/bmp_font.h
Normal file
61
engines/ultima/nuvie/fonts/bmp_font.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FONTS_BMP_FONT_H
|
||||
#define NUVIE_FONTS_BMP_FONT_H
|
||||
|
||||
#include "ultima/nuvie/fonts/font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Screen;
|
||||
|
||||
class BMPFont : public Font {
|
||||
Graphics::ManagedSurface *font_surface;
|
||||
uint8 *font_width_data;
|
||||
|
||||
uint16 char_w, char_h;
|
||||
|
||||
bool dual_font_mode;
|
||||
bool rune_mode;
|
||||
|
||||
public:
|
||||
|
||||
BMPFont();
|
||||
~BMPFont() override;
|
||||
|
||||
bool init(const Common::Path &bmp_filename, bool dual_fontmap = false);
|
||||
|
||||
uint16 getCharWidth(uint8 c) override;
|
||||
uint16 getCharHeight() override {
|
||||
return 16;
|
||||
}
|
||||
uint16 drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) override;
|
||||
uint16 getStringWidth(const char *str, uint16 string_len) override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
62
engines/ultima/nuvie/fonts/conv_font.cpp
Normal file
62
engines/ultima/nuvie/fonts/conv_font.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/fonts/conv_font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
ConvFont::ConvFont() : data_offset(0), f_data(nullptr), f_w_data(nullptr) {
|
||||
}
|
||||
|
||||
ConvFont::~ConvFont() {
|
||||
|
||||
}
|
||||
|
||||
bool ConvFont::init(unsigned char *data, uint8 *width_data, uint16 num_c, uint16 char_offset) {
|
||||
assert(data && width_data);
|
||||
data_offset = char_offset;
|
||||
num_chars = num_c;
|
||||
|
||||
f_data = data;
|
||||
f_w_data = width_data;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 ConvFont::getCharWidth(uint8 c) {
|
||||
return f_w_data[c + data_offset];
|
||||
}
|
||||
|
||||
uint16 ConvFont::drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) {
|
||||
|
||||
unsigned char *buf = (unsigned char *)f_data + (data_offset * 64) + (char_num % 16) * 8 + (char_num / 16) * 128 * 8;
|
||||
screen->blit(x, y, buf, 8, 8, 8, 128, true, nullptr);
|
||||
return getCharWidth(char_num);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
55
engines/ultima/nuvie/fonts/conv_font.h
Normal file
55
engines/ultima/nuvie/fonts/conv_font.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FONTS_CONV_FONT_H
|
||||
#define NUVIE_FONTS_CONV_FONT_H
|
||||
|
||||
#include "ultima/nuvie/fonts/font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Screen;
|
||||
|
||||
class ConvFont : public Font {
|
||||
unsigned char *f_data;
|
||||
uint8 *f_w_data;
|
||||
uint16 data_offset;
|
||||
public:
|
||||
|
||||
ConvFont();
|
||||
~ConvFont() override;
|
||||
|
||||
bool init(unsigned char *data, uint8 *width_data, uint16 num_chars, uint16 char_offset);
|
||||
|
||||
uint16 getCharWidth(uint8 c) override;
|
||||
uint16 getCharHeight() override {
|
||||
return 0;
|
||||
}
|
||||
uint16 drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
97
engines/ultima/nuvie/fonts/font.cpp
Normal file
97
engines/ultima/nuvie/fonts/font.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_shape.h"
|
||||
#include "ultima/nuvie/fonts/font.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
Font::Font() : num_chars(0), offset(0), default_color(FONT_COLOR_U6_NORMAL),
|
||||
default_highlight_color(FONT_COLOR_U6_HIGHLIGHT) {
|
||||
}
|
||||
|
||||
Font::~Font() {
|
||||
|
||||
}
|
||||
|
||||
uint16 Font::drawString(Screen *screen, const char *str, uint16 x, uint16 y) {
|
||||
return drawString(screen, str, strlen(str), x, y, default_color, default_highlight_color);
|
||||
}
|
||||
|
||||
uint16 Font::drawString(Screen *screen, const char *str, uint16 x, uint16 y, uint8 color, uint8 highlight_color) {
|
||||
return drawString(screen, str, strlen(str), x, y, color, highlight_color);
|
||||
}
|
||||
|
||||
uint16 Font::drawString(Screen *screen, const char *str, uint16 string_len, uint16 x, uint16 y, uint8 color, uint8 highlight_color) {
|
||||
uint16 i;
|
||||
bool highlight = false;
|
||||
uint16 font_len = 0;
|
||||
|
||||
for (i = 0; i < string_len; i++) {
|
||||
if (str[i] == '@')
|
||||
highlight = true;
|
||||
else {
|
||||
if (!Common::isAlpha(str[i]))
|
||||
highlight = false;
|
||||
font_len += drawChar(screen, get_char_num(str[i]), x + font_len, y,
|
||||
highlight ? highlight_color : color);
|
||||
}
|
||||
}
|
||||
highlight = false;
|
||||
return font_len;
|
||||
}
|
||||
|
||||
uint8 Font::get_char_num(uint8 c) {
|
||||
if (c >= offset && c < offset + num_chars)
|
||||
c -= offset;
|
||||
else
|
||||
c = 0;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
uint16 Font::getStringWidth(const char *str) {
|
||||
return getStringWidth(str, strlen(str));
|
||||
}
|
||||
|
||||
uint16 Font::getStringWidth(const char *str, uint16 string_len) {
|
||||
uint16 i;
|
||||
uint16 w = 0;
|
||||
|
||||
for (i = 0; i < string_len; i++) {
|
||||
w += getCharWidth(str[i]);
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
uint16 Font::drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y) {
|
||||
return drawChar(screen, char_num, x, y, default_color);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
95
engines/ultima/nuvie/fonts/font.h
Normal file
95
engines/ultima/nuvie/fonts/font.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FONTS_FONT_H
|
||||
#define NUVIE_FONTS_FONT_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define FONT_COLOR_U6_NORMAL 0x48
|
||||
#define FONT_COLOR_U6_HIGHLIGHT 0x0c
|
||||
#define FONT_COLOR_WOU_NORMAL 0
|
||||
#define FONT_COLOR_WOU_CONVERSE_INPUT 1
|
||||
|
||||
#define FONT_COLOR_WOU_HIGHLIGHT 4
|
||||
|
||||
#define FONT_UP_ARROW_CHAR 19
|
||||
#define FONT_DOWN_ARROW_CHAR 20
|
||||
|
||||
class Configuration;
|
||||
class Screen;
|
||||
class U6Shape;
|
||||
|
||||
class Font {
|
||||
protected:
|
||||
uint16 num_chars;
|
||||
uint16 offset;
|
||||
uint8 default_color, default_highlight_color;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Font();
|
||||
virtual ~Font();
|
||||
uint8 getDefaultColor() {
|
||||
return default_color;
|
||||
}
|
||||
void setDefaultColor(uint8 color) {
|
||||
default_color = color;
|
||||
}
|
||||
void setDefaultHighlightColor(uint8 color) {
|
||||
default_highlight_color = color;
|
||||
}
|
||||
|
||||
// bool drawString(Screen *screen, Std::string str, uint16 x, uint16 y);
|
||||
uint16 drawString(Screen *screen, const char *str, uint16 x, uint16 y);
|
||||
uint16 drawString(Screen *screen, const char *str, uint16 x, uint16 y, uint8 color, uint8 highlight_color);
|
||||
uint16 drawString(Screen *screen, const char *str, uint16 string_len, uint16 x, uint16 y, uint8 color, uint8 highlight_color);
|
||||
|
||||
uint16 drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y);
|
||||
virtual uint16 drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) = 0;
|
||||
|
||||
uint16 drawStringToShape(U6Shape *shp, const char *str, uint16 x, uint16 y, uint8 color);
|
||||
uint8 drawCharToShape(U6Shape *shp, uint8 char_num, uint16 x, uint16 y, uint8 color);
|
||||
|
||||
virtual uint16 getCharWidth(uint8 c) = 0;
|
||||
virtual uint16 getCharHeight() = 0;
|
||||
uint16 getStringWidth(const char *str);
|
||||
virtual uint16 getStringWidth(const char *str, uint16 string_len);
|
||||
|
||||
void setOffset(uint16 off) {
|
||||
offset = off;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint8 get_char_num(uint8 c);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
198
engines/ultima/nuvie/fonts/font_manager.cpp
Normal file
198
engines/ultima/nuvie/fonts/font_manager.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
#include "ultima/nuvie/files/nuvie_bmp_file.h"
|
||||
#include "ultima/nuvie/fonts/font_manager.h"
|
||||
#include "ultima/nuvie/fonts/font.h"
|
||||
#include "ultima/nuvie/fonts/conv_font.h"
|
||||
#include "ultima/nuvie/fonts/u6_font.h"
|
||||
#include "ultima/nuvie/fonts/wou_font.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
FontManager::FontManager(const Configuration *cfg) : config(cfg), num_fonts(0),
|
||||
conv_font(nullptr), conv_garg_font(nullptr), conv_font_data(nullptr),
|
||||
conv_font_widths(nullptr) {
|
||||
}
|
||||
|
||||
FontManager::~FontManager() {
|
||||
for (auto *font : fonts) {
|
||||
delete font;
|
||||
}
|
||||
if (conv_font) {
|
||||
delete conv_font;
|
||||
}
|
||||
if (conv_garg_font) {
|
||||
delete conv_garg_font;
|
||||
}
|
||||
if (conv_font_data) {
|
||||
free(conv_font_data);
|
||||
}
|
||||
if (conv_font_widths) {
|
||||
free(conv_font_widths);
|
||||
}
|
||||
}
|
||||
|
||||
bool FontManager::init(nuvie_game_t game_type) {
|
||||
initConvFonts(game_type);
|
||||
|
||||
if (game_type == NUVIE_GAME_U6)
|
||||
return initU6();
|
||||
|
||||
return initWOUSystemFont();
|
||||
}
|
||||
|
||||
bool FontManager::initU6() {
|
||||
U6Font *font;
|
||||
unsigned char *font_data;
|
||||
Common::Path filename;
|
||||
NuvieIOFileRead u6_ch;
|
||||
|
||||
config_get_path(config, "u6.ch", filename);
|
||||
|
||||
if (u6_ch.open(filename) == false)
|
||||
return false;
|
||||
|
||||
font_data = u6_ch.readAll();
|
||||
if (font_data == nullptr || u6_ch.get_size() < 256 * 8)
|
||||
return false;
|
||||
|
||||
// english font
|
||||
font = new U6Font();
|
||||
font->init(font_data, 128, 0);
|
||||
fonts.push_back(font);
|
||||
num_fonts++;
|
||||
|
||||
// runic & gargoyle font
|
||||
font = new U6Font();
|
||||
font->init(&font_data[128 * 8], 128, 0);
|
||||
fonts.push_back(font);
|
||||
num_fonts++;
|
||||
|
||||
free(font_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontManager::initWOU(Std::string filename) {
|
||||
WOUFont *font;
|
||||
Common::Path path;
|
||||
U6Lib_n lib_file;
|
||||
|
||||
config_get_path(config, filename, path);
|
||||
|
||||
lib_file.open(path, 4, NUVIE_GAME_MD); //can be either SE or MD just as long as it isn't set to U6 type.
|
||||
|
||||
font = new WOUFont();
|
||||
unsigned char *buf = lib_file.get_item(0);
|
||||
font->initWithBuffer(buf, lib_file.get_item_size(0)); //buf will be freed by ~Font()
|
||||
fonts.push_back(font);
|
||||
num_fonts++;
|
||||
/*
|
||||
font = new Font();
|
||||
font->init(path.c_str());
|
||||
fonts.push_back(font);
|
||||
num_fonts++;
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontManager::initWOUSystemFont() {
|
||||
U6Font *font;
|
||||
Common::Path path;
|
||||
U6Lib_n lib_file;
|
||||
|
||||
config_get_path(config, "system.lzc", path);
|
||||
|
||||
lib_file.open(path, 4, NUVIE_GAME_MD);
|
||||
|
||||
font = new U6Font();
|
||||
unsigned char *buf = lib_file.get_item(3);
|
||||
font->init(buf, 128, 0);
|
||||
font->setDefaultColor(FONT_COLOR_WOU_NORMAL);
|
||||
font->setDefaultHighlightColor(FONT_COLOR_WOU_HIGHLIGHT);
|
||||
free(buf);
|
||||
fonts.push_back(font);
|
||||
num_fonts++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontManager::initConvFonts(nuvie_game_t game_type) {
|
||||
char filename[7]; // u6.bmp\0 or u6.dat\0
|
||||
Common::Path datadir = GUI::get_gui()->get_data_dir();
|
||||
Common::Path path;
|
||||
|
||||
build_path(datadir, "images", path);
|
||||
datadir = path;
|
||||
build_path(datadir, "gumps", path);
|
||||
datadir = path;
|
||||
build_path(datadir, "fonts", path);
|
||||
datadir = path;
|
||||
|
||||
Common::Path imagefile;
|
||||
Common::sprintf_s(filename, "%s.bmp", get_game_tag(Game::get_game()->get_game_type()));
|
||||
|
||||
build_path(datadir, filename, imagefile);
|
||||
|
||||
NuvieBmpFile bmp;
|
||||
|
||||
bmp.load(imagefile);
|
||||
|
||||
conv_font_data = bmp.getRawIndexedDataCopy();
|
||||
|
||||
Common::Path widthfile;
|
||||
Common::sprintf_s(filename, "%s.dat", get_game_tag(Game::get_game()->get_game_type()));
|
||||
|
||||
build_path(datadir, filename, widthfile);
|
||||
|
||||
NuvieIOFileRead datfile;
|
||||
datfile.open(widthfile);
|
||||
uint32 bytes_read;
|
||||
conv_font_widths = datfile.readBuf(256, &bytes_read);
|
||||
datfile.close();
|
||||
|
||||
conv_font = new ConvFont();
|
||||
((ConvFont *)conv_font)->init(conv_font_data, conv_font_widths, 256, 0);
|
||||
|
||||
if (game_type == NUVIE_GAME_U6) {
|
||||
conv_garg_font = new ConvFont();
|
||||
((ConvFont *)conv_garg_font)->init(conv_font_data, conv_font_widths, 256, 128);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Font *FontManager::get_font(uint16 font_number) {
|
||||
if (num_fonts > 0 && font_number < num_fonts)
|
||||
return fonts[font_number]; //fonts.at(font_number);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
72
engines/ultima/nuvie/fonts/font_manager.h
Normal file
72
engines/ultima/nuvie/fonts/font_manager.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FONTS_FONT_MANAGER_H
|
||||
#define NUVIE_FONTS_FONT_MANAGER_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Font;
|
||||
|
||||
#define NUVIE_FONT_NORMAL 0
|
||||
#define NUVIE_FONT_GARG 1
|
||||
|
||||
class FontManager {
|
||||
const Configuration *config;
|
||||
|
||||
Std::vector<Font *> fonts;
|
||||
uint16 num_fonts;
|
||||
Font *conv_font;
|
||||
Font *conv_garg_font;
|
||||
unsigned char *conv_font_data;
|
||||
uint8 *conv_font_widths;
|
||||
public:
|
||||
|
||||
FontManager(const Configuration *cfg);
|
||||
~FontManager();
|
||||
|
||||
bool init(nuvie_game_t game_type);
|
||||
|
||||
|
||||
Font *get_font(uint16 font_number);
|
||||
Font *get_conv_font() {
|
||||
return conv_font;
|
||||
}
|
||||
Font *get_conv_garg_font() {
|
||||
return conv_garg_font;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool initU6();
|
||||
bool initWOU(Std::string filename);
|
||||
bool initWOUSystemFont();
|
||||
bool initConvFonts(nuvie_game_t game_type);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
80
engines/ultima/nuvie/fonts/u6_font.cpp
Normal file
80
engines/ultima/nuvie/fonts/u6_font.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/fonts/u6_font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Font::U6Font() : font_data(nullptr) {
|
||||
}
|
||||
|
||||
U6Font::~U6Font() {
|
||||
if (font_data != nullptr)
|
||||
free(font_data);
|
||||
}
|
||||
|
||||
bool U6Font::init(unsigned char *data, uint16 num_c, uint16 char_offset) {
|
||||
offset = char_offset;
|
||||
num_chars = num_c;
|
||||
|
||||
font_data = (unsigned char *)malloc(num_chars * 8);
|
||||
memcpy(font_data, data, num_chars * 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 U6Font::drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) {
|
||||
unsigned char buf[64];
|
||||
unsigned char *pixels;
|
||||
uint16 i, j;
|
||||
unsigned char *font;
|
||||
uint16 pitch;
|
||||
|
||||
memset(buf, 0xff, 64);
|
||||
|
||||
//pixels = (unsigned char *)screen->get_pixels();
|
||||
pixels = buf;
|
||||
pitch = 8;//screen->get_pitch();
|
||||
|
||||
font = &font_data[char_num * 8];
|
||||
|
||||
//pixels += y * pitch + x;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 8; j > 0; j--) {
|
||||
if (font[i] & (1 << (j - 1)))
|
||||
pixels[8 - j] = color; // 0th palette entry should be black
|
||||
}
|
||||
|
||||
pixels += pitch;
|
||||
}
|
||||
|
||||
screen->blit(x, y, buf, 8, 8, 8, 8, true, nullptr);
|
||||
return 8;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
62
engines/ultima/nuvie/fonts/u6_font.h
Normal file
62
engines/ultima/nuvie/fonts/u6_font.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FONTS_U6_FONT_H
|
||||
#define NUVIE_FONTS_U6_FONT_H
|
||||
|
||||
#define FONT_COLOR_U6_NORMAL 0x48
|
||||
#define FONT_COLOR_U6_HIGHLIGHT 0x0c
|
||||
|
||||
#include "ultima/nuvie/fonts/font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Screen;
|
||||
|
||||
class U6Font : public Font {
|
||||
private:
|
||||
unsigned char *font_data;
|
||||
|
||||
public:
|
||||
|
||||
U6Font();
|
||||
~U6Font() override;
|
||||
|
||||
bool init(unsigned char *data, uint16 num_chars, uint16 char_offset);
|
||||
|
||||
uint16 getCharWidth(uint8 c) override {
|
||||
return 8;
|
||||
}
|
||||
uint16 getCharHeight() override {
|
||||
return 8;
|
||||
}
|
||||
uint16 drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) override;
|
||||
protected:
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
171
engines/ultima/nuvie/fonts/wou_font.cpp
Normal file
171
engines/ultima/nuvie/fonts/wou_font.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/screen/screen.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_shape.h"
|
||||
#include "ultima/nuvie/fonts/wou_font.h"
|
||||
#include "ultima/nuvie/core/game.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
WOUFont::WOUFont() : font_data(nullptr), char_buf(nullptr), height(0),
|
||||
pixel_char(0) {
|
||||
}
|
||||
|
||||
WOUFont::~WOUFont() {
|
||||
if (font_data != nullptr)
|
||||
free(font_data);
|
||||
|
||||
if (char_buf != nullptr)
|
||||
free(char_buf);
|
||||
}
|
||||
|
||||
bool WOUFont::init(const Common::Path &filename) {
|
||||
|
||||
U6Lzw lzw;
|
||||
uint32 decomp_size;
|
||||
font_data = lzw.decompress_file(filename, decomp_size);
|
||||
|
||||
height = font_data[0];
|
||||
pixel_char = font_data[2];
|
||||
|
||||
num_chars = 256;
|
||||
if (Game::get_game()->get_game_type() != NUVIE_GAME_U6) {
|
||||
default_color = FONT_COLOR_WOU_NORMAL;
|
||||
default_highlight_color = FONT_COLOR_WOU_HIGHLIGHT;
|
||||
}
|
||||
return initCharBuf();
|
||||
}
|
||||
|
||||
bool WOUFont::initWithBuffer(unsigned char *buffer, uint32 buffer_len) {
|
||||
font_data = buffer;
|
||||
|
||||
height = font_data[0];
|
||||
pixel_char = font_data[2];
|
||||
|
||||
num_chars = 256;
|
||||
if (Game::get_game()->get_game_type() != NUVIE_GAME_U6) {
|
||||
default_color = FONT_COLOR_WOU_NORMAL;
|
||||
default_highlight_color = FONT_COLOR_WOU_HIGHLIGHT;
|
||||
}
|
||||
|
||||
return initCharBuf();
|
||||
}
|
||||
|
||||
bool WOUFont::initCharBuf() {
|
||||
uint8 max_width = 0;
|
||||
for (uint16 i = 0; i < num_chars; i++) {
|
||||
uint8 width = font_data[0x4 + i];
|
||||
if (width > max_width) {
|
||||
max_width = width;
|
||||
}
|
||||
}
|
||||
char_buf = (unsigned char *)malloc(max_width * height);
|
||||
if (char_buf == nullptr)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 WOUFont::getCharWidth(uint8 c) {
|
||||
if (font_data == nullptr)
|
||||
return 0;
|
||||
|
||||
return font_data[0x4 + get_char_num(c)];
|
||||
}
|
||||
|
||||
|
||||
uint16 WOUFont::drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) {
|
||||
unsigned char *pixels;
|
||||
uint16 width;
|
||||
|
||||
if (font_data == nullptr)
|
||||
return false;
|
||||
|
||||
pixels = font_data + font_data[0x204 + char_num] * 256 + font_data[0x104 + char_num];
|
||||
width = font_data[0x4 + char_num];
|
||||
|
||||
memset(char_buf, 0xff, width * height);
|
||||
|
||||
//pixels += y * pitch + x;
|
||||
for (uint8 i = 0; i < (width * height); i++) {
|
||||
if (pixels[i] == pixel_char)
|
||||
char_buf[i] = color;
|
||||
}
|
||||
|
||||
screen->blit(x, y, char_buf, 8, width, height, width, true, nullptr);
|
||||
return width;
|
||||
}
|
||||
|
||||
uint16 WOUFont::drawStringToShape(U6Shape *shp, const char *str, uint16 x, uint16 y, uint8 color) {
|
||||
uint16 i;
|
||||
uint16 string_len = strlen(str);
|
||||
|
||||
if (font_data == nullptr)
|
||||
return x;
|
||||
|
||||
for (i = 0; i < string_len; i++) {
|
||||
x += drawCharToShape(shp, get_char_num(str[i]), x, y, color);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
uint8 WOUFont::drawCharToShape(U6Shape *shp, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) {
|
||||
unsigned char *pixels;
|
||||
uint16 i, j;
|
||||
unsigned char *font;
|
||||
uint16 pitch;
|
||||
uint16 dst_w, dst_h;
|
||||
|
||||
pixels = shp->get_data();
|
||||
shp->get_size(&dst_w, &dst_h);
|
||||
pitch = dst_w;
|
||||
|
||||
pixels += y * pitch + x;
|
||||
|
||||
uint16 width;
|
||||
|
||||
font = font_data + font_data[0x204 + char_num] * 256 + font_data[0x104 + char_num];
|
||||
width = font_data[0x4 + char_num];
|
||||
|
||||
for (i = 0; i < height; i++) {
|
||||
for (j = 0; j < width; j++) {
|
||||
if (font[j] == pixel_char) {
|
||||
pixels[j] = color;
|
||||
}
|
||||
}
|
||||
|
||||
font += width;
|
||||
pixels += pitch;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
67
engines/ultima/nuvie/fonts/wou_font.h
Normal file
67
engines/ultima/nuvie/fonts/wou_font.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FONTS_WOU_FONT_H
|
||||
#define NUVIE_FONTS_WOU_FONT_H
|
||||
|
||||
#include "ultima/nuvie/fonts/font.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
class Screen;
|
||||
class U6Shape;
|
||||
|
||||
class WOUFont : public Font {
|
||||
private:
|
||||
unsigned char *font_data;
|
||||
unsigned char *char_buf;
|
||||
uint16 height;
|
||||
uint8 pixel_char;
|
||||
|
||||
public:
|
||||
|
||||
WOUFont();
|
||||
~WOUFont() override;
|
||||
|
||||
bool init(const Common::Path &filename);
|
||||
bool initWithBuffer(unsigned char *buffer, uint32 buffer_len);
|
||||
|
||||
|
||||
uint16 drawChar(Screen *screen, uint8 char_num, uint16 x, uint16 y,
|
||||
uint8 color) override;
|
||||
|
||||
uint16 drawStringToShape(U6Shape *shp, const char *str, uint16 x, uint16 y, uint8 color);
|
||||
uint8 drawCharToShape(U6Shape *shp, uint8 char_num, uint16 x, uint16 y, uint8 color);
|
||||
|
||||
uint16 getCharWidth(uint8 c) override;
|
||||
uint16 getCharHeight() override {
|
||||
return height;
|
||||
}
|
||||
private:
|
||||
bool initCharBuf();
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
414
engines/ultima/nuvie/gui/gui.cpp
Normal file
414
engines/ultima/nuvie/gui/gui.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This is a C++ class for handling a GUI, and associated widgets */
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/conf/configuration.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
#include "ultima/nuvie/gui/gui_types.h"
|
||||
#include "ultima/nuvie/keybinding/keys.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
const int GUI::mouseclick_delay = 300; /* SB-X */
|
||||
|
||||
|
||||
/* Number of widget elements to allocate at once */
|
||||
#define WIDGET_ARRAYCHUNK 32
|
||||
|
||||
GUI *GUI::gui = nullptr;
|
||||
|
||||
GUI::GUI(const Configuration *c, Screen *s) : config(c), screen(s), numwidgets(0),
|
||||
maxwidgets(0), widgets(nullptr), display(1), running(0), dragging(false),
|
||||
full_redraw(true), focused_widget(nullptr), locked_widget(nullptr),
|
||||
block_input(false) {
|
||||
gui = this;
|
||||
selected_color = new GUI_Color(10, 10, 50);
|
||||
selected_color->map_color(screen->get_sdl_surface()->format);
|
||||
|
||||
gui_font = new GUI_Font();
|
||||
gui_drag_manager = new GUI_DragManager(screen);
|
||||
}
|
||||
|
||||
GUI::~GUI() {
|
||||
if (widgets != nullptr) {
|
||||
for (int i = 0; i < numwidgets; ++i) {
|
||||
delete widgets[i];
|
||||
}
|
||||
free(widgets);
|
||||
}
|
||||
|
||||
delete selected_color;
|
||||
|
||||
delete gui_font;
|
||||
delete gui_drag_manager;
|
||||
}
|
||||
|
||||
/* Add a widget to the GUI.
|
||||
The widget will be automatically deleted when the GUI is deleted.
|
||||
*/
|
||||
int GUI::AddWidget(GUI_Widget *widget) {
|
||||
int i = numwidgets;
|
||||
|
||||
// This is commented out because it makes it unsafe to call AddWidget
|
||||
// from a widget's event handler: It could delete the calling widget
|
||||
// if it was marked for deletion during event handling.
|
||||
/* Look for deleted widgets */
|
||||
// for (i = 0; i < numwidgets; ++i) {
|
||||
// if (widgets[i]->Status() == WIDGET_DELETED) {
|
||||
// delete widgets[i];
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (i == numwidgets) {
|
||||
/* Expand the widgets array if necessary */
|
||||
if (numwidgets == maxwidgets) {
|
||||
GUI_Widget **newarray;
|
||||
int maxarray;
|
||||
|
||||
maxarray = maxwidgets + WIDGET_ARRAYCHUNK;
|
||||
if ((newarray = (GUI_Widget **)realloc(widgets,
|
||||
maxarray * sizeof(*newarray))) == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
widgets = newarray;
|
||||
maxwidgets = maxarray;
|
||||
}
|
||||
++numwidgets;
|
||||
// }
|
||||
widgets[i] = widget;
|
||||
widget->PlaceOnScreen(screen, gui_drag_manager, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove widget from gui system but don't delete it */
|
||||
bool GUI::removeWidget(GUI_Widget *widget) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numwidgets; ++i) {
|
||||
if (widgets[i] == widget) {
|
||||
for (int j = i + 1; j < numwidgets; ++j) { //shuffle remaining widgets down.
|
||||
widgets[j - 1] = widgets[j];
|
||||
}
|
||||
|
||||
--numwidgets;
|
||||
force_full_redraw();
|
||||
Display();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GUI::CleanupDeletedWidgets(bool redraw) {
|
||||
/* Garbage collection */
|
||||
if (locked_widget && locked_widget->Status() == WIDGET_DELETED) {
|
||||
locked_widget = nullptr;
|
||||
// Re-enable the global keymapper
|
||||
g_system->getEventManager()->getKeymapper()->setEnabled(true);
|
||||
}
|
||||
if (focused_widget && focused_widget->Status() == WIDGET_DELETED)
|
||||
focused_widget = nullptr;
|
||||
|
||||
for (int i = 0; i < numwidgets;) {
|
||||
if (widgets[i]->Status() == WIDGET_DELETED) {
|
||||
delete widgets[i];
|
||||
|
||||
for (int j = i + 1; j < numwidgets; ++j) //shuffle remaining widgets down.
|
||||
widgets[j - 1] = widgets[j];
|
||||
|
||||
--numwidgets;
|
||||
if (redraw) {
|
||||
// CHECKME: is it really necessary to redraw after each deletion?
|
||||
force_full_redraw();
|
||||
Display();
|
||||
}
|
||||
} else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
bool GUI::moveWidget(GUI_Widget *widget, uint32 dx, uint32 dy) {
|
||||
if (!widget)
|
||||
return false;
|
||||
|
||||
widget->MoveRelative(dx, dy);
|
||||
|
||||
if (widget->Status() == WIDGET_VISIBLE)
|
||||
widget->Redraw();//force_full_redraw();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GUI::force_full_redraw() {
|
||||
full_redraw = true;
|
||||
}
|
||||
|
||||
void GUI::Display() {
|
||||
int i;
|
||||
bool complete_redraw = false;
|
||||
|
||||
// hack for now to make everyhing under the cursor draw until I find a better
|
||||
// way of doing this...
|
||||
if (dragging || full_redraw)
|
||||
complete_redraw = true;
|
||||
|
||||
for (i = 0; i < numwidgets; ++i) {
|
||||
if (widgets[i]->Status() == WIDGET_VISIBLE) {
|
||||
widgets[i]->Display(complete_redraw);
|
||||
//screen->update(widgets[i]->area.left,widgets[i]->area.top,widgets[i]->area.width(),widgets[i]->area.height());
|
||||
}
|
||||
}
|
||||
//SDL_UpdateRect(screen, 0, 0, 0, 0);
|
||||
|
||||
int mx, my;
|
||||
screen->get_mouse_location(&mx, &my);
|
||||
|
||||
gui_drag_manager->draw(mx, my);
|
||||
|
||||
if (full_redraw)
|
||||
full_redraw = false;
|
||||
}
|
||||
|
||||
/* Function to handle a GUI status */
|
||||
void
|
||||
GUI::HandleStatus(GUI_status status) {
|
||||
switch (status) {
|
||||
case GUI_QUIT:
|
||||
running = 0;
|
||||
break;
|
||||
case GUI_REDRAW:
|
||||
display = 1;
|
||||
break;
|
||||
case GUI_DRAG_AND_DROP:
|
||||
dragging = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle an event, passing it to widgets until they return a status */
|
||||
GUI_status GUI::HandleEvent(Common::Event *event) {
|
||||
int i;
|
||||
int hit;
|
||||
GUI_status status = GUI_PASS;
|
||||
|
||||
if (dragging) { //&& !block_input)
|
||||
if (event->type == Common::EVENT_LBUTTONUP ||
|
||||
event->type == Common::EVENT_MBUTTONUP ||
|
||||
event->type == Common::EVENT_RBUTTONUP) { //FIX for button up that doesn't hit a widget.
|
||||
for (hit = false, i = numwidgets - 1; (i >= 0) && (hit == false); --i) {
|
||||
if (widgets[i]->Status() == WIDGET_VISIBLE && widgets[i]->is_drop_target() && widgets[i]->HitRect(event->mouse.x, event->mouse.y)) {
|
||||
gui_drag_manager->drop((GUI_DragArea *)widgets[i], event->mouse.x, event->mouse.y);
|
||||
dragging = false;
|
||||
Display(); // redraw the widget to get rid of the drop graphic.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!block_input) {
|
||||
if (event->type == Common::EVENT_JOYAXIS_MOTION ||
|
||||
event->type == Common::EVENT_JOYBUTTON_DOWN ||
|
||||
event->type == Common::EVENT_JOYBUTTON_UP) {
|
||||
event->kbd.keycode = Game::get_game()->get_keybinder()->get_key_from_joy_events(event);
|
||||
if (event->kbd.keycode == Common::KEYCODE_INVALID) { // isn't mapped, is in deadzone, or axis didn't return to center before moving again
|
||||
HandleStatus(status);
|
||||
CleanupDeletedWidgets(status != GUI_QUIT);
|
||||
return status; // pretend nothing happened
|
||||
}
|
||||
event->type = Common::EVENT_KEYDOWN;
|
||||
event->kbd.flags = 0;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
/* SDL_QUIT events quit the GUI */
|
||||
// case SDL_QUIT:
|
||||
// status = GUI_QUIT;
|
||||
// break;
|
||||
|
||||
/* Keyboard and mouse events go to widgets */
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_MBUTTONDOWN:
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
case Common::EVENT_MBUTTONUP:
|
||||
case Common::EVENT_KEYDOWN:
|
||||
case Common::EVENT_KEYUP:
|
||||
case Common::EVENT_WHEELDOWN:
|
||||
case Common::EVENT_WHEELUP:
|
||||
// /* Go through widgets, topmost first */
|
||||
// status = GUI_PASS;
|
||||
// for (i=numwidgets-1; (i>=0)&&(status==GUI_PASS); --i) {
|
||||
// if ( widgets[i]->Status() == WIDGET_VISIBLE ) {
|
||||
// status = widgets[i]->HandleEvent(event);
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
/* Send everything to locked widget. */
|
||||
if (locked_widget && locked_widget->Status() == WIDGET_VISIBLE) {
|
||||
status = locked_widget->HandleEvent(event);
|
||||
if (status == GUI_PASS) // can't bypass the lock
|
||||
status = GUI_YUM;
|
||||
}
|
||||
/* Go through widgets, focused first, then from the
|
||||
top.*/
|
||||
else {
|
||||
status = GUI_PASS;
|
||||
if (focused_widget && focused_widget->Status() == WIDGET_VISIBLE) {
|
||||
status = focused_widget->HandleEvent(event);
|
||||
}
|
||||
for (i = numwidgets - 1; (i >= 0) && (status == GUI_PASS); --i) {
|
||||
if (widgets[i]->Status() == WIDGET_VISIBLE
|
||||
&& widgets[i] != focused_widget) { // don't send to focused twice
|
||||
status = widgets[i]->HandleEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Ignore unhandled events */
|
||||
default:
|
||||
status = GUI_PASS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HandleStatus(status);
|
||||
|
||||
CleanupDeletedWidgets(status != GUI_QUIT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Run the GUI.
|
||||
This returns when either a widget requests a quit, the idle
|
||||
function requests a quit, or the SDL window has been closed.
|
||||
*/
|
||||
void GUI::Run(GUI_IdleProc idle, int once, int multitaskfriendly) {
|
||||
int i;
|
||||
Common::Event event;
|
||||
|
||||
/* If there's nothing to do, return immediately */
|
||||
if ((numwidgets == 0) && (idle == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = 1;
|
||||
if (! once) {
|
||||
display = 1;
|
||||
}
|
||||
do {
|
||||
CleanupDeletedWidgets();
|
||||
|
||||
/* Display widgets if necessary */
|
||||
if (display) {
|
||||
Display();
|
||||
display = 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////// Polling is time consuming - instead:
|
||||
if (multitaskfriendly && (idle == nullptr)) {
|
||||
while (!Events::get()->pollEvent(event))
|
||||
g_system->delayMillis(5);
|
||||
HandleEvent(&event);
|
||||
} else
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/* Handle events, or run idle functions */
|
||||
if (Events::get()->pollEvent(event)) {
|
||||
/* Handle all pending events */
|
||||
do {
|
||||
HandleEvent(&event);
|
||||
} while (Events::get()->pollEvent(event));
|
||||
} else {
|
||||
if (idle != nullptr) {
|
||||
HandleStatus(idle());
|
||||
}
|
||||
for (i = numwidgets - 1; i >= 0; --i) {
|
||||
HandleStatus(widgets[i]->Idle());
|
||||
}
|
||||
}
|
||||
//ERIC SDL_Delay(10);
|
||||
} while (running && ! once);
|
||||
}
|
||||
|
||||
GUI_Font *GUI::get_font() {
|
||||
return gui_font;
|
||||
}
|
||||
|
||||
|
||||
// SB-X
|
||||
void GUI::Idle() {
|
||||
if (locked_widget) {
|
||||
locked_widget->Idle();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = numwidgets - 1; i >= 0; --i) {
|
||||
HandleStatus(widgets[i]->Idle());
|
||||
}
|
||||
}
|
||||
|
||||
bool GUI::set_focus(GUI_Widget *widget) {
|
||||
/*
|
||||
for(int i = 0; i < numwidgets; ++i)
|
||||
{
|
||||
if(!widget || (widgets[i] == widget)) // must be managed by GUI
|
||||
{
|
||||
*/
|
||||
focused_widget = widget;
|
||||
return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false;
|
||||
}
|
||||
|
||||
void GUI::lock_input(GUI_Widget *widget) {
|
||||
for (int i = 0; i < numwidgets; ++i) {
|
||||
if (!widget || (widgets[i] == widget)) {// must be managed by GUI
|
||||
locked_widget = widget;
|
||||
// Disable the keymapper so direct keys can go into the input
|
||||
g_system->getEventManager()->getKeymapper()->setEnabled(locked_widget == nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::Path GUI::get_data_dir() const {
|
||||
Std::string datadir;
|
||||
|
||||
config->value("config/datadir", datadir, "");
|
||||
|
||||
return Common::Path(datadir);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user