1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
/*
* Crossfire -- cooperative multi-player graphical RPG and adventure game
*
* Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
* Copyright (c) 1992 Frank Tore Johansen
*
* Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
* welcome to redistribute it under certain conditions. For details, please
* see COPYING and LICENSE.
*
* The authors can be reached via e-mail at <crossfire@metalforge.org>.
*/
/**
* @file
* The implementation of the Gate and Timed Gate classes of objects.
* @todo merge GATE and TIMED_GATE object types.
*/
#include "global.h"
#include <stdlib.h>
#include "ob_methods.h"
#include "ob_types.h"
#include "sounds.h"
#include "sproto.h"
static method_ret gate_type_process(ob_methods *context, object *op);
static method_ret timed_gate_type_process(ob_methods *context, object *op);
/**
* Initializer for the gate object type.
*/
void init_type_gate(void) {
register_process(GATE, gate_type_process);
register_process(TIMED_GATE, timed_gate_type_process);
}
/**
* Handle ob_process for all gate objects.
* @param context The method context
* @param op The gate that's being processed.
* @return METHOD_OK
*/
static method_ret gate_type_process(ob_methods *context, object *op) {
object *tmp, *part;
if (op->stats.wc < 0 || (int)op->stats.wc >= NUM_ANIMATIONS(op)) {
StringBuffer *sb;
char *diff;
LOG(llevError, "Gate error: animation was %d, max=%d, on %s (%d, %d)\n", op->stats.wc, NUM_ANIMATIONS(op), map_get_path(op), op->x, op->y);
sb = stringbuffer_new();
object_dump(op, sb);
diff = stringbuffer_finish(sb);
LOG(llevError, "%s\n", diff);
free(diff);
op->stats.wc = 0;
}
/* We're going down */
if (op->value) {
if (--op->stats.wc <= 0) { /* Reached bottom, let's stop */
op->stats.wc = 0;
if (op->arch->clone.speed)
op->value = 0;
else {
op->speed = 0;
object_update_speed(op);
}
}
for (part = op; part != NULL; part = part->more) {
if ((int)op->stats.wc < (NUM_ANIMATIONS(op)/2+1)) {
part->move_block = 0;
CLEAR_FLAG(part, FLAG_BLOCKSVIEW);
update_all_los(part->map, part->x, part->y);
}
SET_ANIMATION(part, op->stats.wc);
}
object_update(op, UP_OBJ_CHANGE);
play_sound_map(SOUND_TYPE_GROUND, op, 0, "gate moving");
return METHOD_OK;
}
/* We're going up */
/* First, lets see if we are already at the top */
if ((unsigned char)op->stats.wc == (NUM_ANIMATIONS(op)-1)) {
/* Check to make sure that only non pickable and non rollable
* objects are above the gate. If so, we finish closing the gate,
* otherwise, we fall through to the code below which should lower
* the gate slightly.
*/
for (tmp = op->above; tmp != NULL; tmp = tmp->above)
if (!QUERY_FLAG(tmp, FLAG_NO_PICK)
|| QUERY_FLAG(tmp, FLAG_CAN_ROLL)
|| QUERY_FLAG(tmp, FLAG_ALIVE))
break;
if (tmp == NULL) {
if (op->arch->clone.speed)
op->value = 1;
else {
op->speed = 0;
object_update_speed(op); /* Reached top, let's stop */
}
return METHOD_OK;
}
}
if (op->stats.food) { /* The gate is going temporarily down */
if (--op->stats.wc <= 0) { /* Gone all the way down? */
op->stats.food = 0; /* Then let's try again */
op->stats.wc = 0;
}
} else { /* The gate is still going up */
op->stats.wc++;
if ((int)op->stats.wc >= (NUM_ANIMATIONS(op)))
op->stats.wc = (signed char)NUM_ANIMATIONS(op)-1;
/* If there is something on top of the gate, we try to roll it off.
* If a player/monster, we don't roll, we just hit them with damage
*/
if ((int)op->stats.wc >= NUM_ANIMATIONS(op)/2) {
/* Halfway or further, check blocks */
/* First, get the top object on the square. */
for (tmp = op->above; tmp != NULL && tmp->above != NULL; tmp = tmp->above)
;
if (tmp != NULL) {
if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
hit_player(tmp, random_roll(1, op->stats.dam, tmp, PREFER_LOW), op, AT_PHYSICAL, 1);
if (tmp->type == PLAYER)
draw_ext_info_format(NDI_UNIQUE, 0, tmp, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT,
"You are crushed by the %s!",
op->name);
} else
/* If the object is not alive, and the object either can
* be picked up or the object rolls, move the object
* off the gate.
*/
if (!QUERY_FLAG(tmp, FLAG_ALIVE)
&& (!QUERY_FLAG(tmp, FLAG_NO_PICK) || QUERY_FLAG(tmp, FLAG_CAN_ROLL))) {
/* If it has speed, it should move itself, otherwise: */
int i = object_find_free_spot(tmp, op->map, op->x, op->y, 1, 9);
/* If there is a free spot, move the object someplace */
if (i != -1) {
object_remove(tmp);
object_insert_in_map_at(tmp, op->map, op, 0, op->x+freearr_x[i], op->y+freearr_y[i]);
}
}
}
/* See if there is still anything blocking the gate */
for (tmp = op->above; tmp != NULL; tmp = tmp->above)
if (!QUERY_FLAG(tmp, FLAG_NO_PICK)
|| QUERY_FLAG(tmp, FLAG_CAN_ROLL)
|| QUERY_FLAG(tmp, FLAG_ALIVE))
break;
/* IF there is, start putting the gate down */
if (tmp) {
op->stats.food = 1;
} else {
object* part;
for (part = op; part != NULL; part = part->more) {
if (!part->stats.luck)
part->move_block = MOVE_ALL;
if (!part->stats.ac) {
SET_FLAG(part, FLAG_BLOCKSVIEW);
update_all_los(part->map, part->x, part->y);
}
}
}
} /* gate is halfway up */
for (part = op; part != NULL; part = part->more) {
SET_ANIMATION(part, op->stats.wc);
}
object_update(op, UP_OBJ_CHANGE);
play_sound_map(SOUND_TYPE_GROUND, op, 0, "gate moving");
} /* gate is going up */
return METHOD_OK;
}
/**
* Handle ob_process for all timed gate objects.
* - hp : how long door is open/closed
* - maxhp : initial value for hp
* - sp : 1 = open, 0 = close
* @param context The method context
* @param op The timed gate that's being processed.
* @return METHOD_OK
* @todo Split function into more managable functions.
*/
static method_ret timed_gate_type_process(ob_methods *context, object *op) {
int v = op->value;
if (op->stats.sp) {
gate_type_process(context, op);
if (op->value != v) /* change direction ? */
op->stats.sp = 0;
return METHOD_OK;
}
if (--op->stats.hp <= 0) { /* keep gate down */
gate_type_process(context, op);
if (op->value != v) { /* ready ? */
op->speed = 0;
object_update_speed(op);
}
}
return METHOD_OK;
}
|