New enemy detection code and rescue of units implemented, bug in change unit owner fixed
This commit is contained in:
parent
5923fab8e9
commit
1912d10a56
8 changed files with 314 additions and 31 deletions
include
sound
stratagus
ui
unit
|
@ -50,6 +50,9 @@ typedef struct _game_sound_ {
|
|||
SoundConfig HumanWorkComplete; /// building ready
|
||||
SoundConfig PeasantWorkComplete; /// building ready
|
||||
SoundConfig OrcWorkComplete; /// building ready
|
||||
|
||||
SoundConfig HumanRescue; /// rescue units
|
||||
SoundConfig OrcRescue; /// rescue units
|
||||
} GameSound;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
|
|
@ -355,7 +355,13 @@ extern void UpdateForNewUnit(const Unit* unit,int upgrade);
|
|||
extern void NearestOfUnit(const Unit* unit,int tx,int ty,int *dx,int *dy);
|
||||
extern int UnitVisible(const Unit* unit);
|
||||
extern void RemoveUnit(Unit* unit);
|
||||
/// Increment mana of all magic units each second.
|
||||
extern void UnitIncrementMana(void);
|
||||
/// Increment health of all regenerating units each second.
|
||||
extern void UnitIncrementHealth(void);
|
||||
/// Check for rescue each second.
|
||||
extern void RescueUnits(void);
|
||||
/// Change owner of unit.
|
||||
extern void ChangeUnitOwner(Unit* unit,Player* old,Player* new);
|
||||
|
||||
extern void UnitNewHeading(Unit* unit);
|
||||
|
@ -397,6 +403,8 @@ extern int ViewPointDistanceToUnit(Unit* dest);
|
|||
|
||||
/// Return true, if unit is an enemy of the player
|
||||
extern int IsEnemy(const Player* player,const Unit* dest);
|
||||
/// Return true, if unit is allied with the player
|
||||
extern int IsAllied(const Player* player,const Unit* dest);
|
||||
extern int CanTarget(const UnitType* type,const UnitType* dest);
|
||||
|
||||
extern void SaveUnit(const Unit* unit,FILE* file); /// save unit-structure
|
||||
|
|
|
@ -270,6 +270,10 @@ local SCM CclDefineGameSounds(SCM list) {
|
|||
} else if ( gh_eq_p(name,gh_symbol2scm("placement-success")) ) {
|
||||
GameSounds.PlacementSuccess.Sound=CCL_SOUND_ID(data);
|
||||
DebugLevel3("SoundPlacementSuccess %d\n",SoundPlacementSuccess);
|
||||
} else if ( gh_eq_p(name,gh_symbol2scm("human-rescue")) ) {
|
||||
GameSounds.HumanRescue.Sound=CCL_SOUND_ID(data);
|
||||
} else if ( gh_eq_p(name,gh_symbol2scm("orc-rescue")) ) {
|
||||
GameSounds.OrcRescue.Sound=CCL_SOUND_ID(data);
|
||||
} else {
|
||||
fprintf(stderr,"Incorrect symbol\n");
|
||||
return list;
|
||||
|
|
|
@ -63,7 +63,9 @@ global GameSound GameSounds={
|
|||
{ "building construction"},
|
||||
{ "basic human voices work complete" },
|
||||
{ "peasant work complete" },
|
||||
{ "basic orc voices work complete" }
|
||||
{ "basic orc voices work complete" },
|
||||
{ "rescue (human)" },
|
||||
{ "rescue (orc)" },
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
@ -277,6 +279,14 @@ global void InitSoundClient(void)
|
|||
GameSounds.OrcWorkComplete.Sound=
|
||||
SoundIdForName(GameSounds.OrcWorkComplete.Name);
|
||||
}
|
||||
if( !GameSounds.HumanRescue.Sound ) {
|
||||
GameSounds.HumanRescue.Sound=
|
||||
SoundIdForName(GameSounds.HumanRescue.Name);
|
||||
}
|
||||
if( !GameSounds.OrcRescue.Sound ) {
|
||||
GameSounds.OrcRescue.Sound=
|
||||
SoundIdForName(GameSounds.OrcRescue.Name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // } WITH_SOUND
|
||||
|
|
|
@ -9,14 +9,17 @@
|
|||
// FreeCraft - A free fantasy real time strategy game engine
|
||||
//
|
||||
/**@name mainloop.c - The main game loop. */
|
||||
/*
|
||||
** (c) Copyright 1998-2000 by Lutz Sammer
|
||||
**
|
||||
** $Id$
|
||||
*/
|
||||
//
|
||||
// (c) Copyright 1998-2000 by Lutz Sammer
|
||||
//
|
||||
// $Id$
|
||||
|
||||
//@{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freecraft.h"
|
||||
|
@ -44,18 +47,30 @@
|
|||
#include <SDL/SDL.h>
|
||||
#endif
|
||||
|
||||
/* variable set when we are scrolling via keyboard */
|
||||
//----------------------------------------------------------------------------
|
||||
// Variables
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// variable set when we are scrolling via keyboard
|
||||
global enum _scroll_state_ KeyScrollState=ScrollNone;
|
||||
|
||||
/* variable set when we are scrolling via mouse */
|
||||
/// variable set when we are scrolling via mouse
|
||||
global enum _scroll_state_ MouseScrollState=ScrollNone;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
** Handle scrolling area.
|
||||
**
|
||||
** @param TempScrollState Scroll direction/state.
|
||||
** @param FastScroll Flag scroll faster.
|
||||
**
|
||||
** FIXME: Support dynamic acceleration of scroll speed.
|
||||
*/
|
||||
local void DoScrollArea(enum _scroll_state_ TempScrollState, int FastScroll)
|
||||
{
|
||||
|
||||
switch( TempScrollState ) {
|
||||
case ScrollUp:
|
||||
if( MapY ) {
|
||||
|
@ -257,7 +272,9 @@ global void UpdateDisplay(void)
|
|||
DrawMissiles();
|
||||
SetClipping(0,0,VideoWidth,VideoHeight);
|
||||
}
|
||||
|
||||
// FIXME: trick17! must find a better solution
|
||||
// Resources over map!
|
||||
if( TheUI.MapX<TheUI.ResourceX && TheUI.MapWidth>TheUI.ResourceX ) {
|
||||
MustRedraw|=RedrawResources;
|
||||
}
|
||||
|
@ -278,7 +295,7 @@ global void UpdateDisplay(void)
|
|||
,TheUI.MenuButton.Graphic->Width
|
||||
,TheUI.MenuButton.Graphic->Height
|
||||
,TheUI.MenuButtonX,TheUI.MenuButtonY);
|
||||
// FIXME: Button position is configured
|
||||
|
||||
DrawMenuButton(MBUTTON_MAIN, (ButtonUnderCursor == 0
|
||||
? MenuButtonActive : 0)|
|
||||
(GameMenuButtonClicked ? MenuButtonClicked : 0),
|
||||
|
@ -437,20 +454,22 @@ global void GameMainLoop(void)
|
|||
|
||||
MustRedraw&=~RedrawMinimap; // FIXME: this a little hack!
|
||||
|
||||
/*
|
||||
** Called each second. Split into different frames.
|
||||
** Increment mana of magic units.
|
||||
** Update mini-map.
|
||||
** Update map fog of war.
|
||||
** Call AI.
|
||||
** Check game goals.
|
||||
*/
|
||||
//
|
||||
// Work todo each second.
|
||||
// Split into different frames, to reduce cpu time.
|
||||
// Increment mana of magic units.
|
||||
// Update mini-map.
|
||||
// Update map fog of war.
|
||||
// Call AI.
|
||||
// Check game goals.
|
||||
// Check rescue of units.
|
||||
//
|
||||
switch( FrameCounter%FRAMES_PER_SECOND ) {
|
||||
case 0:
|
||||
UnitIncrementMana(); // magic units
|
||||
break;
|
||||
case 1:
|
||||
//UnitIncrementHealth();// berserker healing
|
||||
UnitIncrementHealth(); // berserker healing
|
||||
break;
|
||||
case 2:
|
||||
MapUpdateVisible();
|
||||
|
@ -467,8 +486,14 @@ global void GameMainLoop(void)
|
|||
case 6:
|
||||
RegenerateForest();
|
||||
break;
|
||||
case 7:
|
||||
RescueUnits();
|
||||
break;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Map scrolling
|
||||
//
|
||||
if( TheUI.MouseScroll && !(FrameCounter%SpeedMouseScroll) ) {
|
||||
DoScrollArea(MouseScrollState, 0);
|
||||
}
|
||||
|
@ -502,16 +527,9 @@ global void GameMainLoop(void)
|
|||
|
||||
WaitEventsAndKeepSync();
|
||||
|
||||
#ifndef NEW_NETWORK
|
||||
NetworkSync(); // FIXME: wrong position
|
||||
#if 0
|
||||
//
|
||||
// Sync: not needed done by DoEvent
|
||||
//
|
||||
while( VideoSyncSpeed && VideoInterrupts<1 ) {
|
||||
sigpause(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
VideoInterrupts=0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,58 @@ global void CreatePlayer(char* name,int type)
|
|||
player->Enemy=0;
|
||||
player->Allied=0;
|
||||
player->AiNum=PlayerAiUniversal;
|
||||
|
||||
//
|
||||
// Calculate enemy/allied mask.
|
||||
//
|
||||
for( i=0; i<NumPlayers; ++i ) {
|
||||
switch( type ) {
|
||||
case PlayerNeutral:
|
||||
case PlayerNobody:
|
||||
default:
|
||||
break;
|
||||
case PlayerComputer:
|
||||
// Computer allied with computer and enemy of all humans.
|
||||
if( Players[i].Type==PlayerComputer ) {
|
||||
player->Allied|=(1<<i);
|
||||
Players[i].Allied|=(1<<NumPlayers);
|
||||
} else if( Players[i].Type==PlayerHuman
|
||||
|| Players[i].Type==PlayerRescueActive ) {
|
||||
player->Enemy|=(1<<i);
|
||||
Players[i].Enemy|=(1<<NumPlayers);
|
||||
}
|
||||
break;
|
||||
case PlayerHuman:
|
||||
// Humans are enemy of all?
|
||||
if( Players[i].Type==PlayerComputer
|
||||
|| Players[i].Type==PlayerHuman ) {
|
||||
player->Enemy|=(1<<i);
|
||||
Players[i].Enemy|=(1<<NumPlayers);
|
||||
} else if( Players[i].Type==PlayerRescueActive
|
||||
|| Players[i].Type==PlayerRescuePassive ) {
|
||||
player->Allied|=(1<<i);
|
||||
Players[i].Allied|=(1<<NumPlayers);
|
||||
}
|
||||
break;
|
||||
case PlayerRescuePassive:
|
||||
// Rescue passive are allied with humans
|
||||
if( Players[i].Type==PlayerHuman ) {
|
||||
player->Allied|=(1<<i);
|
||||
Players[i].Allied|=(1<<NumPlayers);
|
||||
}
|
||||
break;
|
||||
case PlayerRescueActive:
|
||||
// Rescue active are allied with humans and enemies of computer
|
||||
if( Players[i].Type==PlayerComputer ) {
|
||||
player->Enemy|=(1<<i);
|
||||
Players[i].Enemy|=(1<<NumPlayers);
|
||||
} else if( Players[i].Type==PlayerHuman ) {
|
||||
player->Allied|=(1<<i);
|
||||
Players[i].Allied|=(1<<NumPlayers);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Initial default resources.
|
||||
|
|
|
@ -288,12 +288,11 @@ global void DoRightButton(int x,int y)
|
|||
//
|
||||
if( action==MouseActionAttack ) {
|
||||
// FIXME: more units on same tile
|
||||
// FIXME: should use enemy first, than other functions!
|
||||
dest=TargetOnMapTile(unit,x,y);
|
||||
if( dest ) {
|
||||
dest->Blink=3;
|
||||
if( dest->Player==ThisPlayer ||
|
||||
dest->Player->Player==PlayerNumNeutral) {
|
||||
// FIXME: lokh: maybe we should add the ally players here
|
||||
if( dest->Player==ThisPlayer || IsAllied(ThisPlayer,dest) ) {
|
||||
SendCommandFollow(unit,dest,flush);
|
||||
continue;
|
||||
} else {
|
||||
|
|
191
unit/unit.cpp
191
unit/unit.cpp
|
@ -328,7 +328,9 @@ global Unit* MakeUnit(UnitType* type,Player* player)
|
|||
if( !type->Building ) {
|
||||
unit->Heading=(MyRand()>>13)&7; // random heading
|
||||
player->NumFoodUnits++; // food needed
|
||||
MustRedraw|=RedrawResources; // update food
|
||||
if( player==ThisPlayer ) {
|
||||
MustRedraw|=RedrawResources;// update food
|
||||
}
|
||||
} else {
|
||||
player->NumBuildings++;
|
||||
}
|
||||
|
@ -529,6 +531,7 @@ global void UnitLost(const Unit* unit)
|
|||
if( unit->Type->Building ) {
|
||||
// FIXME: This should be complete rewritten
|
||||
// FIXME: Slow and new members are available
|
||||
// FIXME: most redraws only needed for player==ThisPlayer
|
||||
|
||||
// Still under construction
|
||||
if( unit->Command.Action!=UnitActionBuilded ) {
|
||||
|
@ -799,8 +802,57 @@ global void UnitIncrementMana(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
** Increment health of all regenerating units. Called each second.
|
||||
*/
|
||||
global void UnitIncrementHealth(void)
|
||||
{
|
||||
#ifdef NEW_UNIT
|
||||
Unit** table;
|
||||
Unit* unit;
|
||||
static UnitType* berserker;
|
||||
static int regeneration;
|
||||
|
||||
if( !berserker ) { // FIXME: can move to init code!
|
||||
berserker=UnitTypeByIdent("unit-berserker");
|
||||
regeneration=UpgradeIdByIdent("upgrade-berserker-regeneration");
|
||||
}
|
||||
|
||||
for( table=Units; table<Units+NumUnits; table++ ) {
|
||||
unit=*table;
|
||||
if( unit->Type==berserker
|
||||
&& unit->HP<unit->Stats->HitPoints
|
||||
&& UpgradeIdAllowed(unit->Player,regeneration)=='R' ) {
|
||||
++unit->HP; // FIXME: how fast do we regenerate
|
||||
}
|
||||
}
|
||||
#else
|
||||
Unit* unit;
|
||||
int i;
|
||||
static UnitType* berserker;
|
||||
static int regeneration;
|
||||
|
||||
if( !berserker ) {
|
||||
berserker=UnitTypeByIdent("unit-berserker");
|
||||
regeneration=UpgradeIdByIdent("upgrade-berserker-regeneration");
|
||||
}
|
||||
|
||||
for( i=0; i< NumUnits; i++) {
|
||||
unit=Units[i];
|
||||
if( unit->Type==berserker
|
||||
&& unit->HP<unit->Stats->HP
|
||||
&& UpgradeIdAllowed(unit->Player,regeneration)=='R' ) {
|
||||
++unit->HP; // FIXME: how fast do we regenerate
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
** Change the unit's owner
|
||||
**
|
||||
** @param oldplayer Old owning player.
|
||||
** @param newplayer New owning player.
|
||||
*/
|
||||
global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
|
||||
{
|
||||
|
@ -821,6 +873,11 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Must change food/gold and other.
|
||||
//
|
||||
UnitLost(unit);
|
||||
|
||||
#ifdef NEW_UNIT
|
||||
// Remove from old player table
|
||||
|
||||
|
@ -845,6 +902,110 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
|
|||
#endif
|
||||
|
||||
unit->Player=newplayer;
|
||||
|
||||
//
|
||||
// Must change food/gold and other.
|
||||
//
|
||||
if( unit->Type->GivesOil ) {
|
||||
DebugLevel0(__FUNCTION__":FIXME: oil platform transfer unsupported\n");
|
||||
}
|
||||
if( !unit->Type->Building ) {
|
||||
newplayer->NumFoodUnits++; // food needed
|
||||
if( newplayer==ThisPlayer ) {
|
||||
MustRedraw|=RedrawResources;// update food
|
||||
}
|
||||
} else {
|
||||
newplayer->NumBuildings++;
|
||||
}
|
||||
newplayer->UnitTypesCount[unit->Type->Type]++;
|
||||
UpdateForNewUnit(unit,0);
|
||||
}
|
||||
|
||||
/**
|
||||
** Change the owner of all units of a player.
|
||||
**
|
||||
** @param oldplayer Old owning player.
|
||||
** @param newplayer New owning player.
|
||||
*/
|
||||
local void ChangePlayerOwner(Player* oldplayer,Player* newplayer)
|
||||
{
|
||||
Unit* table[MAX_UNITS];
|
||||
Unit* unit;
|
||||
int i;
|
||||
int n;
|
||||
|
||||
// NOTE: table is changed.
|
||||
n=oldplayer->TotalNumUnits;
|
||||
memcpy(table,oldplayer->Units,n*sizeof(Unit*));
|
||||
for( i=0; i<n; i++ ) {
|
||||
unit=table[i];
|
||||
ChangeUnitOwner(unit,oldplayer,newplayer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Rescue units.
|
||||
*/
|
||||
global void RescueUnits(void)
|
||||
{
|
||||
Player* p;
|
||||
Unit* unit;
|
||||
Unit* table[MAX_UNITS];
|
||||
Unit* near[MAX_UNITS];
|
||||
int n;
|
||||
int i;
|
||||
int j;
|
||||
int l;
|
||||
static int norescue;
|
||||
|
||||
if( norescue ) {
|
||||
return;
|
||||
}
|
||||
norescue=1;
|
||||
for( p=Players; p<Players+NumPlayers; ++p ) {
|
||||
if( p->Type!=PlayerRescuePassive && p->Type!=PlayerRescueActive ) {
|
||||
continue;
|
||||
}
|
||||
if( p->TotalNumUnits ) {
|
||||
norescue=0;
|
||||
// NOTE: table is changed.
|
||||
l=p->TotalNumUnits;
|
||||
memcpy(table,p->Units,l*sizeof(Unit*));
|
||||
for( j=0; j<l; j++ ) {
|
||||
unit=table[j];
|
||||
DebugLevel3("Checking %Zd\n",UnitNumber(unit));
|
||||
// NOTE: I hope SelectUnits checks bounds?
|
||||
n=SelectUnits(
|
||||
unit->X-1,unit->Y-1,
|
||||
unit->X+unit->Type->TileWidth+1,
|
||||
unit->Y+unit->Type->TileHeight+1,near);
|
||||
//
|
||||
// Look if human near the unit.
|
||||
//
|
||||
for( i=0; i<n; ++i ) {
|
||||
if( near[i]->Player->Type==PlayerHuman ) {
|
||||
ChangeUnitOwner(unit,unit->Player,near[i]->Player);
|
||||
// FIXME: more races?
|
||||
if( unit->Player->Race==PlayerRaceHuman ) {
|
||||
PlayGameSound(GameSounds.HumanRescue.Sound
|
||||
,MaxSampleVolume);
|
||||
} else {
|
||||
PlayGameSound(GameSounds.OrcRescue.Sound
|
||||
,MaxSampleVolume);
|
||||
}
|
||||
//
|
||||
// City center converts complete race
|
||||
// NOTE: I use a trick here, centers could
|
||||
// store gold.
|
||||
if( unit->Type->StoresGold ) {
|
||||
ChangePlayerOwner(p,unit->Player);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
@ -2366,6 +2527,7 @@ global int ViewPointDistanceToUnit(Unit* dest) {
|
|||
return MapDistanceToUnit(x_v,y_v,dest);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
** Check if unit is an enemy.
|
||||
**
|
||||
|
@ -2387,6 +2549,33 @@ global int IsEnemy(const Player* player,const Unit* dest)
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
** Check if unit is an enemy.
|
||||
**
|
||||
** @param player The source player.
|
||||
** @param dest The destination unit.
|
||||
**
|
||||
** @return Returns true, if the destination unit is an enemy.
|
||||
*/
|
||||
global int IsEnemy(const Player* player,const Unit* dest)
|
||||
{
|
||||
return player->Enemy&(1<<dest->Player->Player);
|
||||
}
|
||||
|
||||
/**
|
||||
** Check if unit is allied.
|
||||
**
|
||||
** @param player The source player.
|
||||
** @param dest The destination unit.
|
||||
**
|
||||
** @return Returns true, if the destination unit is allied.
|
||||
*/
|
||||
global int IsAllied(const Player* player,const Unit* dest)
|
||||
{
|
||||
return player->Allied&(1<<dest->Player->Player);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
** Can the source unit attack the destionation unit.
|
||||
|
|
Loading…
Add table
Reference in a new issue