dxx-rebirth/similar/main/switch.cpp

928 lines
25 KiB
C++
Raw Normal View History

2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* New Triggers and Switches.
*
*/
#include <stdexcept>
2006-03-20 17:12:09 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "gauges.h"
#include "newmenu.h"
#include "game.h"
#include "switch.h"
#include "inferno.h"
#include "segment.h"
#include "dxxerror.h"
2006-03-20 17:12:09 +00:00
#include "gameseg.h"
#include "wall.h"
#include "texmap.h"
2014-11-23 04:36:58 +00:00
#include "object.h"
2006-03-20 17:12:09 +00:00
#include "fuelcen.h"
#include "cntrlcen.h"
#include "newdemo.h"
#include "player.h"
#include "endlevel.h"
#include "gameseq.h"
#include "multi.h"
#include "palette.h"
2015-04-19 04:18:51 +00:00
#include "hudmsg.h"
2006-03-20 17:12:09 +00:00
#include "robot.h"
#include "bm.h"
#ifdef EDITOR
#include "editor/editor.h"
#endif
2014-01-12 23:00:43 +00:00
#include "physfs-serial.h"
2014-12-08 04:19:26 +00:00
#include "compiler-range_for.h"
#include "partial_range.h"
2014-01-12 23:00:43 +00:00
2006-03-20 17:12:09 +00:00
#ifdef EDITOR
//-----------------------------------------------------------------
// Initializes all the switches.
void trigger_init()
{
2016-02-06 22:12:53 +00:00
Triggers.set_count(0);
2006-03-20 17:12:09 +00:00
}
#endif
2016-01-09 16:38:11 +00:00
template <typename T1, typename T2>
static inline void trigger_wall_op(const trigger &t, const T1 &segment_factory, const T2 &op)
{
for (unsigned i = 0, num_links = t.num_links; i != num_links; ++i)
op(segment_factory(t.seg[i]), t.side[i]);
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Executes a link, attached to a trigger.
// Toggles all walls linked to the switch.
// Opens doors, Blasts blast walls, turns off illusions.
2016-01-09 16:38:11 +00:00
static void do_link(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
trigger_wall_op(t, vsegptridx, wall_toggle);
2006-03-20 17:12:09 +00:00
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//close a door
2016-01-09 16:38:11 +00:00
static void do_close_door(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
trigger_wall_op(t, vsegptridx, wall_close_door);
2006-03-20 17:12:09 +00:00
}
//turns lighting on. returns true if lights were actually turned on. (they
//would not be if they had previously been shot out).
2016-01-09 16:38:11 +00:00
static int do_light_on(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
int ret=0;
2016-01-09 16:38:11 +00:00
const auto op = [&ret](const vsegptridx_t segnum, const unsigned sidenum) {
2006-03-20 17:12:09 +00:00
//check if tmap2 casts light before turning the light on. This
//is to keep us from turning on blown-out lights
if (TmapInfo[segnum->sides[sidenum].tmap_num2 & 0x3fff].lighting) {
2006-03-20 17:12:09 +00:00
ret |= add_light(segnum, sidenum); //any light sets flag
enable_flicker(segnum, sidenum);
}
2016-01-09 16:38:11 +00:00
};
trigger_wall_op(t, vsegptridx, op);
2006-03-20 17:12:09 +00:00
return ret;
}
//turns lighting off. returns true if lights were actually turned off. (they
//would not be if they had previously been shot out).
2016-01-09 16:38:11 +00:00
static int do_light_off(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
int ret=0;
2016-01-09 16:38:11 +00:00
const auto op = [&ret](const vsegptridx_t segnum, const unsigned sidenum) {
2006-03-20 17:12:09 +00:00
//check if tmap2 casts light before turning the light off. This
//is to keep us from turning off blown-out lights
if (TmapInfo[segnum->sides[sidenum].tmap_num2 & 0x3fff].lighting) {
2006-03-20 17:12:09 +00:00
ret |= subtract_light(segnum, sidenum); //any light sets flag
disable_flicker(segnum, sidenum);
}
2016-01-09 16:38:11 +00:00
};
trigger_wall_op(t, vsegptridx, op);
2006-03-20 17:12:09 +00:00
return ret;
}
// Unlocks all doors linked to the switch.
2016-01-09 16:38:11 +00:00
static void do_unlock_doors(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
const auto op = [](const vsegptr_t segp, const unsigned sidenum) {
const auto wall_num = segp->sides[sidenum].wall_num;
2016-01-09 16:38:11 +00:00
auto &w = Walls[wall_num];
w.flags &= ~WALL_DOOR_LOCKED;
w.keys = KEY_NONE;
2016-01-09 16:38:11 +00:00
};
trigger_wall_op(t, vsegptr, op);
2006-03-20 17:12:09 +00:00
}
// Locks all doors linked to the switch.
2016-01-09 16:38:11 +00:00
static void do_lock_doors(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
const auto op = [](const vsegptr_t segp, const unsigned sidenum) {
const auto wall_num = segp->sides[sidenum].wall_num;
2016-01-09 16:38:11 +00:00
auto &w = Walls[wall_num];
w.flags |= WALL_DOOR_LOCKED;
2016-01-09 16:38:11 +00:00
};
trigger_wall_op(t, vsegptr, op);
2006-03-20 17:12:09 +00:00
}
// Changes walls pointed to by a trigger. returns true if any walls changed
static int do_change_walls(const trigger &t, const uint8_t new_wall_type)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
int ret=0;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:11 +00:00
for (unsigned i = 0; i < t.num_links; ++i)
2015-04-26 20:15:50 +00:00
{
2016-01-09 16:38:11 +00:00
uint8_t cside;
const auto &&segp = vsegptridx(t.seg[i]);
const auto side = t.side[i];
2014-10-02 03:02:34 +00:00
segptridx_t csegp = segment_none;
2006-03-20 17:12:09 +00:00
2015-04-26 20:15:50 +00:00
if (!IS_CHILD(segp->children[side]))
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
cside = side_none;
2006-03-20 17:12:09 +00:00
}
else
{
2014-10-02 03:02:34 +00:00
csegp = segptridx(segp->children[side]);
2006-03-20 17:12:09 +00:00
cside = find_connect_side(segp, csegp);
Assert(cside != side_none);
2006-03-20 17:12:09 +00:00
}
2016-01-09 16:38:11 +00:00
auto &wall0 = Walls[segp->sides[side].wall_num];
wall *wall1 = nullptr;
if (wall0.type == new_wall_type &&
(cside != side_none || csegp->sides[cside].wall_num == wall_none ||
(wall1 = &Walls[csegp->sides[cside].wall_num])->type == new_wall_type))
2006-03-20 17:12:09 +00:00
continue; //already in correct state, so skip
ret |= 1;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:11 +00:00
switch (t.type)
{
2006-03-20 17:12:09 +00:00
case TT_OPEN_WALL:
if ((TmapInfo[segp->sides[side].tmap_num].flags & TMI_FORCE_FIELD)) {
ret |= 2;
const auto pos = compute_center_point_on_side(segp, side );
2014-10-02 03:02:34 +00:00
digi_link_sound_to_pos( SOUND_FORCEFIELD_OFF, segp, side, pos, 0, F1_0 );
digi_kill_sound_linked_to_segment(segp,side,SOUND_FORCEFIELD_HUM);
2016-01-09 16:38:11 +00:00
wall0.type = new_wall_type;
if (wall1)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
wall1->type = new_wall_type;
2014-10-02 03:02:34 +00:00
digi_kill_sound_linked_to_segment(csegp, cside, SOUND_FORCEFIELD_HUM);
2006-03-20 17:12:09 +00:00
}
}
else
start_wall_cloak(segp,side);
break;
case TT_CLOSE_WALL:
if ((TmapInfo[segp->sides[side].tmap_num].flags & TMI_FORCE_FIELD)) {
ret |= 2;
{
const auto pos = compute_center_point_on_side(segp, side );
2014-10-02 03:02:34 +00:00
digi_link_sound_to_pos(SOUND_FORCEFIELD_HUM,segp,side,pos,1, F1_0/2);
}
case TT_ILLUSORY_WALL:
2016-01-09 16:38:11 +00:00
wall0.type = new_wall_type;
if (wall1)
wall1->type = new_wall_type;
2006-03-20 17:12:09 +00:00
}
else
start_wall_decloak(segp,side);
break;
default:
return 0;
2006-03-20 17:12:09 +00:00
}
kill_stuck_objects(segp->sides[side].wall_num);
2016-01-09 16:38:11 +00:00
if (wall1)
2006-03-20 17:12:09 +00:00
kill_stuck_objects(csegp->sides[cside].wall_num);
}
return ret;
}
#define print_trigger_message(pnum,trig,shot,message) \
2016-01-09 16:38:11 +00:00
((void)((print_trigger_message(pnum,trig,shot)) && \
HUD_init_message(HM_DEFAULT, message, &"s"[trig.num_links <= 1])))
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:11 +00:00
static int (print_trigger_message)(int pnum, const trigger &t, int shot)
{
2016-01-09 16:38:11 +00:00
if (shot && pnum == Player_num && !(t.flags & TF_NO_MESSAGE))
return 1;
return 0;
2006-03-20 17:12:09 +00:00
}
#endif
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:11 +00:00
static void do_matcen(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
range_for (const auto &i, partial_range(t.seg, t.num_links))
trigger_matcen(vsegptridx(i));
2006-03-20 17:12:09 +00:00
}
2016-01-09 16:38:11 +00:00
static void do_il_on(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
trigger_wall_op(t, vsegptridx, wall_illusion_on);
2006-03-20 17:12:09 +00:00
}
2016-01-09 16:38:11 +00:00
static void do_il_off(const trigger &t)
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:11 +00:00
#if defined(DXX_BUILD_DESCENT_I)
auto &op = wall_illusion_off;
#elif defined(DXX_BUILD_DESCENT_II)
const auto op = [](const vsegptridx_t &seg, unsigned side) {
wall_illusion_off(seg, side);
const auto &&cp = compute_center_point_on_side(seg, side);
digi_link_sound_to_pos(SOUND_WALL_REMOVED, seg, side, cp, 0, F1_0);
};
#endif
2016-01-09 16:38:11 +00:00
trigger_wall_op(t, vsegptridx, op);
2006-03-20 17:12:09 +00:00
}
int check_trigger_sub(const trgnum_t trigger_num, int pnum,int shot)
2006-03-20 17:12:09 +00:00
{
if (pnum < 0 || pnum > MAX_PLAYERS)
return 1;
if ((Game_mode & GM_MULTI) && (Players[pnum].connected != CONNECT_PLAYING)) // as a host we may want to handle triggers for our clients. to do that properly we must check wether we (host) or client is actually playing.
return 1;
auto &trigger = *vtrgptr(trigger_num);
#if defined(DXX_BUILD_DESCENT_I)
(void)shot;
if (pnum == Player_num) {
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_SHIELD_DAMAGE) {
get_local_player_shields() -= trigger.value;
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_EXIT) {
start_endlevel_sequence();
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_SECRET_EXIT) {
if (Newdemo_state == ND_STATE_RECORDING) // stop demo recording
Newdemo_state = ND_STATE_PAUSED;
if (Game_mode & GM_MULTI)
multi_send_endlevel_start(1);
if (Game_mode & GM_NETWORK)
multi_do_protocol_frame(1, 1);
PlayerFinishedLevel(1); //1 means go to secret level
Control_center_destroyed = 0;
return 1;
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_ENERGY_DRAIN) {
get_local_player_energy() -= trigger.value;
}
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_CONTROL_DOORS) {
do_link(trigger);
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_MATCEN) {
if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS))
2016-01-09 16:38:11 +00:00
do_matcen(trigger);
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_ILLUSION_ON) {
do_il_on(trigger);
}
2016-01-09 16:38:11 +00:00
if (trigger.flags & TRIGGER_ILLUSION_OFF) {
do_il_off(trigger);
}
#elif defined(DXX_BUILD_DESCENT_II)
2016-01-09 16:38:11 +00:00
if (trigger.flags & TF_DISABLED)
2006-03-20 17:12:09 +00:00
return 1; //1 means don't send trigger hit to other players
2016-01-09 16:38:11 +00:00
if (trigger.flags & TF_ONE_SHOT) //if this is a one-shot...
trigger.flags |= TF_DISABLED; //..then don't let it happen again
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:11 +00:00
switch (trigger.type)
{
2006-03-20 17:12:09 +00:00
case TT_EXIT:
if (pnum!=Player_num)
break;
if (!EMULATING_D1)
digi_stop_digi_sounds(); //Sound shouldn't cut out when exiting a D1 lvl
2006-03-20 17:12:09 +00:00
if (Current_level_num > 0) {
start_endlevel_sequence();
} else if (Current_level_num < 0) {
if (get_local_player_shields() < 0 ||
Player_dead_state != player_dead_state::no)
2006-03-20 17:12:09 +00:00
break;
// NMN 04/09/07 Do endlevel movie if we are
// playing a D1 secret level
if (EMULATING_D1)
{
start_endlevel_sequence();
} else {
ExitSecretLevel();
}
2006-03-20 17:12:09 +00:00
return 1;
} else {
#ifdef EDITOR
nm_messagebox_str( "Yo!", "You have hit the exit trigger!", "" );
2006-03-20 17:12:09 +00:00
#else
Int3(); //level num == 0, but no editor!
#endif
}
return 1;
break;
case TT_SECRET_EXIT: {
int truth;
if (pnum!=Player_num)
break;
if (get_local_player_shields() < 0 ||
Player_dead_state != player_dead_state::no)
2006-03-20 17:12:09 +00:00
break;
if (is_SHAREWARE || is_MAC_SHARE) {
HUD_init_message_literal(HM_DEFAULT, "Secret Level Teleporter disabled in Descent 2 Demo");
digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
break;
}
2006-03-20 17:12:09 +00:00
if (Game_mode & GM_MULTI) {
HUD_init_message_literal(HM_DEFAULT, "Secret Level Teleporter disabled in multiplayer!");
2006-03-20 17:12:09 +00:00
digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
break;
}
truth = p_secret_level_destroyed();
if (Newdemo_state == ND_STATE_RECORDING) // record whether we're really going to the secret level
newdemo_record_secret_exit_blown(truth);
if ((Newdemo_state != ND_STATE_PLAYBACK) && truth) {
HUD_init_message_literal(HM_DEFAULT, "Secret Level destroyed. Exit disabled.");
2006-03-20 17:12:09 +00:00
digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
break;
}
if (Newdemo_state == ND_STATE_RECORDING) // stop demo recording
Newdemo_state = ND_STATE_PAUSED;
digi_stop_digi_sounds();
2006-03-20 17:12:09 +00:00
EnterSecretLevel();
Control_center_destroyed = 0;
return 1;
break;
}
case TT_OPEN_DOOR:
2016-01-09 16:38:11 +00:00
do_link(trigger);
print_trigger_message(pnum, trigger, shot, "Door%s opened!");
2006-03-20 17:12:09 +00:00
break;
case TT_CLOSE_DOOR:
2016-01-09 16:38:11 +00:00
do_close_door(trigger);
print_trigger_message(pnum, trigger, shot, "Door%s closed!");
2006-03-20 17:12:09 +00:00
break;
case TT_UNLOCK_DOOR:
2016-01-09 16:38:11 +00:00
do_unlock_doors(trigger);
print_trigger_message(pnum, trigger, shot, "Door%s unlocked!");
2006-03-20 17:12:09 +00:00
break;
2006-03-20 17:12:09 +00:00
case TT_LOCK_DOOR:
2016-01-09 16:38:11 +00:00
do_lock_doors(trigger);
print_trigger_message(pnum, trigger, shot, "Door%s locked!");
2006-03-20 17:12:09 +00:00
break;
2006-03-20 17:12:09 +00:00
case TT_OPEN_WALL:
if (const auto w = do_change_walls(trigger, WALL_OPEN))
2016-01-09 16:38:11 +00:00
print_trigger_message(pnum, trigger, shot, (w & 2) ? "Force field%s deactivated!" : "Wall%s opened!");
2006-03-20 17:12:09 +00:00
break;
case TT_CLOSE_WALL:
if (const auto w = do_change_walls(trigger, WALL_CLOSED))
2016-01-09 16:38:11 +00:00
print_trigger_message(pnum, trigger, shot, (w & 2) ? "Force field%s activated!" : "Wall%s closed!");
2006-03-20 17:12:09 +00:00
break;
case TT_ILLUSORY_WALL:
//don't know what to say, so say nothing
do_change_walls(trigger, WALL_ILLUSION);
2006-03-20 17:12:09 +00:00
break;
case TT_MATCEN:
if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS))
2016-01-09 16:38:11 +00:00
do_matcen(trigger);
2006-03-20 17:12:09 +00:00
break;
2006-03-20 17:12:09 +00:00
case TT_ILLUSION_ON:
2016-01-09 16:38:11 +00:00
do_il_on(trigger);
print_trigger_message(pnum, trigger, shot, "Illusion%s on!");
2006-03-20 17:12:09 +00:00
break;
2006-03-20 17:12:09 +00:00
case TT_ILLUSION_OFF:
2016-01-09 16:38:11 +00:00
do_il_off(trigger);
print_trigger_message(pnum, trigger, shot, "Illusion%s off!");
2006-03-20 17:12:09 +00:00
break;
case TT_LIGHT_OFF:
2016-01-09 16:38:11 +00:00
if (do_light_off(trigger))
print_trigger_message(pnum, trigger, shot, "Light%s off!");
2006-03-20 17:12:09 +00:00
break;
case TT_LIGHT_ON:
2016-01-09 16:38:11 +00:00
if (do_light_on(trigger))
print_trigger_message(pnum, trigger, shot, "Light%s on!");
2006-03-20 17:12:09 +00:00
break;
default:
Int3();
break;
}
#endif
2006-03-20 17:12:09 +00:00
return 0;
}
//-----------------------------------------------------------------
// Checks for a trigger whenever an object hits a trigger side.
2015-07-12 01:04:22 +00:00
void check_trigger(const vcsegptridx_t seg, short side, const vcobjptridx_t objnum, int shot)
2006-03-20 17:12:09 +00:00
{
if ((Game_mode & GM_MULTI) && (get_local_player().connected != CONNECT_PLAYING)) // as a host we may want to handle triggers for our clients. so this function may be called when we are not playing.
return;
#if defined(DXX_BUILD_DESCENT_I)
if (objnum == get_local_player().objnum)
#elif defined(DXX_BUILD_DESCENT_II)
if (objnum == get_local_player().objnum || (objnum->type == OBJ_ROBOT && Robot_info[get_robot_id(objnum)].companion))
#endif
{
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_I)
if ( Newdemo_state == ND_STATE_PLAYBACK )
return;
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Newdemo_state == ND_STATE_RECORDING )
2014-10-02 03:02:34 +00:00
newdemo_record_trigger( seg, side, objnum,shot);
#endif
2006-03-20 17:12:09 +00:00
2014-09-21 22:11:51 +00:00
auto wall_num = seg->sides[side].wall_num;
if ( wall_num == wall_none ) return;
2015-04-26 20:15:50 +00:00
auto trigger_num = Walls[wall_num].trigger;
if (trigger_num == trigger_none)
2006-03-20 17:12:09 +00:00
return;
if (check_trigger_sub(trigger_num, Player_num,shot))
return;
#if defined(DXX_BUILD_DESCENT_I)
auto &t = *vtrgptr(trigger_num);
if (t.flags & TRIGGER_ONE_SHOT)
{
t.flags &= ~TRIGGER_ON;
2015-07-12 01:04:21 +00:00
const auto &&csegp = vcsegptr(seg->children[side]);
auto cside = find_connect_side(seg, csegp);
Assert(cside != side_none);
wall_num = csegp->sides[cside].wall_num;
2014-09-21 22:11:51 +00:00
if ( wall_num == wall_none ) return;
auto ctrigger_num = Walls[wall_num].trigger;
const auto &&ct = vtrgptr(ctrigger_num);
ct->flags &= ~TRIGGER_ON;
}
#endif
2006-03-20 17:12:09 +00:00
if (Game_mode & GM_MULTI)
multi_send_trigger(trigger_num);
}
}
void triggers_frame_process()
{
2014-12-08 04:19:26 +00:00
range_for (auto &i, partial_range(Triggers, Num_triggers))
if (i.time >= 0)
i.time -= FrameTime;
2006-03-20 17:12:09 +00:00
}
/*
* reads a v29_trigger structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
#if defined(DXX_BUILD_DESCENT_I)
2014-02-09 05:56:44 +00:00
void v26_trigger_read(PHYSFS_File *fp, trigger &t)
{
int type;
switch ((type = PHYSFSX_readByte(fp)))
{
case TT_OPEN_DOOR: // door
t.flags = TRIGGER_CONTROL_DOORS;
break;
case TT_MATCEN: // matcen
t.flags = TRIGGER_MATCEN;
break;
case TT_EXIT: // exit
t.flags = TRIGGER_EXIT;
break;
case TT_SECRET_EXIT: // secret exit
t.flags = TRIGGER_SECRET_EXIT;
break;
case TT_ILLUSION_OFF: // illusion off
t.flags = TRIGGER_ILLUSION_OFF;
break;
case TT_ILLUSION_ON: // illusion on
t.flags = TRIGGER_ILLUSION_ON;
break;
default:
con_printf(CON_URGENT,"Warning: unsupported trigger type %d", type);
throw std::runtime_error("unsupported trigger type");
}
2016-02-06 22:12:54 +00:00
if (PHYSFSX_readByte(fp) & 2) // one shot
t.flags |= TRIGGER_ONE_SHOT;
2014-02-09 05:56:44 +00:00
t.num_links = PHYSFSX_readShort(fp);
t.value = PHYSFSX_readInt(fp);
t.time = PHYSFSX_readInt(fp);
for (unsigned i=0; i < MAX_WALLS_PER_LINK; i++ )
t.seg[i] = PHYSFSX_readShort(fp);
for (unsigned i=0; i < MAX_WALLS_PER_LINK; i++ )
t.side[i] = PHYSFSX_readShort(fp);
}
void v25_trigger_read(PHYSFS_File *fp, trigger *t)
#elif defined(DXX_BUILD_DESCENT_II)
extern void v29_trigger_read(v29_trigger *t, PHYSFS_File *fp)
#endif
2006-03-20 17:12:09 +00:00
{
int i;
#if defined(DXX_BUILD_DESCENT_I)
PHYSFSX_readByte(fp);
#elif defined(DXX_BUILD_DESCENT_II)
t->type = PHYSFSX_readByte(fp);
#endif
t->flags = PHYSFSX_readShort(fp);
t->value = PHYSFSX_readFix(fp);
t->time = PHYSFSX_readFix(fp);
t->link_num = PHYSFSX_readByte(fp);
t->num_links = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
for (i=0; i<MAX_WALLS_PER_LINK; i++ )
t->seg[i] = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
for (i=0; i<MAX_WALLS_PER_LINK; i++ )
t->side[i] = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
/*
* reads a v30_trigger structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
extern void v30_trigger_read(v30_trigger *t, PHYSFS_File *fp)
2006-03-20 17:12:09 +00:00
{
int i;
t->flags = PHYSFSX_readShort(fp);
t->num_links = PHYSFSX_readByte(fp);
t->pad = PHYSFSX_readByte(fp);
t->value = PHYSFSX_readFix(fp);
t->time = PHYSFSX_readFix(fp);
2006-03-20 17:12:09 +00:00
for (i=0; i<MAX_WALLS_PER_LINK; i++ )
t->seg[i] = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
for (i=0; i<MAX_WALLS_PER_LINK; i++ )
t->side[i] = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
}
/*
* reads a trigger structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
extern void trigger_read(trigger *t, PHYSFS_File *fp)
2006-03-20 17:12:09 +00:00
{
int i;
t->type = PHYSFSX_readByte(fp);
t->flags = PHYSFSX_readByte(fp);
t->num_links = PHYSFSX_readByte(fp);
2014-01-12 23:00:43 +00:00
PHYSFSX_readByte(fp);
t->value = PHYSFSX_readFix(fp);
t->time = PHYSFSX_readFix(fp);
2006-03-20 17:12:09 +00:00
for (i=0; i<MAX_WALLS_PER_LINK; i++ )
t->seg[i] = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
for (i=0; i<MAX_WALLS_PER_LINK; i++ )
t->side[i] = PHYSFSX_readShort(fp);
2006-03-20 17:12:09 +00:00
}
2014-02-09 05:45:27 +00:00
static ubyte trigger_type_from_flags(short flags)
{
if (flags & TRIGGER_CONTROL_DOORS)
return TT_OPEN_DOOR;
2015-11-08 18:55:51 +00:00
else if (flags & (TRIGGER_SHIELD_DAMAGE | TRIGGER_ENERGY_DRAIN))
{
}
2014-02-09 05:45:27 +00:00
else if (flags & TRIGGER_EXIT)
return TT_EXIT;
else if (flags & TRIGGER_MATCEN)
return TT_MATCEN;
else if (flags & TRIGGER_ILLUSION_OFF)
return TT_ILLUSION_OFF;
else if (flags & TRIGGER_SECRET_EXIT)
return TT_SECRET_EXIT;
else if (flags & TRIGGER_ILLUSION_ON)
return TT_ILLUSION_ON;
else if (flags & TRIGGER_UNLOCK_DOORS)
return TT_UNLOCK_DOOR;
else if (flags & TRIGGER_OPEN_WALL)
return TT_OPEN_WALL;
else if (flags & TRIGGER_CLOSE_WALL)
return TT_CLOSE_WALL;
else if (flags & TRIGGER_ILLUSORY_WALL)
return TT_ILLUSORY_WALL;
2015-11-08 18:55:51 +00:00
throw std::runtime_error("unsupported trigger type");
2014-02-09 05:45:27 +00:00
}
static void v30_trigger_to_v31_trigger(trigger &t, const v30_trigger &trig)
{
t.type = trigger_type_from_flags(trig.flags & ~TRIGGER_ON);
t.flags = (trig.flags & TRIGGER_ONE_SHOT) ? TF_ONE_SHOT : 0;
t.num_links = trig.num_links;
t.num_links = trig.num_links;
t.value = trig.value;
t.time = trig.time;
t.seg = trig.seg;
t.side = trig.side;
}
static void v29_trigger_read_as_v30(PHYSFS_File *fp, v30_trigger &trig)
{
v29_trigger trig29;
v29_trigger_read(&trig29, fp);
trig.flags = trig29.flags;
// skip trig29.link_num. v30_trigger does not need it
trig.num_links = trig29.num_links;
trig.value = trig29.value;
trig.time = trig29.time;
trig.seg = trig29.seg;
trig.side = trig29.side;
}
void v29_trigger_read_as_v31(PHYSFS_File *fp, trigger &t)
{
v30_trigger trig;
v29_trigger_read_as_v30(fp, trig);
v30_trigger_to_v31_trigger(t, trig);
}
void v30_trigger_read_as_v31(PHYSFS_File *fp, trigger &t)
{
v30_trigger trig;
v30_trigger_read(&trig, fp);
v30_trigger_to_v31_trigger(t, trig);
}
#endif
2006-03-20 17:12:09 +00:00
2016-02-06 22:12:54 +00:00
namespace dsx {
#if defined(DXX_BUILD_DESCENT_I)
2014-01-12 23:00:43 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE(trigger, t, (serial::pad<1>(), t.flags, t.value, t.time, t.link_num, t.num_links, t.seg, t.side));
ASSERT_SERIAL_UDT_MESSAGE_SIZE(trigger, 54);
#elif defined(DXX_BUILD_DESCENT_II)
DEFINE_SERIAL_UDT_TO_MESSAGE(trigger, t, (t.type, t.flags, t.num_links, serial::pad<1>(), t.value, t.time, t.seg, t.side));
ASSERT_SERIAL_UDT_MESSAGE_SIZE(trigger, 52);
#endif
2016-02-06 22:12:54 +00:00
}
/*
* reads n trigger structs from a PHYSFS_File and swaps if specified
*/
void trigger_read(PHYSFS_File *fp, trigger &t)
{
2014-01-12 23:00:43 +00:00
PHYSFSX_serialize_read(fp, t);
}
void trigger_write(PHYSFS_File *fp, const trigger &t)
2014-01-12 23:00:43 +00:00
{
PHYSFSX_serialize_write(fp, t);
}
void v29_trigger_write(PHYSFS_File *fp, const trigger &rt)
2006-03-20 17:12:09 +00:00
{
const trigger *t = &rt;
PHYSFSX_writeU8(fp, 0); // unused 'type'
#if defined(DXX_BUILD_DESCENT_I)
PHYSFS_writeSLE16(fp, t->flags);
#elif defined(DXX_BUILD_DESCENT_II)
switch (t->type)
{
case TT_OPEN_DOOR:
PHYSFS_writeSLE16(fp, TRIGGER_CONTROL_DOORS | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_EXIT:
PHYSFS_writeSLE16(fp, TRIGGER_EXIT | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_MATCEN:
PHYSFS_writeSLE16(fp, TRIGGER_MATCEN | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_ILLUSION_OFF:
PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_OFF | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_SECRET_EXIT:
PHYSFS_writeSLE16(fp, TRIGGER_SECRET_EXIT | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_ILLUSION_ON:
PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_ON | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_UNLOCK_DOOR:
PHYSFS_writeSLE16(fp, TRIGGER_UNLOCK_DOORS | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_OPEN_WALL:
PHYSFS_writeSLE16(fp, TRIGGER_OPEN_WALL | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_CLOSE_WALL:
PHYSFS_writeSLE16(fp, TRIGGER_CLOSE_WALL | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_ILLUSORY_WALL:
PHYSFS_writeSLE16(fp, TRIGGER_ILLUSORY_WALL | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
default:
Int3();
PHYSFS_writeSLE16(fp, 0);
break;
}
#endif
PHYSFSX_writeFix(fp, t->value);
PHYSFSX_writeFix(fp, t->time);
#if defined(DXX_BUILD_DESCENT_I)
PHYSFSX_writeU8(fp, t->link_num);
#elif defined(DXX_BUILD_DESCENT_II)
PHYSFSX_writeU8(fp, -1); //t->link_num
#endif
PHYSFS_writeSLE16(fp, t->num_links);
2006-03-20 17:12:09 +00:00
for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
PHYSFS_writeSLE16(fp, t->seg[i]);
for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
PHYSFS_writeSLE16(fp, t->side[i]);
}
void v30_trigger_write(PHYSFS_File *fp, const trigger &rt)
{
const trigger *t = &rt;
#if defined(DXX_BUILD_DESCENT_I)
if (t->flags & TRIGGER_CONTROL_DOORS)
PHYSFSX_writeU8(fp, TT_OPEN_DOOR); // door
else if (t->flags & TRIGGER_MATCEN)
PHYSFSX_writeU8(fp, TT_MATCEN); // matcen
else if (t->flags & TRIGGER_EXIT)
PHYSFSX_writeU8(fp, TT_EXIT); // exit
else if (t->flags & TRIGGER_SECRET_EXIT)
PHYSFSX_writeU8(fp, TT_SECRET_EXIT); // secret exit
else if (t->flags & TRIGGER_ILLUSION_OFF)
PHYSFSX_writeU8(fp, TT_ILLUSION_OFF); // illusion off
else if (t->flags & TRIGGER_ILLUSION_ON)
PHYSFSX_writeU8(fp, TT_ILLUSION_ON); // illusion on
#elif defined(DXX_BUILD_DESCENT_II)
PHYSFSX_writeU8(fp, t->type);
#endif
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_I)
PHYSFS_writeSLE16(fp, t->flags);
#elif defined(DXX_BUILD_DESCENT_II)
switch (t->type)
{
case TT_OPEN_DOOR:
PHYSFS_writeSLE16(fp, TRIGGER_CONTROL_DOORS | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
2006-03-20 17:12:09 +00:00
case TT_EXIT:
PHYSFS_writeSLE16(fp, TRIGGER_EXIT | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_MATCEN:
PHYSFS_writeSLE16(fp, TRIGGER_MATCEN | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_ILLUSION_OFF:
PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_OFF | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_SECRET_EXIT:
PHYSFS_writeSLE16(fp, TRIGGER_SECRET_EXIT | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_ILLUSION_ON:
PHYSFS_writeSLE16(fp, TRIGGER_ILLUSION_ON | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_UNLOCK_DOOR:
PHYSFS_writeSLE16(fp, TRIGGER_UNLOCK_DOORS | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_OPEN_WALL:
PHYSFS_writeSLE16(fp, TRIGGER_OPEN_WALL | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_CLOSE_WALL:
PHYSFS_writeSLE16(fp, TRIGGER_CLOSE_WALL | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
case TT_ILLUSORY_WALL:
PHYSFS_writeSLE16(fp, TRIGGER_ILLUSORY_WALL | ((t->flags & TF_ONE_SHOT) ? TRIGGER_ONE_SHOT : 0));
break;
default:
Int3();
PHYSFS_writeSLE16(fp, 0);
break;
}
#endif
2006-03-20 17:12:09 +00:00
PHYSFSX_writeU8(fp, t->num_links);
PHYSFSX_writeU8(fp, 0); // t->pad
2006-03-20 17:12:09 +00:00
PHYSFSX_writeFix(fp, t->value);
PHYSFSX_writeFix(fp, t->time);
for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
PHYSFS_writeSLE16(fp, t->seg[i]);
for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
PHYSFS_writeSLE16(fp, t->side[i]);
}
void v31_trigger_write(PHYSFS_File *fp, const trigger &rt)
{
const trigger *t = &rt;
#if defined(DXX_BUILD_DESCENT_I)
if (t->flags & TRIGGER_CONTROL_DOORS)
PHYSFSX_writeU8(fp, TT_OPEN_DOOR); // door
else if (t->flags & TRIGGER_MATCEN)
PHYSFSX_writeU8(fp, TT_MATCEN); // matcen
else if (t->flags & TRIGGER_EXIT)
PHYSFSX_writeU8(fp, TT_EXIT); // exit
else if (t->flags & TRIGGER_SECRET_EXIT)
PHYSFSX_writeU8(fp, TT_SECRET_EXIT); // secret exit
else if (t->flags & TRIGGER_ILLUSION_OFF)
PHYSFSX_writeU8(fp, TT_ILLUSION_OFF); // illusion off
else if (t->flags & TRIGGER_ILLUSION_ON)
PHYSFSX_writeU8(fp, TT_ILLUSION_ON); // illusion on
#elif defined(DXX_BUILD_DESCENT_II)
PHYSFSX_writeU8(fp, t->type);
#endif
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_I)
PHYSFSX_writeU8(fp, (t->flags & TRIGGER_ONE_SHOT) ? 2 : 0); // flags
#elif defined(DXX_BUILD_DESCENT_II)
PHYSFSX_writeU8(fp, t->flags);
#endif
PHYSFSX_writeU8(fp, t->num_links);
PHYSFSX_writeU8(fp, 0); // t->pad
PHYSFSX_writeFix(fp, t->value);
PHYSFSX_writeFix(fp, t->time);
for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
2006-03-20 17:12:09 +00:00
PHYSFS_writeSLE16(fp, t->seg[i]);
for (unsigned i = 0; i < MAX_WALLS_PER_LINK; i++)
2006-03-20 17:12:09 +00:00
PHYSFS_writeSLE16(fp, t->side[i]);
}