- The Spells are ready now!

This commit is contained in:
cade 2000-06-25 22:07:55 +00:00
parent 1b7e613b54
commit be03bc5d50
30 changed files with 1455 additions and 532 deletions

View file

@ -1,6 +1,6 @@
## ___________ _________ _____ __
## \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \ __\ __\
## | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
## \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
## \/ \/ \/ \/ \/
@ -8,244 +8,71 @@
## T H E W A R B E G I N S
## FreeCraft - A free fantasy real time strategy game engine
##
## Rules.make - Make RULES (GNU MAKE).
##
## (c) Copyright 1998-2000 by Lutz Sammer
##
## $Id$
##
############################################################################
# Configurable:
# Choose what you want to include and the correct
# version. Minimal is now the default.
############################################################################
# Compile commands
CC=gcc
RM=rm -f
MAKE=make
# Uncomment next to get guile with gtk (and choose your gtklib version)
#GUILEGTK = -DGUILE_GTK $(shell gtk-config --cflags)
#GUILEGTK = -DGUILE_GTK -I/usr/X11R6/include -I/usr/lib/glib/include
#GUILEGTKLIB = -lguilegtk-1.0 $(shell gtk-config --libs)
#GUILEGTKLIB = -lguilegtk-1.2 $(shell gtk-config --libs)
#GUILEGTKLIB = -L/opt/gnome/lib -lguilegtk-1.1 \
$(shell /opt/gnome/bin/gtk-config --libs)
#------------------------------------------------------------------------------
# Uncomment next for a version with guile (GNU scheme interpreter)
# (and choose your guile version)
# guile 1.2 old version
# If you have unreferenced "gh_vector_ref" define LIBGUILE12
#GUILE = -DLIBGUILE12 -DUSE_CCL $(GUILEGTK)
#GUILELIB = $(GUILEGTKLIB) -lguile -ldl
# guile 1.3 latest version
# -lreadline -lncurses are needed with the distribution SuSe 5.3
#GUILE_CFLAGS = $(shell guile-config compile)
#GUILE = -DUSE_CCL $(GUILEGTK) $(GUILE_CFLAGS)
#GUILELIB = $(GUILEGTKLIB) -lguile -lqthreads -ldl -lm
#GUILELIB = $(GUILEGTKLIB) $(shell guile-config link) -lreadline -lncurses
#GUILELIB = $(GUILEGTKLIB) $(shell guile-config link)
# guile
#CCL = $(GUILE)
#CCLLIB = $(GUILELIB)
#------------------------------------------------------------------------------
# Comment next for a version without SIOD (scheme interpreter)
#
# Try to use siod (currently default)
CCL = -DUSE_CCL2
CCLLIB = -lm
#------------------------------------------------------------------------------
# Uncomment next to add threaded sound support
# You should have a thread safe X11 (libc6 or glibc)
#THREAD = -D_REENTRANT -DUSE_THREAD
#THREADLIB = -lpthread
#------------------------------------------------------------------------------
# Choose correct version of glib (needed for gtk)
# Where do you have installed your glib?
#GLIB = -DUSE_GLIB -I/usr/lib/glib/include/
#GLIBLIB = -lglib
# Should work with >= 1.2
#GLIB_CFLAGS = $(shell glib-config glib --cflags)
#GLIBLIB = $(shell glib-config glib --libs)
#GLIB = -DUSE_GLIB $(GLIB_CFLAGS)
#------------------------------------------------------------------------------
# Video driver part
#------------------------------------------------------------------------------
# Uncomment the next for the normal X11 support.
# Use SIOD support
CCL = -DUSE_CCL2
CCLLIB = -lm -ldl
# Video support
VIDEO = -DUSE_X11
VIDEOLIB = -lXext -lX11 -ldl
# Uncomment the next to get the support for SDL.
# Old SDL <1.0.0
#SDL_CFLAGS =
#SDLLIB = -lSDL -ldl -lpthread
# New SDL >=1.0.0
SDL_CFLAGS = $(shell sdl-config --cflags)
#SDLLIB = $(shell sdl-config --static-libs)
SDLLIB = $(shell sdl-config --libs)
# Without SDL Sound
#SDL = -DUSE_SDL $(SDL_CFLAGS)
# With SDL Sound
SDL = -DUSE_SDL -DUSE_SDLA $(SDL_CFLAGS)
# Uncomment the next for the SDL X11/SVGALIB support.
VIDEO = $(SDL)
VIDEOLIB = $(SDLLIB) -lXext -lX11 -lXxf86dga -lXxf86vm -lvga -lvgagl -ldl -lesd -lm
# Choose next to get svgalib support.
#VIDEO = -DUSE_SVGALIB
#VIDEOLIB = -lvga -lm -ldl
# Uncomment the next for the win32/cygwin support.
#VIDEO = -DUSE_WIN32 $(SDL)
#VIDEOLIB = $(SDLLIB)
# Uncomment the next for the win32/mingw32 support.
#VIDEO = -DUSE_WIN32 $(SDL)
#VIDEOLIB = $(SDLLIB) -lwsock32 -Wl,--stack,33554432
#------------------------------------------------------------------------------
# Sound driver part
#------------------------------------------------------------------------------
# Comment next if you want to remove sound support.
VIDEOLIB = -lXext -lX11 -ldl
# Sound support
DSOUND = -DWITH_SOUND
#------------------------------------------------------------------------------
# Choose which compress you like
# The win32 port didn't support BZ2LIB
# None
#ZDEFS =
#ZLIBS =
# GZ compression
ZDEFS = -DUSE_ZLIB
ZLIBS = -lz
# BZ2 compression
#ZDEFS = -DUSE_BZ2LIB
#ZLIBS = -lbz2
# GZ + BZ2 compression
#ZDEFS = -DUSE_ZLIB -DUSE_BZ2LIB
#ZLIBS = -lz -lbz2
#------------------------------------------------------------------------------
# May be required on some distributions for libpng and libz!
# extra linker flags and include directory
# -L/usr/lib
# Compression support
ZDEFS = -DUSE_ZLIB -DUSE_BZ2LIB
ZLIBS = -lz -lbz2
XLDFLAGS = -L/usr/X11R6/lib -L/usr/local/lib \
-L$(TOPDIR)/libpng-1.0.5 -L$(TOPDIR)/zlib-1.1.3
XIFLAGS = -I/usr/X11R6/include -I/usr/local/include \
-I$(TOPDIR)/libpng-1.0.5 -I$(TOPDIR)/zlib-1.1.3
#------------------------------------------------------------------------------
# Uncomment next to profile
#PROFILE= -pg
#####################################################################
# Don't change anything below here unless you know what you're doing!
# Version
VERSION= '-DVERSION="1.17pre1-build7"'
PROFILE=
############################################################################
# below this, nothing should be changed!
# Libraries needed to build tools
TOOLLIBS=$(XLDFLAGS) -lpng -lz -lm $(THREADLIB)
# Libraries needed to build freecraft
CLONELIBS=$(XLDFLAGS) -lpng -lz -lm \
$(THREADLIB) $(CCLLIB) $(GLIBLIB) $(VIDEOLIB) $(ZLIBS)
$(THREADLIB) $(CCLLIB) $(VIDEOLIB) $(ZLIBS)
DISTLIST=$(TOPDIR)/distlist
TAGS=$(TOPDIR)/src/tags
# LINUX
# Linux
EXE=
OUTFILE=$(TOPDIR)/freecraft
ARCH=linux
OE=o
EXE=
# WIN32
#OUTFILE=$(TOPDIR)/freecraft$(EXE)
#ARCH=win32
#OE=o
#EXE=.exe
## architecture-dependant objects
#ARCHOBJS=stdmman.$(OE) svgalib.$(OE) unix_lib.$(OE) bitm_lnx.$(OE)
## include flags
IFLAGS= -I$(TOPDIR)/src/include $(XIFLAGS)
## define flags
DEBUG= -DDEBUG #-DNEW_AI # -DFLAG_DEBUG
DFLAGS= $(THREAD) $(CCL) $(VERSION) $(GLIB) $(VIDEO) $(ZDEFS) $(DSOUND) \
$(DEBUG) -DUNIT_ON_MAP # -DNEW_NAMES
## choose optimise level
#CFLAGS=-g -O0 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS)
#CFLAGS=-g -O1 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS)
#CFLAGS=-g -O2 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS)
CFLAGS=-g -O3 $(PROFILE) -pipe -Wall -Werror $(IFLAGS) $(DFLAGS)
#CFLAGS=-g -O3 $(PROFILE) -pipe -Wall $(IFLAGS) $(DFLAGS)
#CFLAGS=-g -O6 -pipe -fconserve-space -fexpensive-optimizations -ffast-math $(IFLAGS) $(DFLAGS)
#-- Production
#CFLAGS=-O6 -pipe -fomit-frame-pointer -fconserve-space -fexpensive-optimizations -ffast-math $(IFLAGS) $(DFLAGS)
CFLAGS=-O6 -pipe -fomit-frame-pointer -fconserve-space -fexpensive-optimizations -ffast-math $(IFLAGS) $(DFLAGS) -static
CC=gcc
RM=rm -f
MAKE=make
## JOHNS: my ctags didn't support
#CTAGSFLAGS=-i defmpstuvFS -a -f
DFLAGS= $(THREAD) $(CCL) $(VERSION) \
$(VIDEO) $(ZDEFS) $(DSOUND) \
$(DEBUG)
CFLAGS=-g $(IFLAGS) $(DFLAGS) -DSLOW_INPUT -DUNIT_ON_MAP
CTAGSFLAGS=-i defptvS -a -f
#
# Locks versions with symbolic name
#
# Locks versions with a symbolic name
LOCKVER= rcs -q -n$(NAME)
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
@ar cru $(TOPDIR)/src/libclone.a $@
#------------
# Source code documentation
#
# Source code documentation
DOXYGEN= doxygen
DOCIFY= docify
DOCPP= doc++
# Still didn't work
#DOCIFY= /root/doc++-3.4.2/src/docify
#DOCPP= /root/doc++-3.4.2/src/doc++
%.doc: %.c
@$(TOPDIR)/tools/aledoc $< | $(DOCIFY) > $*-c.doc 2>/dev/null
%.doc: %.h
@$(TOPDIR)/tools/aledoc $< | $(DOCIFY) > $*-h.doc 2>/dev/null

View file

@ -1,6 +1,6 @@
## ___________ _________ _____ __
## \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
## | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
## | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
## \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
## \/ \/ \/ \/ \/
@ -27,6 +27,7 @@ OBJS = command.$(OE) actions.$(OE) action_die.$(OE) action_patrol.$(OE) \
action_board.$(OE) action_unload.$(OE) action_repair.$(OE) \
action_harvest.$(OE) action_minegold.$(OE) action_hauloil.$(OE) \
action_returngoods.$(OE) action_research.$(OE) action_demolish.$(OE) \
action_build.$(OE) action_train.$(OE) action_upgradeto.$(OE)
action_build.$(OE) action_train.$(OE) action_upgradeto.$(OE) \
action_spellcast.$(OE)
include $(TOPDIR)/Common.mk

View file

@ -73,6 +73,7 @@ local void DoActionAttackGeneric(Unit* unit,const Animation* attack)
if( flags&AnimationMissile ) { // time to fire projectil
FireMissile(unit);
unit->Invisible = 0; // unit is invisible untill attacks
}
}

View file

@ -130,7 +130,8 @@ global void HandleActionDemolish(Unit* unit)
n=SelectUnits(x-2,y-2, x+2, y+2,table);
// FIXME: Don't hit flying units!
for( i=0; i<n; ++i ) {
HitUnit(table[i],DEMOLISH_DAMAGE);
if ( table[i]->Type->LandUnit )
HitUnit(table[i],DEMOLISH_DAMAGE);
}
//

View file

@ -46,6 +46,14 @@
*/
global int HandleActionDie(Unit* unit)
{
if ( unit->Revealer )
{
unit->HP--;
if( unit->HP == 0 )
ReleaseUnit(unit);
return 0;
}
//
// Show death animation
//

View file

@ -94,6 +94,10 @@ global int UnitShowAnimation(Unit* unit,const Animation* animation)
*/
local void HandleUnitAction(Unit* unit)
{
if ( unit->Revealer )
{
unit->Command.Action = UnitActionDie;
}
//
// If current action is breakable proceed with next one.
//
@ -237,6 +241,10 @@ local void HandleUnitAction(Unit* unit)
HandleActionDemolish(unit);
break;
case UnitActionSpellCast:
HandleActionSpellCast(unit);
break;
default:
DebugLevel1Fn("Unknown action %d\n",unit->Command.Action);
break;

View file

@ -33,6 +33,7 @@
#include "map.h"
#include "upgrade.h"
#include "pathfinder.h"
#include "spells.h"
/*----------------------------------------------------------------------------
-- Functions
@ -829,4 +830,66 @@ global void CommandDemolish(Unit* unit,int x,int y,Unit* dest,int flush)
unit->SavedCommand.Action=UnitActionStill; // clear saved action
}
/**
** Demolish at position
**
** @param unit pointer to unit.
** @param x X map position to spell cast on.
** @param y Y map position to spell cast on.
** @param dest Spell cast on unit (if exist).
** @param spellid Spell type id.
** @param flush if true, flush command queue.
*/
global void CommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid,int flush)
{
Command* command;
const SpellType* spell;
IfDebug(
if( x<0 || y<0 || x>=TheMap.Width || y>=TheMap.Height ) {
DebugLevel0("Internal movement error\n");
return;
}
if( unit->Type->Vanishes ) {
DebugLevel0("Internal error\n");
abort();
}
);
DebugLevel3(__FUNCTION__": %Zd spell-casts on %Zd\n"
,UnitNumber(unit),dest ? UnitNumber(dest) : 0);
if( unit->Type->Building ) {
// FIXME: should find a better way for pending commands.
command=&unit->PendCommand;
} else if( !(command=GetNextCommand(unit,flush)) ) {
return;
}
spell = SpellTypeById( spellid );
command->Action=UnitActionSpellCast;
ResetPath(*command);
if (dest)
dest->Refs++;
command->Data.Move.Goal=dest;
command->Data.Move.Range=spell->Range;
command->Data.Move.SX=unit->X;
command->Data.Move.SY=unit->Y;
command->Data.Move.DX=x;
command->Data.Move.DY=y;
command->Data.Move.SpellId = spellid;
/*
command->Data.Spell.Goal=dest;
command->Data.Spell.Range=spell->Range;
command->Data.Spell.DX=x;
command->Data.Spell.DY=y;
*/
unit->SavedCommand.Action=UnitActionStill; // clear saved action
}
//@}

View file

@ -88,6 +88,8 @@ extern void CommandCancelResearch(Unit* unit);
//extern void CommandUpgradeUnit(Unit* unit,int what,int flush);
/// Prepare command demolish
extern void CommandDemolish(Unit* unit,int x,int y,Unit* dest,int flush);
/// Prepare command spellcast
extern void CommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid, int flush);
/*----------------------------------------------------------------------------
-- Actions: in action_<name>.c
@ -135,6 +137,8 @@ extern void HandleActionUpgrade(Unit* unit);
extern void HandleActionResearch(Unit* unit);
/// Handle command demolish
extern void HandleActionDemolish(Unit* unit);
/// Handle command spellcast
extern void HandleActionSpellCast(Unit* unit);
/*----------------------------------------------------------------------------
-- Actions: actions.c

View file

@ -82,6 +82,7 @@ extern CursorType Cursors[CursorMax]; /// cursor types description
extern enum CursorState_e CursorState; /// cursor state
extern int CursorAction; /// action for selection
extern int CursorValue; /// value for CursorAction (spell type f.e.)
extern UnitType* CursorBuilding; /// building cursor
extern CursorType* GameCursor; /// cursor type

View file

@ -47,7 +47,7 @@ enum _button_cmd_ {
B_AttackGround, /// order attack ground
B_Return, /// order return goods
B_Demolish, /// order demolish/explode
B_Magic, /// order cast spell
B_SpellCast, /// order cast spell
B_Research, /// order reseach
B_UpgradeTo, /// order upgrade
B_Unload, /// order unload unit

View file

@ -69,7 +69,7 @@ struct _missile_type_ {
};
/// how many missile type are maximal supported
#define MissileTypeMax 0x1E
#define MissileTypeMax 0x22
/// mark a free missile slot
#define MissileFree (MissileType*)0
@ -96,11 +96,18 @@ typedef struct _missile_ {
UnitStats* SourceStats; /// stats of unit that fires
Player* SourcePlayer; /// player of unit that fires
Unit* TargetUnit; /// target unit, used for spells
int Damage; /// direct damage that missile applies
int D; /// for point to point missiles
int Dx; /// delta x
int Dy; /// delta y
int Xstep; /// X step
int Ystep; /// Y step
int TTL; /// time to live (ticks) used for spells
int (*Controller)( void* this_missile ); /// used to controll spells
} Missile;
/*----------------------------------------------------------------------------

View file

@ -105,7 +105,8 @@ extern void SendCommandResearch(Unit* unit,Upgrade* what,int flush);
extern void SendCommandCancelResearch(Unit* unit);
/// Send demolish command
extern void SendCommandDemolish(Unit* unit,int x,int y,Unit* dest,int flush);
/// Send spell cast command
extern void SendCommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid,int flush);
//@}
#endif // !__NETWORK_H__

112
src/include/spells.h Normal file
View file

@ -0,0 +1,112 @@
// ___________ _________ _____ __
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
// \/ \/ \/ \/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// FreeCraft - A free fantasy real time strategy game engine
//
/**@name spells.h - The Spells. */
/*
** (c) Copyright 1999,2000 by Vladi Belperchinov-Shabanski
**
** $Id$
*/
#ifndef __SPELLS_H__
#define __SPELLS_H__
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include <stdio.h>
#include "freecraft.h"
#include "sound_id.h"
#include "sound.h"
#include "unittype.h"
#include "unit.h"
/*----------------------------------------------------------------------------
-- Definitons
----------------------------------------------------------------------------*/
typedef enum _spell_action_type_ {
SpellActionNone,
// ---human paladins---
SpellActionHolyVision,
SpellActionHealing,
SpellActionExorcism,
// ---human mages---
SpellActionFireball,
SpellActionSlow,
SpellActionFlameShield,
SpellActionInvisibility,
SpellActionPolymorph,
SpellActionBlizzard,
// ---orc ogres---
SpellActionEyeOfKilrogg,
SpellActionBloodlust,
SpellActionRunes,
// ---orc death knights---
SpellActionDeathCoil,
SpellActionHaste,
SpellActionRaiseDead,
SpellActionWhirlwind,
SpellActionUnholyArmor,
SpellActionDeathAndDecay
} SpellActionType;
typedef struct _spell_type_ {
int Id; /// index in the table (set by InitSpells())
char Ident[64]; /// spell name (ident)
int Range; /// spell range
int ManaCost; /// required mana for each cast
int TTL; /// time to live (ticks)
SpellActionType Action; /// SpellAction*
char SoundIdent[64]; /// sound string id
SoundId SoundId; /// sound id
} SpellType;
/*----------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
/// init spell tables
extern global void InitSpells();
/// done spell tables
extern global void DoneSpells();
/// return spell id by ident string
extern global int SpellIdByIdent( const char* Ident );
/// return spell type by ident string
extern global const SpellType* SpellTypeByIdent( const char* Ident );
/// return spell type by ident string
extern global const SpellType* SpellTypeById( int Id );
/* /// returns != 0 if spell can be casted by this unit, enough mana?
extern global int CanCastSpell( Unit* unit, int SpellId );
*/
/// fire spell on target unit or place at x,y
extern global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y );
//@}
#endif // !__BUTTON_H__

View file

@ -1,6 +1,6 @@
// ___________ _________ _____ __
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
// \/ \/ \/ \/ \/
@ -62,6 +62,8 @@ enum _unit_action_ {
UnitActionAttackGround, /// unit attacks ground
UnitActionDie, /// unit dies
UnitActionSpellCast, /// unit casts spell
UnitActionTrain, /// building is training
UnitActionUpgradeTo, /// building is upgrading itself
// UnitActionUpgrade, /// building is researching upgrade
@ -130,6 +132,7 @@ struct _command_ {
unsigned SY; /// Source
unsigned DX;
unsigned DY; /// Destination
int SpellId; /// spell type id
} Move; /// move:
struct {
unsigned Fast : 1; /// Can fast move
@ -152,7 +155,7 @@ struct _command_ {
struct {
unsigned Ticks; /// Ticks to complete
unsigned Count; /// Units in training queue
// FIXME: cade: later we should train more units or automatic
// FIXME: vladi: later we should train more units or automatic
#define MAX_UNIT_TRAIN 6
UnitType* What[MAX_UNIT_TRAIN]; /// Unit trained
} Train; /// train:
@ -230,7 +233,7 @@ struct _unit_ {
UnitType* Type; /// pointer to unit type (peon,...)
Player* Player; /// owner of this unit
UnitStats* Stats; /// current unit stats
// DISPLAY:
char IX;
char IY; /// image displacement to map position
@ -252,10 +255,11 @@ struct _unit_ {
unsigned HP; /// hit points
unsigned Bloodlust; /// ticks bloodlust
unsigned Haste; /// ticks haste
unsigned Slow; /// ticks slow
unsigned Haste; /// ticks haste (disables slow)
unsigned Slow; /// ticks slow (disables haste)
unsigned Invisible; /// ticks invisible
unsigned Shield; /// ticks shield
unsigned FlameShield; /// ticks flame shield
unsigned UnholyArmor; /// ticks unholy armor
unsigned GroupId; /// unit belongs to this group id
@ -273,6 +277,10 @@ struct _unit_ {
** ,used for fancy buildings
*/
unsigned Rs : 8;
unsigned Revealer; // hack -- `revealer' is unit that
// has to keep FOW revealed for some
// time, this unit cannot be used in
// usual manner
#define MAX_UNITS_ONBOARD 6 /// max number of units in transporter
// FIXME: use the new next pointer

View file

@ -358,11 +358,13 @@ global void MapUpdateVisible(void)
#ifdef NEW_FOW
MapMarkSight(unit->Player,unit->X+unit->Type->TileWidth/2
,unit->Y+unit->Type->TileHeight/2
,unit->Stats->SightRange);
,unit->Stats->SightRange*(unit->Revealer == 0)
+ 12*(unit->Revealer != 0));
#else
MapMarkSight(unit->X+unit->Type->TileWidth/2
,unit->Y+unit->Type->TileHeight/2
,unit->Stats->SightRange);
,unit->Stats->SightRange*(unit->Revealer == 0)
+ 12*(unit->Revealer != 0));
#endif
}
}

View file

@ -113,6 +113,7 @@ enum _message_type_ {
MessageCommandResearch, /// unit command research
MessageCommandCancelResearch, /// unit command cancel research
MessageCommandDemolish, /// unit command demolish
MessageCommandSpellCast /// unit command spell cast
};
/**
@ -710,6 +711,30 @@ global void SendCommandDemolish(Unit* unit,int x,int y,Unit* attack,int flush)
}
}
/**
** Send command: Unit spell cast on position/unit.
**
** @param unit pointer to unit.
** @param x X map tile position where to cast spell.
** @param y Y map tile position where to cast spell.
** @param attack Cast spell on unit (if exist).
** @param spellid Spell type id.
** @param flush Flag flush all pending commands.
*/
global void SendCommandSpellCast(Unit* unit,int x,int y,Unit* dest,int spellid,int flush)
{
CommandLog("spell-cast",unit,flush,1,x,y,dest,NULL); //FIXME: vladi: spellid?
if( NetworkFildes==-1 ) {
CommandSpellCast(unit,x,y,dest,spellid,flush);
} else {
// FIXME: WARNING: vladi: this is weird, we should have and
// integer argument to all this or just few bytes buffer or
// something... I'll pass spell id's as pointers until...
UnitType* ut = (UnitType*)spellid;
NetworkSendCommand(MessageCommandSpellCast,unit,x,y,dest,ut,flush);
}
}
//@}
//----------------------------------------------------------------------------
@ -1718,6 +1743,12 @@ local void ParseNetworkCommand(const NetworkCommandQueue* ncq)
}
CommandDemolish(unit,x,y,dest,status);
break;
case MessageCommandSpellCast:
// FIXME: WARNING: vladi: currently network protocol
// cannot carry required information for spell cast
// it is required to handle dest unit and aditional
// integer value!
break;
}
}

View file

@ -24,6 +24,7 @@ MODULE = clone
OBJS = clone.$(OE) unit.$(OE) unit_draw.$(OE) unitcache.$(OE) \
unit_find.$(OE) missile.$(OE) construct.$(OE)\
player.$(OE) pud.$(OE) ccl.$(OE) interface.$(OE) iolib.$(OE) \
mainloop.$(OE) goal.$(OE) selection.$(OE) groups.$(OE)
mainloop.$(OE) goal.$(OE) selection.$(OE) groups.$(OE) \
spells.$(OE)
include $(TOPDIR)/Common.mk

View file

@ -318,7 +318,27 @@ local int InputKey(int key)
SpeedResearch=1; /// speed factor for researching
SetMessage( "NORMAL DEBUG SPEED" );
}
if (strcmp(Input, "make it so") == 0)
{
SpeedMine=10; /// speed factor for mine gold
SpeedGold=10; /// speed factor for getting gold
SpeedChop=10; /// speed factor for chop
SpeedWood=10; /// speed factor for getting wood
SpeedHaul=10; /// speed factor for haul oil
SpeedOil=10; /// speed factor for getting oil
SpeedBuild=10; /// speed factor for building
SpeedTrain=10; /// speed factor for training
SpeedUpgrade=10; /// speed factor for upgrading
SpeedResearch=10; /// speed factor for researching
ThisPlayer->Resources[GoldCost] += 32000;
ThisPlayer->Resources[WoodCost] += 32000;
ThisPlayer->Resources[OilCost] += 32000;
ThisPlayer->Resources[OreCost] += 32000;
ThisPlayer->Resources[StoneCost]+= 32000;
ThisPlayer->Resources[CoalCost] += 32000;
MustRedraw|=RedrawResources;
SetMessage( "SO!" );
} else
// FIXME: only to selected players
NetworkChatMessage(Input);
#if defined(USE_CCL) || defined(USE_CCL2)

View file

@ -95,6 +95,10 @@
** Missile don't move, than checks the source unit for HP.
*/
#define MissileClassFire 12
/**
** Missile is controlled completely by Controller() function. (custom)
*/
#define MissileClassCustom 13
/*----------------------------------------------------------------------------
-- Variables
@ -135,6 +139,9 @@ local const char* MissileTypeWcNames[] = {
"missile-cannon-tower-explosion",
"missile-daemon-fire",
"missile-green-cross",
"missile-blizzard-hit",
"missile-death-coil",
"missile-custom",
"missile-none",
};
@ -178,7 +185,7 @@ global MissileType MissileTypes[MissileTypeMax] = {
"fireball.png",
32,32,
{ "fireball hit" },
MissileClassFireball,
MissileClassPointToPoint,
1,
},
{ MissileTypeType,
@ -194,15 +201,16 @@ global MissileType MissileTypes[MissileTypeMax] = {
"blizzard.png",
32,32,
{ NULL },
MissileClassBlizzard,
MissileClassBlizzard,
1,
"missile-blizzard-hit", NULL
},
{ MissileTypeType,
"missile-death-and-decay",
"death and decay.png",
32,32,
{ NULL },
MissileClassDeathDecay,
MissileClassStayWithDelay,
1,
},
{ MissileTypeType,
@ -227,7 +235,7 @@ global MissileType MissileTypes[MissileTypeMax] = {
"heal effect.png",
48,48,
{ NULL },
MissileClassPointToPoint,
MissileClassStayWithDelay,
1,
},
{ MissileTypeType,
@ -244,7 +252,7 @@ global MissileType MissileTypes[MissileTypeMax] = {
16,16,
{ NULL },
MissileClassStayWithDelay,
1,
5,
},
{ MissileTypeType,
"missile-whirlwind",
@ -320,7 +328,7 @@ global MissileType MissileTypes[MissileTypeMax] = {
48,48,
{ NULL },
MissileClassFire,
8,
8,
},
{ MissileTypeType,
"missile-impact",
@ -395,6 +403,38 @@ global MissileType MissileTypes[MissileTypeMax] = {
MissileClassNone,
1,
},
{ MissileTypeType,
"missile-blizzard-hit",
"blizzard.png",
32,32,
{ NULL },
MissileClassStayWithDelay,
1,
},
{ MissileTypeType,
"missile-death-coil",
"touch of death.png",
32,32,
{ NULL },
MissileClassPointToPoint,
1,
},
{ MissileTypeType,
"missile-custom",
NULL,
32,32,
{ NULL },
MissileClassCustom,
1,
},
{ MissileTypeType,
"missile-none",
NULL,
32,32,
{ NULL },
MissileClassNone,
1,
},
};
/*
@ -409,7 +449,7 @@ local int NumMissiles; /// currently used missiles
local Missile Missiles[MAX_MISSILES]; /// all missiles on map
/// lookup table for missile names
local hashtable(MissileType*,61) MissileHash;
local hashtable(MissileType*,65) MissileHash;
/*----------------------------------------------------------------------------
-- Functions
@ -531,6 +571,11 @@ found:
missile->SourceStats=NULL;
missile->SourcePlayer=NULL;
missile->Damage = 0;
missile->TargetUnit = NULL;
missile->TTL = -1;
missile->Controller = NULL;
return missile;
}
@ -762,6 +807,9 @@ global void DrawMissiles(void)
if( missile->Type==MissileFree ) {
continue;
}
if( missile->Type->Class == MissileClassCustom ) {
continue; // custom missiles are handled by Controller() only
}
// Draw only visibile missiles
if (MissileVisible(missile)) {
x=missile->X-MapX*TileSizeX+TheUI.MapX;
@ -830,7 +878,15 @@ local int PointToPointMissile(Missile* missile)
}
// FIXME: could be better written
MissileNewHeadingFromXY(missile,dx*xstep,dy*ystep);
if( missile->Type->Class == MissileClassWhirlwind )
{
// must not call MissileNewHeading nor frame change
}
else
if( missile->Type->Class == MissileClassBlizzard )
missile->Frame = 0;
else
MissileNewHeadingFromXY(missile,dx*xstep,dy*ystep);
if( dy==0 ) { // horizontal line
if( dx==0 ) {
@ -943,7 +999,9 @@ global void MissileHit(const Missile* missile)
x=missile->X+missile->Type->Width/2;
y=missile->Y+missile->Type->Height/2;
if( missile->Type->ImpactMissile ) {
MakeMissile(missile->Type->ImpactMissile,x,y,0,0);
Missile* mis = MakeMissile(missile->Type->ImpactMissile,x,y,0,0);
mis->Damage = missile->Damage; // direct damage, spells mostly
mis->SourceUnit = missile->SourceUnit;
}
if( !missile->SourceType ) { // no target
return;
@ -961,19 +1019,27 @@ global void MissileHit(const Missile* missile)
DebugLevel3Fn("Missile on wall?\n");
// FIXME: don't use UnitTypeByIdent here, this is slow!
if( HumanWallOnMap(x,y) ) {
HitWall(x,y,CalculateDamageStats(missile->SourceStats,
UnitTypeByIdent("unit-human-wall")->Stats));
if ( missile->Damage )
HitWall(x,y,missile->Damage); // direct damage, spells mostly
else
HitWall(x,y,CalculateDamageStats(missile->SourceStats,
UnitTypeByIdent("unit-human-wall")->Stats));
} else {
HitWall(x,y,CalculateDamageStats(missile->SourceStats,
UnitTypeByIdent("unit-orc-wall")->Stats));
if ( missile->Damage )
HitWall(x,y,missile->Damage); // direct damage, spells mostly
else
HitWall(x,y,CalculateDamageStats(missile->SourceStats,
UnitTypeByIdent("unit-orc-wall")->Stats));
}
return;
}
DebugLevel3Fn("Oops nothing to hit (%d,%d)?\n",x,y);
return;
}
HitUnit(goal,CalculateDamage(missile->SourceStats,goal));
if ( missile->Damage )
HitUnit(goal,missile->Damage); // direct damage, spells mostly
else
HitUnit(goal,CalculateDamage(missile->SourceStats,goal));
}
/**
@ -992,6 +1058,25 @@ global void MissileActions(void)
if( missile->Wait-- ) {
continue;
}
if ( missile->TTL != -1 ) {
missile->TTL--; // overall time to live if specified
}
if ( missile->Controller ) {
missile->Controller( missile );
}
if ( missile->TTL == 0 ) {
missile->Type=MissileFree;
continue;
}
if ( missile->Type->Class == MissileClassCustom ) {
missile->Wait=missile->Type->Speed;
continue; // custom missiles are handled by Controller() only
}
if (MissileVisible(missile)) {
// check before movement
MustRedraw|=RedrawMap;
@ -1088,6 +1173,31 @@ global void MissileActions(void)
}
break;
case MissileClassBlizzard:
missile->Wait=missile->Type->Speed;
if( PointToPointMissile(missile) ) {
//
// Animate hit
//
missile->Frame+=4; // FIXME: frames pro row
if( (missile->Frame&127)
>=VideoGraphicFrames(missile->Type->Sprite) ) {
MissileHit(missile);
missile->Type=MissileFree;
}
}
break;
case MissileClassWhirlwind:
missile->Wait=missile->Type->Speed;
missile->Frame++;
if ( missile->Frame > 3 )
missile->Frame = 0;
//NOTE: vladi: whirlwind moves slowly, i.e. it stays
// 5 ticks at the same pixels...
if ( missile->TTL < 1 || missile->TTL % 10 == 0 )
PointToPointMissile(missile);
break;
case MissileClassStayWithDelay:
missile->Wait=missile->Type->Speed;
if( ++missile->Frame

View file

@ -89,6 +89,11 @@ global void ChangeSelectedUnits(Unit** units,int count)
*/
global int SelectUnit(Unit* unit)
{
if ( unit->Revealer )
{
return 0; // Revealers cannot be selected
}
if( NumSelected == MaxSelectable ) {
return 0;
}

793
src/stratagus/spells.cpp Normal file
View file

@ -0,0 +1,793 @@
// ___________ _________ _____ __
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
// \/ \/ \/ \/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// FreeCraft - A free fantasy real time strategy game engine
//
/**@name action_spellcast.c - The spell cast action. */
/*
** (c) Copyright 1998-2000 by Vladi Belperchinov-Shabanski
**
** $Id$
*/
/*
** And when we cast our final spell
** And we meet in our dreams
** A place that no one else can go
** Don't ever let your love die
** Don't ever go breaking this spell
*/
//@{
/*----------------------------------------------------------------------------
-- Notes
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include "spells.h"
#include "sound.h"
#include "missile.h"
#include "map.h"
#include "ui.h"
/*----------------------------------------------------------------------------
-- Definitons
----------------------------------------------------------------------------*/
#define FIREBALL_DAMAGE 20
#define WHIRLWIND_DAMAGE1 4 // the center of the whirlwind
#define WHIRLWIND_DAMAGE2 1 // the periphery of the whirlwind
#define BLIZZARD_DAMAGE 10
#define DEATHANDDECAY_DAMAGE 10
#define RUNE_DAMAGE 50
/*----------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------*/
/*
NOTE: vladi:
The point to have variable unosrted list of spell types and
dynamic id's and in the same time -- SpellAction id's is that
spell actions are hardcoded and cannot be changed at all.
On the other hand we can have different spell types as with
different range, cost and time to live (possibly and other
parameters as extensions)
*/
global SpellType SpellTypeTable[] = {
//TTL's below are in ticks: approx: 500=13sec, 1000=25sec, 2000=50sec
//id, ident, range, mana, ttl, spell action, sound ident, sound id
// ---human paladins---
{ 0, "spell-holy-vision", 1024, 70, -1, SpellActionHolyVision , "holy vision", NULL },
{ 0, "spell-healing", 4, 6, -1, SpellActionHealing , "healing", NULL },
{ 0, "spell-exorcism", 10, 4, -1, SpellActionExorcism , "exorcism", NULL },
// ---human mages--- ---human mages---
{ 0, "spell-fireball", 8, 100, 999, SpellActionFireball , "fireball", NULL },
{ 0, "spell-slow", 10, 50,1000, SpellActionSlow , "slow", NULL },
{ 0, "spell-flame-shield", 6, 80, 600, SpellActionFlameShield , "flame shield", NULL },
{ 0, "spell-invisibility", 6, 200,2000, SpellActionInvisibility , "invisibility", NULL },
{ 0, "spell-polymorph", 10, 200, -1, SpellActionPolymorph , "polymorph", NULL },
{ 0, "spell-blizzard", 12, 5, -1, SpellActionBlizzard , "blizzard", NULL },
// ---orc ogres--- ---orc ogres---
{ 0, "spell-eye-of-kilrogg",1024, 70, -1, SpellActionEyeOfKilrogg , "eye of kilrogg", NULL },
{ 0, "spell-bloodlust", 6, 50,1000, SpellActionBloodlust , "bloodlust", NULL },
{ 0, "spell-runes", 10, 50,2000, SpellActionRunes , "runes", NULL },
// ---orc death knights--- ---orc death knights-
{ 0, "spell-death-coil", 10, 100, -1, SpellActionDeathCoil , "death coil", NULL },
{ 0, "spell-haste", 6, 50,1000, SpellActionHaste , "haste", NULL },
{ 0, "spell-raise-dead", 6, 50, -1, SpellActionRaiseDead , "raise dead", NULL },
{ 0, "spell-whirlwind", 12, 100, 800, SpellActionWhirlwind , "whirlwind", NULL },
{ 0, "spell-unholy-armor", 6, 100, 500, SpellActionUnholyArmor , "unholy armor", NULL },
{ 0, "spell-death-and-decay", 12, 5, -1, SpellActionDeathAndDecay, "death and decay",NULL },
// ---eot marker--- ---eot marker---
{-1, "", 1, 1, -1, SpellActionNone , "", }
};
local int SpellTypeCount;
MissileType* missile_healing = NULL;
MissileType* missile_spell = NULL;
MissileType* missile_exorcism = NULL;
MissileType* missile_explosion = NULL;
MissileType* missile_rune = NULL;
/*----------------------------------------------------------------------------
-- Functions (Spells Controllers/Callbacks)
----------------------------------------------------------------------------*/
/*
** Missile controllers should return 0 to continue
** or 1 to cancel the spell/missile
*/
/*
** Fireball controller
*/
global int SpellFireballController( void* missile )
{
Unit* table[MAX_UNITS];
int i;
int n;
Missile* mis = (Missile*)missile;
//NOTE: vladi: TTL is used as counter for explosions
// first 6 counts there are no explosions, then on each second
// and at the target destination
if ( (mis->TTL < 993 && mis->TTL % 2 == 0) || (mis->X == mis->DX && mis->Y == mis->DY) ) // approx.
{
//+TileSize/2 to align gfx to baseline
int x = mis->X + TileSizeX/2;
int y = mis->Y + TileSizeY/2;
MakeMissile( missile_explosion, x, y, x, y );
x = x / TileSizeX;
y = y / TileSizeY;
//Effect of the explosion on units.
//NOTE: vladi: this is slightly different than original
// now it hits all units in range 1
n = SelectUnits(x-1,y-1, x+1, y+1,table);
for( i=0; i<n; ++i )
HitUnit(table[i],FIREBALL_DAMAGE);
}
return 0;
};
/*
** Death-Coil controller
*/
global int SpellDeathCoilController( void* missile )
{
Unit* table[MAX_UNITS];
int i;
int n;
Missile* mis = (Missile*)missile;
mis->SourceUnit->Refs--;
if ( mis->TargetUnit )
mis->TargetUnit->Refs--;
if ( mis->X == mis->DX && mis->Y == mis->DY )
{ // missile has reached target unit/spot
if ( !mis->SourceUnit->Destroyed )
{ // source unit still exists
if ( mis->TargetUnit && !mis->TargetUnit->Destroyed )
{ // target unit still exists
int hp = mis->TargetUnit->HP;
hp -= 50;
mis->SourceUnit->HP += 50;
if ( hp <= 0 )
{
mis->TargetUnit->HP = 0;
DestroyUnit( mis->TargetUnit );
}
else
mis->TargetUnit->HP = hp;
if ( mis->SourceUnit->HP > mis->SourceUnit->Stats->HitPoints )
mis->SourceUnit->HP = mis->SourceUnit->Stats->HitPoints;
}
else
{ // no target unit -- try enemies in range 5x5
int ec = 0; // enemy count
int x = mis->DX / TileSizeX;
int y = mis->DY / TileSizeY;
n = SelectUnits(x-2,y-2, x+2, y+2,table);
if ( n > 0 )
{
// calculate organic enemy count
for( i=0; i<n; ++i )
ec += ( IsEnemy(mis->SourceUnit->Player,table[i])
&& table[i]->Type->Organic != 0);
if ( ec > 0 )
{ // yes organic enemies found
for( i=0; i<n; ++i )
if ( IsEnemy(mis->SourceUnit->Player,table[i])
&& table[i]->Type->Organic != 0 )
{
// disperse dabage between them
int hp = table[i]->HP;
hp -= 50/ec; //NOTE: 1 is the minimal damage
if ( hp <= 0 )
{
table[i]->HP = 0;
DestroyUnit( table[i] ); // too much damage
}
else
table[i]->HP = hp;
}
mis->SourceUnit->HP += 50;
if ( mis->SourceUnit->HP > mis->SourceUnit->Stats->HitPoints )
mis->SourceUnit->HP = mis->SourceUnit->Stats->HitPoints;
}
}
}
}
}
return 0;
}
/*
** Whirlwind controller
*/
/*
FIXME: vladi: whirlwind is particulary bad! :)
we need slow smooth missile movement that we don't
have yet... should be fixed later
*/
global int SpellWhirlwindController( void* missile )
{
Unit* table[MAX_UNITS];
int i;
int n;
int x;
int y;
Missile* mis = (Missile*)missile;
x = mis->X / TileSizeX;
y = mis->Y / TileSizeY;
n = SelectUnitsOnTile( x, y, table);
for( i=0; i<n; ++i ) {
HitUnit(table[i],WHIRLWIND_DAMAGE1);
}
n = SelectUnits( x - 1, y - 1, x + 1, y + 1, table);
for( i=0; i<n; ++i ) {
HitUnit(table[i],WHIRLWIND_DAMAGE2);
}
//printf( "Whirlwind: %d, %d, TTL: %d\n", mis->X, mis->Y, mis->TTL );
if ( mis->TTL % 100 == 0 ) // changes direction every 3 seconds (approx.)
{ // missile has reached target unit/spot
int nx, ny;
do
{
// find new destination in the map
nx = x + rand() % 5 - 2;
ny = y + rand() % 5 - 2;
}
while( nx < 0 && ny < 0 && nx >= TheMap.Width && ny >= TheMap.Height );
mis->X = mis->DX;
mis->Y = mis->DY;
mis->DX = nx * TileSizeX + TileSizeX/2;
mis->DY = ny * TileSizeY + TileSizeY/2;
//printf( "Whirlwind new direction: %d, %d, TTL: %d\n", mis->X, mis->Y, mis->TTL );
}
return 0;
}
/*
** Runes controller
*/
global int SpellRunesController( void* missile )
{
Unit* table[MAX_UNITS];
int i;
int n;
int x;
int y;
Missile* mis = (Missile*)missile;
x = mis->X / TileSizeX;
y = mis->Y / TileSizeY;
n = SelectUnitsOnTile( x, y, table);
for( i=0; i<n; ++i ) {
if ( table[i]->Type->LandUnit )
HitUnit(table[i],RUNE_DAMAGE);
}
if ( mis->TTL % 100 == 0 || mis->TTL == 0 )
{ // show rune every 4 seconds (approx.)
MakeMissile( missile_rune, mis->X, mis->Y, mis->X, mis->Y );
}
return 0;
}
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
/*
** Spells constructor, inits spell id's and sounds
**
*/
global void InitSpells()
{
int z = 0;
while( SpellTypeTable[z].Id != -1 )
{
SpellTypeTable[z].Id = z;
//FIXME: vladi: this won't work 'cos sound init is called after InitSpells()
SpellTypeTable[z].SoundId = SoundIdForName(SpellTypeTable[z].SoundIdent);
if( SpellTypeTable[z].SoundId == NULL )
{
DebugLevel0Fn( "cannot get SoundId for `%s'\n", SpellTypeTable[z].SoundIdent ); //FIXME: vladi: some log level func instead of printf?
}
z++;
}
SpellTypeCount = z;
missile_healing = MissileTypeByIdent( "missile-heal-effect" );
missile_spell = MissileTypeByIdent( "missile-normal-spell" );
missile_exorcism = MissileTypeByIdent( "missile-exorcism" );
missile_explosion = MissileTypeByIdent( "missile-explosion" );
missile_rune = MissileTypeByIdent( "missile-rune" );
}
/*
** Spells destructor (currently does nothing)
**
*/
global void DoneSpells()
{
// nothing yet
}
/*
** Get spell id by ident
**
** @param Id Spell ident.
**
** @return spell id (index in spell type table)
*/
global int SpellIdByIdent( const char* Ident )
{
int z = 0;
while( SpellTypeTable[z].Id != -1 )
{
if ( strcmp( SpellTypeTable[z].Ident, Ident ) == 0 )
return z;
z++;
}
return -1;
}
/*
** Get spell type struct ptr by ident
**
** @param Id Spell ident.
**
** @return spell type struct ptr
*/
global const SpellType* SpellTypeByIdent( const char* Ident )
{
int z = SpellIdByIdent( Ident );
return z != -1 ? &(SpellTypeTable[z]) : NULL;
}
/*
** Get spell type struct ptr by id
**
** @param Id Spell id (index in the spell type table).
**
** @return spell type struct ptr
*/
global const SpellType* SpellTypeById( int Id )
{
DebugCheck( Id < 0 || Id >= SpellTypeCount );
if ( Id < 0 || Id >= SpellTypeCount ) return NULL;
return &(SpellTypeTable[ Id ]);
}
/*
** Check if unit can cast spell
**
** @param unit Unit that has to be checked.
** @param SpellId Spell id (index in the spell type table).
**
** @return 0 if unit cannot or 1 (=!0) if unit can cast this spell type
global int CanCastSpell( Unit* unit, int SpellId )
{
const SpellType* spell = SpellTypeById( SpellId );
DebugCheck( spell == NULL );
if ( !unit->Type->CanCastSpell ) return 0; // NOTE: this must not happen
if ( unit->Mana < spell->ManaCost ) return 0;
return 1;
}
*/
/*
** Spell cast!
**
** @param SpellId Spell id (index in the spell type table).
** @param unit Unit that casts the spell.
** @param target Target unit that spell is addressed to
** @param X X coord of target spot when/if target does not exist
** @param Y Y coord of target spot when/if target does not exist
**
** @return 0 if spell should/can continue or =! 0 to stop
**
*/
global int SpellCast( int SpellId, Unit* unit, Unit* target, int x, int y )
{
int repeat = 0;
const SpellType* spell = SpellTypeById( SpellId );
/*
this does not work when no target unit
DebugLevel0Fn("Spell cast: %d (%s), %s -> %s (%d,%d)",
SpellId, spell->Ident, unit->Type->Name, target->Type->Name, x, y );
printf("Spell cast: %d (%s), %s -> %s (%d,%d)\n",
SpellId, spell->Ident, unit->Type->Name, target->Type->Name, x, y );
*/
// the unit can collect mana during the move to target, so check is here...
#define PLAY_FIREWORKS(s) \
{ \
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
MakeMissile( s, x*TileSizeX+TileSizeX/2, \
y*TileSizeX+TileSizeX/2, \
x*TileSizeX+TileSizeX/2, \
y*TileSizeX+TileSizeX/2 ); \
}
switch( spell->Action )
{
case SpellActionNone:
DebugLevel0Fn( "No spell action" );
break;
// ---human paladins---
case SpellActionHolyVision:
unit->Mana -= spell->ManaCost; // get mana cost
{
Unit* u = MakeUnit(UnitTypeByIdent("unit-daemon"), unit->Player);
u->Revealer = 1;
u->HP = 2;
u->X = x;
u->Y = y;
}
break;
case SpellActionHealing:
// only can heal organic units
if (target && target->Type->Organic)
{
while( target->HP < target->Stats->HitPoints
&& unit->Mana > spell->ManaCost )
{
unit->Mana -= spell->ManaCost; // get mana cost
target->HP++;
}
PLAY_FIREWORKS(missile_healing);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionExorcism:
// exorcism works only on undead units
if ( target && target->Type->IsUndead )
{
while( target->HP > 0 && unit->Mana > spell->ManaCost )
{
unit->Mana -= spell->ManaCost; // get mana cost
target->HP--;
}
PLAY_FIREWORKS(missile_exorcism);
}
else
{
//FIXME: vladi: exorcism effect should be disperced on near units
}
DebugCheck( unit->Mana < 0 );
break;
// ---human mages---
case SpellActionFireball:
{ //NOTE: fireball can be casted on spot
Missile* mis;
int sx = unit->X;
int sy = unit->Y;
int dx = x;
int dy = y;
if ( target )
{
dx = target->X;
dy = target->Y;
}
unit->Mana -= spell->ManaCost;
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
mis = MakeMissile( MissileTypeByIdent("missile-fireball"),
sx*TileSizeX+TileSizeX/2,
sy*TileSizeX+TileSizeX/2,
dx*TileSizeX+TileSizeX/2,
dy*TileSizeX+TileSizeX/2 );
mis->TTL = spell->TTL;
mis->Controller = SpellFireballController;
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionSlow:
if (target && !target->Type->Building && target->Slow < spell->TTL)
{
// get mana cost
unit->Mana -= spell->ManaCost;
target->Slow = spell->TTL; // about 25 sec
target->Haste = 0;
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionFlameShield:
if ( target && target->Type->Organic && target->Type->LandUnit
&& target->FlameShield < spell->TTL )
{
// get mana cost
unit->Mana -= spell->ManaCost;
target->FlameShield = spell->TTL; // about 15 sec
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionInvisibility:
if (target && target->Type->Organic && target->Invisible < spell->TTL )
{
// get mana cost
unit->Mana -= spell->ManaCost;
target->Invisible = spell->TTL; // about 50 sec
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionPolymorph:
if ( target && target->Type->Organic )
{
int x = target->X;
int y = target->Y;
Player* pl = target->Player;
// as said somewhere else -- no corpses :)
RemoveUnit( target );
UnitLost( target );
ReleaseUnit( target );
MakeUnitAndPlace( x, y, UnitTypeByIdent("unit-critter"), pl );
unit->Mana -= spell->ManaCost;
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionBlizzard:
{
/*
NOTE: vladi: blizzard differs than original in this way:
original: launches 50 shards at 5 random spots x 10 for 25 mana
here: launches 10 shards at 10 random spots x 1 for 5 mana
reason: it cannot be done w/o aditional spells list... perhasp
could be managed with fake spell with controller, but
for now it is leaved as it is...
*/
int shards = 10;
while( shards-- )
{
Missile* mis;
int sx, sy, dx, dy;
do
{
// find new destination in the map
dx = x + rand() % 5 - 2;
dy = y + rand() % 5 - 2;
}
while( dx < 0 && dy < 0 && dx >= TheMap.Width && dy >= TheMap.Height );
sx = dx - 1 - rand() % 4;
sy = dy - 1 - rand() % 4;
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
mis = MakeMissile( MissileTypeByIdent( "missile-blizzard" ),
sx*TileSizeX+TileSizeX/2,
sy*TileSizeX+TileSizeX/2,
dx*TileSizeX+TileSizeX/2,
dy*TileSizeX+TileSizeX/2 );
mis->Damage = BLIZZARD_DAMAGE;
//FIXME: not correct -- blizzard should continue even if mage is
// destroyed (though it will be quite short time...)
mis->SourceUnit = unit;
mis->SourceType = unit->Type;
}
unit->Mana -= spell->ManaCost;
if ( unit->Mana > spell->ManaCost )
repeat = 1;
}
break;
// ---orc ogres---
case SpellActionEyeOfKilrogg:
MakeUnitAndPlace( x, y, UnitTypeByIdent("unit-eye-of-kilrogg"),
unit->Player );
unit->Mana -= spell->ManaCost;
PLAY_FIREWORKS(missile_spell);
DebugCheck( unit->Mana < 0 );
break;
case SpellActionBloodlust:
if (target && target->Type->Organic && target->Bloodlust < spell->TTL )
{
// get mana cost
unit->Mana -= spell->ManaCost;
target->Bloodlust = spell->TTL; // about 25 sec
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionRunes:
{
//FIXME: vladi: runes should be set in formation as in original
//FIXME: vladi: runes should be set on empty tile (ground or water)
Missile* mis;
unit->Mana -= spell->ManaCost;
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
mis = MakeMissile( MissileTypeByIdent( "missile-custom" ),
x*TileSizeX+TileSizeX/2,
y*TileSizeX+TileSizeX/2,
x*TileSizeX+TileSizeX/2,
y*TileSizeX+TileSizeX/2 );
mis->TTL = spell->TTL;
mis->Controller = SpellRunesController;
}
DebugCheck( unit->Mana < 0 );
break;
// ---orc death knights---
case SpellActionDeathCoil:
if( (target && target->Type->Organic) || (!target) )
{ //NOTE: fireball can be casted on spot
Missile* mis;
int sx = unit->X;
int sy = unit->Y;
int dx = x;
int dy = y;
if ( target )
{
dx = target->X;
dy = target->Y;
}
unit->Mana -= spell->ManaCost;
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
mis = MakeMissile( MissileTypeByIdent("missile-death-coil"),
sx*TileSizeX+TileSizeX/2,
sy*TileSizeX+TileSizeX/2,
dx*TileSizeX+TileSizeX/2,
dy*TileSizeX+TileSizeX/2 );
unit->Refs++;
mis->SourceUnit = unit;
if (target)
{
mis->TargetUnit = target;
target->Refs++;
}
mis->Controller = SpellDeathCoilController;
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionHaste:
if (target && !target->Type->Building && target->Haste < spell->TTL)
{
// get mana cost
unit->Mana -= spell->ManaCost;
target->Slow = 0;
target->Haste = spell->TTL; // about 25 sec
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionRaiseDead:
{
int i;
for( i=0; i<NumUnits; ++i ) {
// FIXME: this tries to draw all corps, ohje
if( (Units[i]->Type->Vanishes || Units[i]->Command.Action==UnitActionDie)
&& Units[i]->X == x && Units[i]->Y == y ) {
//FIXME: URGENT: remove corpse
//RemoveUnit( Units[i] );
//UnitLost( Units[i] );
//ReleaseUnit( Units[i] );
MakeUnitAndPlace( x, y, UnitTypeByIdent("unit-skeleton"),
unit->Player );
unit->Mana -= spell->ManaCost;
break;
}
}
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionWhirlwind:
{
Missile* mis;
unit->Mana -= spell->ManaCost;
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
mis = MakeMissile( MissileTypeByIdent("missile-whirlwind"),
x*TileSizeX+TileSizeX/2,
y*TileSizeX+TileSizeX/2,
x*TileSizeX+TileSizeX/2,
y*TileSizeX+TileSizeX/2 );
mis->TTL = spell->TTL;
mis->Controller = SpellWhirlwindController;
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionUnholyArmor:
if (target && !target->Type->Building && target->UnholyArmor < spell->TTL)
{
// get mana cost
unit->Mana -= spell->ManaCost;
target->UnholyArmor = spell->TTL; // about 13 sec
PLAY_FIREWORKS(missile_spell);
}
DebugCheck( unit->Mana < 0 );
break;
case SpellActionDeathAndDecay:
{
/*
See notes about Blizzard spell above...
*/
int shards = 10; // blizzard thing, yep :)
while( shards-- )
{
Missile* mis;
int dx, dy;
do
{
// find new destination in the map
dx = x + rand() % 5 - 2;
dy = y + rand() % 5 - 2;
}
while( dx < 0 && dy < 0 && dx >= TheMap.Width && dy >= TheMap.Height );
PlayGameSound(SoundIdForName(spell->SoundIdent),MaxSampleVolume); \
mis = MakeMissile( MissileTypeByIdent( "missile-death-and-decay" ),
dx*TileSizeX+TileSizeX/2,
dy*TileSizeX+TileSizeX/2,
dx*TileSizeX+TileSizeX/2,
dy*TileSizeX+TileSizeX/2 );
mis->Damage = DEATHANDDECAY_DAMAGE;
//FIXME: not correct -- blizzard should continue even if mage is
// destroyed (though it will be quite short time...)
mis->SourceUnit = unit;
mis->SourceType = unit->Type;
}
unit->Mana -= spell->ManaCost;
if ( unit->Mana > spell->ManaCost )
repeat = 1;
}
break;
default:
DebugLevel0Fn( "Unknown spell action" );
break;
}
#undef PLAY_FIREWORKS
DebugCheck( unit->Mana < 0 );
return repeat;
}
//@}

View file

@ -71,6 +71,7 @@ extern int getopt(int argc, char *const*argv, const char *opt);
#include "sound.h"
#include "network.h"
#include "pathfinder.h"
#include "spells.h"
/*----------------------------------------------------------------------------
-- Variables
@ -204,6 +205,7 @@ global void FreeCraftInit(void)
LoadTileset();
InitUnitButtons();
LoadMissileSprites();
InitSpells();
LoadUnitSprites();
LoadConstructions();
LoadDecorations();

View file

@ -50,6 +50,7 @@
#include "map.h"
#include "unit.h"
#include "font.h"
#include "spells.h"
/*----------------------------------------------------------------------------
-- Defines
@ -142,12 +143,12 @@ int AddButton( int pos, int level, const char* IconIdent,
ba->ValueStr = strdup( value );
switch( action )
{
case B_Magic: ba->Value = UpgradeIdByIdent( value ); break;
case B_Train: ba->Value = UnitTypeIdByIdent( value ); break;
case B_Research: ba->Value = UpgradeIdByIdent( value ); break;
case B_UpgradeTo: ba->Value = UnitTypeIdByIdent( value ); break;
case B_Build: ba->Value = UnitTypeIdByIdent( value ); break;
default: ba->Value = atoi( value ); break;
case B_SpellCast: ba->Value = SpellIdByIdent( value ); break;
case B_Train: ba->Value = UnitTypeIdByIdent( value ); break;
case B_Research: ba->Value = UpgradeIdByIdent( value ); break;
case B_UpgradeTo: ba->Value = UnitTypeIdByIdent( value ); break;
case B_Build: ba->Value = UnitTypeIdByIdent( value ); break;
default: ba->Value = atoi( value ); break;
}
} else {
ba->ValueStr = NULL;
@ -297,9 +298,11 @@ global void DrawButtonPanel(void)
case B_Research:
SetCosts(0,Upgrades[v].Costs);
break;
case B_Magic:
// FIXME: correct costs!!!
SetCosts(11,NULL);
case B_SpellCast:
{
const SpellType* spell = SpellTypeById( v );
SetCosts(spell->ManaCost,NULL);
}
break;
default:
@ -521,7 +524,7 @@ global void UpdateButtonPanel(void)
allow=UpgradeIdentAllowed( ThisPlayer,
UnitButtonTable[z]->ValueStr )=='A';
}
} else if ( UnitButtonTable[z]->Action == B_Magic ) {
} else if ( UnitButtonTable[z]->Action == B_SpellCast ) {
DebugLevel3("Magic: %d,%c\n",
CheckDependByIdent( ThisPlayer,
UnitButtonTable[z]->ValueStr ),
@ -600,9 +603,11 @@ global void DoButtonButtonClicked(int button)
case B_Repair:
case B_AttackGround:
case B_Demolish:
case B_SpellCast:
CursorState=CursorStateSelect;
GameCursor=&Cursors[CursorTypeYellowHair];
CursorAction=CurrentButtons[button].Action;
CursorValue=CurrentButtons[button].Value;
CurrentButtonLevel=9; // level 9 is cancel-only
UpdateButtonPanel();
MustRedraw|=RedrawCursor;

View file

@ -353,20 +353,20 @@ global ButtonAction AllButtons[] = {
// paladin specific actions --------------------------------------------
{ 7, 0, { "icon-holy-vision" },
B_Magic, 0, "upgrade-holy-vision",
NULL, NULL,
B_SpellCast, 0, "spell-holy-vision",
CheckTrue, NULL,
'v', "HOLY ~!VISION",
"unit-paladin"
},
{ 8, 0, { "icon-heal" },
B_Magic, 0, "upgrade-healing",
NULL, NULL,
B_SpellCast, 0, "spell-healing",
CheckUpgrade, "upgrade-healing",
'h', "~!HEALING (per 1 HP)",
"unit-paladin"
},
{ 9, 0, { "icon-exorcism" },
B_Magic, 0, "upgrade-exorcism",
NULL, NULL,
B_SpellCast, 0, "spell-exorcism",
CheckUpgrade, "upgrade-exorcism",
'e', "~!EXORCISM",
"unit-paladin"
},
@ -378,38 +378,38 @@ global ButtonAction AllButtons[] = {
"unit-mage,unit-khadgar"
},
{ 4, 0, { "icon-fireball" },
B_Magic, 0, "upgrade-fireball",
NULL, NULL,
B_SpellCast, 0, "spell-fireball",
CheckTrue, NULL,
'f', "~!FIREBALL",
"unit-mage,unit-khadgar"
},
{ 5, 0, { "icon-slow" },
B_Magic, 0, "upgrade-slow",
NULL, NULL,
B_SpellCast, 0, "spell-slow",
CheckUpgrade, "upgrade-slow",
'o', "SL~!OW",
"unit-mage"
},
{ 6, 0, { "icon-flame-shield" },
B_Magic, 0, "upgrade-flame-shield",
NULL, NULL,
B_SpellCast, 0, "spell-flame-shield",
CheckUpgrade, "upgrade-flame-shield",
'l', "F~!LAME SHIELD",
"unit-mage"
},
{ 7, 0, { "icon-invisibility" },
B_Magic, 0, "upgrade-invisibility",
NULL, NULL,
B_SpellCast, 0, "spell-invisibility",
CheckUpgrade, "upgrade-invisibility",
'i', "~!INVISIBILITY",
"unit-mage"
},
{ 8, 0, { "icon-polymorph" },
B_Magic, 0, "upgrade-polymorph",
NULL, NULL,
B_SpellCast, 0, "spell-polymorph",
CheckUpgrade, "upgrade-polymorph",
'p', "~!POLYMORPH",
"unit-mage"
},
{ 9, 0, { "icon-blizzard" },
B_Magic, 0, "upgrade-blizzard",
NULL, NULL,
B_SpellCast, 0, "spell-blizzard",
CheckUpgrade, "upgrade-blizzard",
'b', "~!BLIZZARD",
"unit-mage"
},
@ -1080,37 +1080,38 @@ global ButtonAction AllButtons[] = {
// ogre-mage specific actions --------------------------------------------
{ 7, 0, { "icon-eye-of-kilrogg" },
B_Magic, 0, "upgrade-eye-of-kilrogg",
NULL, NULL,
B_SpellCast, 0, "spell-eye-of-kilrogg",
CheckTrue, NULL,
'k', "EYE OF ~!KILROGG",
"unit-ogre-mage"
},
{ 8, 0, { "icon-bloodlust" },
B_Magic, 0, "upgrade-bloodlust",
NULL, NULL,
B_SpellCast, 0, "spell-bloodlust",
CheckUpgrade, "upgrade-bloodlust",
'b', "~!BLOODLUST",
"unit-ogre-mage"
},
{ 9, 0, { "icon-runes" },
B_Magic, 0, "upgrade-runes",
NULL, NULL,
B_SpellCast, 0, "spell-runes",
CheckTrue, "upgrade-runes",
'r', "~!RUNES",
"unit-ogre-mage"
},
// cho'gall specific actions --- same as ogre mage but it has them always --
{ 7, 0, { "icon-eye-of-kilrogg" },
B_Magic, 0, "upgrade-eye-of-kilrogg",
B_SpellCast, 0, "spell-eye-of-kilrogg",
CheckTrue, NULL,
'k', "EYE OF ~!KILROGG",
"unit-cho'gall"
},
{ 8, 0, { "icon-bloodlust" },
B_Magic, 0, "upgrade-bloodlust",
B_SpellCast, 0, "spell-bloodlust",
CheckTrue, NULL,
'b', "~!BLOODLUST",
"unit-cho'gall"
},
{ 9, 0, { "icon-runes" },
B_Magic, 0, "upgrade-runes",
B_SpellCast, 0, "spell-runes",
CheckTrue, NULL,
'r', "~!RUNES",
"unit-cho'gall"
@ -1123,38 +1124,38 @@ global ButtonAction AllButtons[] = {
"unit-death-knight" ",unit-gul'dan" ",unit-teron-gorefiend"
},
{ 4, 0, { "icon-death-coil" },
B_Magic, 0, "upgrade-death-coil",
NULL, NULL,
B_SpellCast, 0, "spell-death-coil",
CheckTrue, NULL,
'c', "DEATH ~!COIL",
"unit-death-knight" ",unit-gul'dan" ",unit-teron-gorefiend"
},
{ 5, 0, { "icon-haste" },
B_Magic, 0, "upgrade-haste",
NULL, NULL,
B_SpellCast, 0, "spell-haste",
CheckUpgrade, "upgrade-haste",
'h', "~!HASTE",
"unit-death-knight"
},
{ 6, 0, { "icon-raise-dead" },
B_Magic, 0, "upgrade-raise-dead",
NULL, NULL,
B_SpellCast, 0, "spell-raise-dead",
CheckUpgrade, "upgrade-raise-dead",
'r', "~!RAISE DEAD",
"unit-death-knight"
},
{ 7, 0, { "icon-whirlwind" },
B_Magic, 0, "upgrade-whirlwind",
NULL, NULL,
B_SpellCast, 0, "spell-whirlwind",
CheckUpgrade, "upgrade-whirlwind",
'w', "~!WHIRLWIND",
"unit-death-knight"
},
{ 8, 0, { "icon-unholy-armor" },
B_Magic, 0, "upgrade-unholy-armor",
NULL, NULL,
B_SpellCast, 0, "spell-unholy-armor",
CheckUpgrade, "upgrade-unholy-armor",
'u', "~!UNHOLY ARMOR",
"unit-death-knight"
},
{ 9, 0, { "icon-death-and-decay" },
B_Magic, 0, "upgrade-death-and-decay",
NULL, NULL,
B_SpellCast, 0, "spell-death-and-decay",
CheckUpgrade, "upgrade-death-and-decay",
'd', "~!DEATH AND DECAY",
"unit-death-knight"
},

View file

@ -1,6 +1,6 @@
// ___________ _________ _____ __
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
// \/ \/ \/ \/ \/
@ -513,7 +513,7 @@ global void ShiftMessages(void)
{
strcpy( Messages[z], "" );
}
MessagesCount--;
MessagesCount--;
}
/**
@ -534,7 +534,7 @@ global void ShiftMessagesEvent(void)
MessagesEventX[z] = -1;
MessagesEventY[z] = -1;
}
MessagesCount--;
MessagesCount--;
}
/**
@ -571,7 +571,7 @@ global void SetMessage( char* fmt, ... )
va_end( va );
if ( MessagesCount == MESSAGES_MAX )
ShiftMessages();
strcpy( Messages[ MessagesCount ], temp );
strcpy( Messages[ MessagesCount ], temp );
MessagesCount++;
MustRedraw|=RedrawMessage|RedrawMap;
MessageFrameTimeout = FrameCounter + MESSAGES_TIMEOUT;
@ -596,16 +596,16 @@ global void SetMessage2( int x, int y, char* fmt, ... )
va_end( va );
if ( MessagesCount == MESSAGES_MAX )
ShiftMessages();
strcpy( Messages[ MessagesCount ], temp );
strcpy( Messages[ MessagesCount ], temp );
MessagesCount++;
if ( MessagesEventCount == MESSAGES_MAX )
ShiftMessagesEvent();
strcpy( MessagesEvent[ MessagesEventCount ], temp );
MessagesEventX[ MessagesEventCount ] = x;
MessagesEventY[ MessagesEventCount ] = y;
MessagesEventCount++;
MustRedraw|=RedrawMessage|RedrawMap;
MessageFrameTimeout = FrameCounter + MESSAGES_TIMEOUT;
}
@ -664,8 +664,8 @@ global void CenterOnMessage(void)
MessagesEventIndex = 0;
if ( MessagesEventIndex >= MessagesEventCount )
return;
MapCenter( MessagesEventX[ MessagesEventIndex ],
MessagesEventY[ MessagesEventIndex ] );
MapCenter( MessagesEventX[ MessagesEventIndex ],
MessagesEventY[ MessagesEventIndex ] );
SetMessage( "*Event: %s", MessagesEvent[ MessagesEventIndex ] );
MessagesEventIndex++;
}

View file

@ -771,6 +771,43 @@ local void SendUnload(int x,int y)
}
}
/**
** Send the current selected group for spell cast.
**
** To empty field:
** To unit:
** Spell cast on unit or on map spot.
**
** @param x X map tile position.
** @param y Y map tile position.
**
** @see Selected, @see NumSelected
*/
local void SendSpellCast(int x,int y)
{
int i;
Unit* unit;
Unit* dest;
dest=UnitOnMapTile(x,y);
DebugLevel3Fn("SpellCast on: %p (%d,%d)\n", dest, x, y);
/* NOTE: Vladi:
This is a high-level function, it sends target spot and unit
(if exists). All checks are performed at spell cast handle
function which will cancel function if cannot be executed
*/
for( i=0; i<NumSelected; i++ ) {
unit=Selected[i];
if( !unit->Type->CanCastSpell )
continue; // this unit cannot cast spell
if( dest && unit == dest )
continue; // no unit can cast spell on himself
// CursorValue here holds the spell type id
SendCommandSpellCast(unit,x,y,dest,CursorValue,!(KeyModifiers&ModifierShift));
}
}
/**
** Send a command to selected units.
**
@ -808,6 +845,9 @@ local void SendCommand(int x,int y)
case B_Demolish:
SendDemolish(x,y);
break;
case B_SpellCast:
SendSpellCast(x,y);
break;
default:
DebugLevel1("Unsupported send action %d\n",CursorAction);
break;

View file

@ -271,6 +271,7 @@ global Unit* MakeUnit(UnitType* type,Player* player)
unit->Reset=1;
unit->Rs=MyRand()%100; // used for random fancy buildings and other things
unit->Revealer = 0; // FOW revealer
unit->Command.Action=UnitActionStill;
unit->PendCommand.Action=UnitActionStill;
@ -735,6 +736,9 @@ global int UnitVisible(const Unit* unit)
**
** NOTE: we could build a table of all magic units reducing cpu use.
*/
//FIXME: vladi: the doc says incrementing mana is done by 1 per second
// the spells effect can be decremented at the same time and this
// will reduse calls to this function to one time per second only!
global void UnitIncrementMana(void)
{
Unit** table;
@ -758,6 +762,20 @@ global void UnitIncrementMana(void)
MustRedraw|=RedrawInfoPanel;
}
}
//
// decrease spells effects time
//
if ( unit->Bloodlust > 0 )
unit->Bloodlust--;
if ( unit->Haste > 0 )
unit->Haste--;
if ( unit->Slow > 0 )
unit->Slow--;
if ( unit->Invisible > 0 )
unit->Invisible--;
if ( unit->UnholyArmor > 0 )
unit->UnholyArmor--;
}
}
@ -2018,7 +2036,9 @@ global void DestroyUnit(Unit* unit)
// FIXME: units in transporters should die without corpes...
if( unit->Type->Transporter ) { // Transporters loose their units
DestroyAllInside(unit);
//FIXME: vladi: it could be usefull if transport is near land
// to unload instead of destroying all units in it... ?
DestroyAllInside(unit);
}
RemoveUnit(unit);
@ -2578,12 +2598,13 @@ global void SaveUnit(const Unit* unit,FILE* file)
fprintf(file,"\t%d ",unit->Player->Player);
fprintf(file,"%d ",unit->Mana);
fprintf(file,"%d ",unit->HP);
fprintf(file,"(%d %d %d %d %d)\n"
fprintf(file,"(%d %d %d %d %d %d)\n"
,unit->Bloodlust
,unit->Haste
,unit->Slow
,unit->Invisible
,unit->Shield);
,unit->FlameShield
,unit->UnholyArmor);
fprintf(file,"\t%d ",unit->GroupId);
fprintf(file,"%d\n",unit->Value);

View file

@ -60,10 +60,6 @@ global int ShowHealthHorizontal=1;
global int ShowManaHorizontal=1;
/// Flag: show bars and dot energy only for selected
global int ShowEnergySelectedOnly;
/// Flag: show health bar always full sized
global int ShowFullSizedHealthBar=1;
/// Flag: show mana bar always full sized
global int ShowFullSizedManaBar=1;
// FIXME: not all variables of this file are here
// FIXME: perhaps split this file into two?
@ -277,6 +273,11 @@ global Decoration ShadowSprite = {
"graphic/unit shadow.png", 0,42, 32,32
};
/**
** Sprite to display the active spells on units.
*/
global Graphic* SpellSprites;
#if defined(USE_CCL) || defined(USE_CCL2)
/**
@ -334,6 +335,7 @@ global void LoadDecorations(void)
,ManaSprite.Width,ManaSprite.Height);
ShadowSprite.Sprite=LoadSprite(ShadowSprite.File
,ShadowSprite.Width,ShadowSprite.Height);
SpellSprites=LoadSprite("graphic/bloodlust,haste,slow,invis.,shield.png",16,16);
}
/**
@ -348,8 +350,6 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
{
int f;
int color;
int deco_x;
int deco_y;
UnitStats* stats;
@ -360,13 +360,11 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
return;
}
// FIXME: Should split this into small functions?
//
// Health bar of the unit.
// Health bar on left side of unit.
//
stats=unit->Stats;
if( ShowHealthBar ) {
if( !type->Critter && ShowHealthBar ) {
if( stats->HitPoints
&& !(ShowNoFull && unit->HP==stats->HitPoints) ) {
f=(100*unit->HP)/stats->HitPoints;
@ -377,31 +375,26 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
} else {
color=ColorRed;
}
if( ShowHealthHorizontal ) {
deco_x=x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1;
deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2
+type->BoxHeight;
if( !ShowFullSizedHealthBar ) {
if ( ShowHealthHorizontal == 0) {
VideoFillRectangleClip(color
,x+(type->TileWidth*TileSizeX
-type->BoxWidth)/2
,y+(type->TileHeight*TileSizeY
-type->BoxHeight)/2
,2,(f*type->BoxHeight)/100);
} else {
VideoFillRectangleClip(ColorBlack
,deco_x,deco_y,type->BoxWidth+2,4);
} else {
VideoDrawRectangleClip(ColorBlack
,deco_x,deco_y,(f*type->BoxWidth)/100+1,3);
}
VideoFillRectangleClip(color
,deco_x+1,deco_y+1,(f*type->BoxWidth)/100,2);
} else {
deco_x=x+(type->TileWidth*TileSizeX-type->BoxWidth)/2-3;
deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2;
if( ShowFullSizedHealthBar ) {
VideoFillRectangleClip(ColorBlack
,deco_x,deco_y,4,type->BoxHeight+1);
} else {
VideoDrawRectangleClip(ColorBlack
,deco_x,deco_y,3,(f*type->BoxHeight)/100);
}
VideoFillRectangleClip(color
,deco_x+1,deco_y+1,2,(f*type->BoxHeight)/100-1);
,x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1
,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2)
+type->BoxHeight+1
,((f*type->BoxHeight)/100)+2
,5);
VideoFillRectangleClip(color
,x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)
,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2)
+type->BoxHeight+2
,(f*type->BoxHeight)/100
,3);
}
}
}
@ -409,9 +402,11 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
//
// Health dot on left side of unit.
//
if( ShowHealthDot ) {
if( !type->Critter && ShowHealthDot ) {
if( stats->HitPoints
&& !(ShowNoFull && unit->HP==stats->HitPoints) ) {
int x1;
int y1;
int n;
f=(100*unit->HP)/stats->HitPoints;
@ -426,61 +421,55 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
}
DebugCheck( n<0 );
if( HealthSprite.HotX<0 ) {
deco_x=x+HealthSprite.HotX
x1=x+HealthSprite.HotX
+(type->TileWidth*TileSizeX
+type->BoxWidth+1)/2;
} else {
deco_x=x-HealthSprite.HotX
x1=x-HealthSprite.HotX
+(type->TileWidth*TileSizeX
-type->BoxWidth+1)/2;
}
if( HealthSprite.HotY<0 ) {
deco_y=y+HealthSprite.HotY
y1=y+HealthSprite.HotY
+(type->TileHeight*TileSizeY
+type->BoxHeight+1)/2;
} else {
deco_y=y-HealthSprite.HotY
y1=y-HealthSprite.HotY
+(type->TileHeight*TileSizeY
-type->BoxHeight+1)/2;
}
VideoDrawClip(HealthSprite.Sprite,n,deco_x,deco_y);
VideoDrawClip(HealthSprite.Sprite,n,x1,y1);
}
}
//
// Mana bar for the unit.
// Mana bar on right side of unit.
//
if( ShowManaBar ) {
if( type->CanCastSpell
&& !(ShowNoFull && unit->Mana==255) ) {
if( ShowManaHorizontal ) {
f=(100*unit->Mana)/255;
deco_x=x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1;
deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2
+type->BoxHeight+4;
if( ShowFullSizedManaBar ) {
VideoFillRectangleClip(ColorBlack
,deco_x,deco_y,type->BoxHeight+2,4);
} else {
VideoDrawRectangleClip(ColorBlack
,deco_x,deco_y,(f*type->BoxHeight)/100+1,3);
}
VideoFillRectangleClip(ColorBlue
,deco_x+1,deco_y+1,(f*type->BoxHeight)/100,2);
} else {
deco_x=x+(type->TileWidth*TileSizeX+type->BoxWidth)/2;
deco_y=y+(type->TileHeight*TileSizeY-type->BoxHeight)/2;
if ( ShowManaHorizontal == 0) {
f=(100*unit->Mana)/255;
if( ShowFullSizedManaBar ) {
VideoFillRectangleClip(ColorBlue
,x+(type->TileWidth*TileSizeX
+type->BoxWidth)/2
,y+(type->TileHeight*TileSizeY
-type->BoxHeight)/2
,2,(f*type->BoxHeight)/100);
} else {
f=(100*unit->Mana)/255;
VideoFillRectangleClip(ColorBlack
,deco_x,deco_y,4,type->BoxHeight+1);
} else {
VideoDrawRectangleClip(ColorBlack
,deco_x,deco_y,3,(f*type->BoxHeight)/100);
}
VideoFillRectangleClip(ColorBlue
,deco_x+1,deco_y+1,2,(f*type->BoxHeight)/100-1);
,x+((type->TileWidth*TileSizeX-type->BoxWidth)/2)-1
,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2)+type->BoxHeight+6
,(type->BoxHeight)+2
,5);
VideoFillRectangleClip(ColorBlue
,x+(type->TileWidth*TileSizeX-type->BoxWidth)/2
,(y+(type->TileHeight*TileSizeY-type->BoxHeight)/2)+type->BoxHeight+7
,(f*type->BoxHeight)/100
,3);
}
}
}
@ -490,6 +479,8 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
if( ShowManaDot ) {
if( type->CanCastSpell
&& !(ShowNoFull && unit->Mana==255) ) {
int x1;
int y1;
int n;
f=(100*unit->Mana)/255;
@ -507,27 +498,41 @@ local void DrawDecoration(Unit* unit,const UnitType* type,int x,int y)
n=4;
}
if( ManaSprite.HotX<0 ) {
deco_x=x+ManaSprite.HotX
x1=x+ManaSprite.HotX
+(type->TileWidth*TileSizeX
+type->BoxWidth+1)/2;
} else {
deco_x=x-ManaSprite.HotX
x1=x-ManaSprite.HotX
+(type->TileWidth*TileSizeX
-type->BoxWidth+1)/2;
}
if( ManaSprite.HotY<0 ) {
deco_y=y+ManaSprite.HotY
y1=y+ManaSprite.HotY
+(type->TileHeight*TileSizeY
+type->BoxHeight+1)/2;
} else {
deco_y=y-ManaSprite.HotY
y1=y-ManaSprite.HotY
+(type->TileHeight*TileSizeY
-type->BoxHeight+1)/2;
}
VideoDrawClip(ManaSprite.Sprite,n,deco_x,deco_y);
VideoDrawClip(ManaSprite.Sprite,n,x1,y1);
}
}
//
// Draw spells decoration
//
if ( unit->Bloodlust > 0 )
VideoDrawClip( SpellSprites, 0, x, y );
if ( unit->Haste > 0 ) // same slot as slow
VideoDrawClip( SpellSprites, 1, x+16, y );
if ( unit->Slow > 0 ) // same slot as haste
VideoDrawClip( SpellSprites, 2, x+16, y );
if ( unit->Invisible > 0 )
VideoDrawClip( SpellSprites, 3, x+16+16, y );
if ( unit->UnholyArmor > 0 )
VideoDrawClip( SpellSprites, 4, x+16+16+16, y );
// FIXME: group number could also be shown
}
@ -559,7 +564,6 @@ local void DrawShadow(Unit* unit,UnitType* type,int x,int y)
,x+ShadowSprite.HotX,y+ShadowSprite.HotY);
}
#if 0
/**
** Draw path from current postion to the destination of the move.
**
@ -681,134 +685,15 @@ global void DrawPath(Unit* unit)
,6,6);
}
}
#endif
/**
** Show the current order of an unit.
**
** @param unit Pointer to the unit.
**
*/
local void ShowOrder(const Unit* unit)
{
#ifdef NEW_ORDERS
int x1;
int y1;
int x2;
int y2;
int color;
const Unit* goal;
int i;
if( unit->Destroyed ) {
return;
}
x1=Map2ScreenX(unit->X)+unit->IX+unit->Type->TileWidth*TileSizeX/2;
y1=Map2ScreenY(unit->Y)+unit->IY+unit->Type->TileHeight*TileSizeY/2;
VideoFillCircleClip(ColorGreen,x1,y1,1);
for( i=0; i<=unit->OrderCount; ++i ) {
if( (goal=unit->Orders[i].Goal) ) {
x2=Map2ScreenX(goal->X)+goal->IX+goal->Type->TileWidth*TileSizeX/2;
y2=Map2ScreenY(goal->Y)+goal->IY+goal->Type->TileHeight*TileSizeY/2;
} else {
x2=Map2ScreenX(unit->Orders[i].X)+TileSizeX/2;
y2=Map2ScreenY(unit->Orders[i].Y)+TileSizeY/2;
}
switch( unit->Orders[i].Action ) {
case UnitActionNone:
color=ColorGray;
break;
case UnitActionStill:
color=ColorGray;
break;
case UnitActionStandGround:
color=ColorGray;
break;
case UnitActionFollow:
case UnitActionMove:
color=ColorGreen;
break;
case UnitActionPatrol:
VideoDrawLineClip(ColorGreen,x1,y1,x2,y2);
color=ColorBlue;
break;
case UnitActionRepair:
color=ColorGreen;
break;
case UnitActionAttack:
case UnitActionAttackGround:
color=ColorRed;
break;
case UnitActionBoard:
color=ColorGreen;
break;
case UnitActionUnload:
color=ColorGreen;
break;
case UnitActionDie:
color=ColorGray;
break;
case UnitActionTrain:
color=ColorGray;
break;
case UnitActionUpgradeTo:
color=ColorGray;
break;
case UnitActionResearch:
color=ColorGray;
break;
case UnitActionBuild:
color=ColorGreen;
break;
case UnitActionBuilded:
color=ColorGray;
break;
case UnitActionHarvest:
color=ColorGreen;
break;
case UnitActionMineGold:
color=ColorGreen;
break;
case UnitActionHaulOil:
color=ColorGreen;
break;
case UnitActionReturnGoods:
color=ColorGreen;
break;
case UnitActionDemolish:
color=ColorRed;
break;
default:
color=ColorGray;
DebugLevel1Fn("Unknown action %d\n",unit->Orders[i].Action);
break;
}
VideoDrawLineClip(color,x1,y1,x2,y2);
VideoFillCircleClip(color,x2,y2,2);
x1=x2;
y1=y2;
}
#else
int x1;
int y1;
int x2;
@ -925,7 +810,6 @@ local void ShowOrder(const Unit* unit)
VideoFillCircleClip(color,x2,y2,2);
//DrawPath(unit);
#endif
}
/*
@ -933,7 +817,6 @@ local void ShowOrder(const Unit* unit)
**
** 1) Must draw underground/underwater units. (FUTURE extension)
** 2) Must draw buildings and corpse.
** FIXME: Bridges?
** 3) Must draw land/sea units.
** 4) Must draw decoration units. (FUTURE extension)
** 5) Must draw low air units.
@ -998,34 +881,6 @@ local void DrawBuilding(Unit* unit)
DrawSelection(unit,type,x,y);
#endif
#ifdef NEW_ORDERS
//
// Buildings under construction/upgrade/ready.
//
if( unit->Orders[0].Action==UnitActionBuilded ) {
if( unit->Constructed || VideoGraphicFrames(type->Sprite)<=1 ) {
DrawConstruction(type->OverlapFrame
,frame&127
,x+(type->TileWidth*TileSizeX)/2
,y+(type->TileHeight*TileSizeY)/2);
} else {
DrawUnitType(type,frame,x,y);
}
} else if( unit->Orders[0].Action==UnitActionUpgradeTo ) {
// FIXME: update frame hard coded.
DrawUnitType(unit->Orders[0].Type,(frame&128)+1,x,y);
} else {
DrawUnitType(type,frame,x,y);
}
// FIXME: johns: ugly check here should be removed!
if( unit->Orders[0].Action!=UnitActionDie ) {
DrawDecoration(unit,type,x,y);
#if 0
DrawSelection(unit,type,x,y);
#endif
}
#else
//
// Buildings under construction/upgrade/ready.
//
@ -1044,12 +899,16 @@ local void DrawBuilding(Unit* unit)
DrawUnitType(type,frame,x,y);
}
#if 0
// FIXME: johns: ugly check here should be removed!
if( unit->Command.Action!=UnitActionDie ) {
DrawDecoration(unit,type,x,y);
#if 0
DrawSelection(unit,type,x,y);
#endif
}
#else
// FIXME: johns: ugly check here should be removed!
if( unit->Command.Action!=UnitActionDie ) {
DrawDecoration(unit,type,x,y);
}
#endif
}
@ -1067,6 +926,8 @@ local void DrawUnit(Unit* unit)
UnitType* type;
UnitStats* stats;
if ( unit->Revealer ) return; // Revealers are not drawn
type=unit->Type;
x=Map2ScreenX(unit->X)+unit->IX;
@ -1124,17 +985,10 @@ local void DrawUnit(Unit* unit)
ShowOrder(unit);
}
#ifdef NEW_ORDERS
// FIXME: johns: ugly check here should be removed!
if( unit->Orders[0].Action!=UnitActionDie ) {
DrawDecoration(unit,type,x,y);
}
#else
// FIXME: johns: ugly check here should be removed!
if( unit->Command.Action!=UnitActionDie ) {
DrawDecoration(unit,type,x,y);
}
#endif
}
/**
@ -1159,17 +1013,9 @@ global void DrawUnits(void)
for( i=0; i<NumUnits; ++i ) {
unit=Units[i];
// FIXME: this tries to draw all corps, ohje
#ifdef NEW_ORDERS
if( (unit->Type->Vanishes || unit->Orders[0].Action==UnitActionDie)
&& UnitVisible(unit) ) {
if( unit->Type->Vanishes || unit->Command.Action==UnitActionDie ) {
DrawUnit(unit);
}
#else
if( (unit->Type->Vanishes || unit->Command.Action==UnitActionDie)
&& UnitVisible(unit) ) {
DrawUnit(unit);
}
#endif
}
//
// 2b) buildings

View file

@ -67,7 +67,10 @@
*/
global int SelectUnits(int x1,int y1,int x2,int y2,Unit** table)
{
return UnitCacheSelect(x1,y1,x2,y2,table);
if ( x1 == x2 && y1 == y2 )
return UnitCacheOnTile(x1,y1,table);
else
return UnitCacheSelect(x1,y1,x2,y2,table);
}
/**

View file

@ -124,6 +124,7 @@ global CursorType Cursors[CursorMax] = {
global enum CursorState_e CursorState; /// cursor state
global int CursorAction; /// action for selection
global int CursorValue; /// value for CursorAction (spell type f.e.)
global UnitType* CursorBuilding; /// building cursor
global CursorType* GameCursor; /// cursor type